본문 바로가기
도전! N잡러!/도전! 게임 개발!

TAP FLOAT 개발 일기 #7

by NAWE 2023. 9. 20.
반응형

안녕하세요. 나위입니다.

STUDIO NAWE의 첫 타이틀. TAP FLOAT의 개발 일기를 찾아주셔서 감사합니다.

이전 포스팅에서 이어지는 내용이오니, 못 보신 분들은 아래 링크로 앞 내용을 먼저 보고 오시는 게 좋겠어요!

 

TAP FLOAT 개발 일기 #0 바로가기 ◀

TAP FLOAT 개발 일기 #1 바로가기 ◀

TAP FLOAT 개발 일기 #2 바로가기 ◀

TAP FLOAT 개발 일기 #3 바로가기 ◀

TAP FLOAT 개발 일기 #4 바로가기 ◀

TAP FLOAT 개발 일기 #5 바로가기 ◀

TAP FLOAT 개발 일기 #6 바로가기 ◀

 

그럼, 시작합니다!


우주 배경이 무한히 흐르도록 만드는 작업까지 했었습니다.

물론, 속도라던가 대략의 위치같은 것들은 미세한 조정이 필요하겠지만, 그야 천천히 조절해 나가면 되는 것이죠!

moveSpeed, delPosition, respawnPosition

저 변수들을 간단하게 조절되도록 만들었으니까요!

스크립트에서 public으로 선언한 변수는 엔진에서 값을 조절할 수 있어요!

 

이제는 2번째 기능을 구현해야죠.

바로 '화면 상단에서 방해 요소 오브젝트를 무작위 생성시킨 뒤에 아래로 떨어지게 만들기.'

 입니다.

 

말은 상당히 간단하지만... 이 작업을 위해서는 선행되어야 할 작업들이 많아요.

우선, 방해요소 오브젝트를 프리팹(Prefab)으로 만드는 것입니다.

 

프리팹이 뭐냐고요?

유니티의 프리팹이란?

프리팹(Prefab)은 미리 만들어 놓은 게임 오브젝트, 템플릿이다.

프리팹은 Asset에 저장되어 있으며, 프리팹 인스턴스(Prefab Instance)를 생성하여, 월드 상에서 동작한다.

프리팹으로부터 원하는 만큼의 프리팹 인스턴스를 생성할 수 있다.

→ 출처 : notyu님의 티스토리

 

다른 분의 티스토리 블로그에 잘 설명된 내용이 있어서 인용해보았습니다.

문제는, 설명이야 잘 되어 있다손 치더라도 그걸 온전히 이해하느냐라는 것이겠죠.

 

저의 경우에는 나름의 방식대로 이해하고 있습니다.

프리팹의 정의가 뭐고, 어떻게 작동하는 것이고, 어떤 논리이고 구조인지 그런 건 잘 모르겠다만...

이걸 '어떨 때, 왜 써야 하는지'는 알고 있거든요. (으쓱)

아이구 그러셨어요? 으쓱으쓱 하셨어요?

 

프리팹은 언제 쓰는가?!

바로, 똑같은 오브젝트가 많이 등장할 때 쓰는 것입니다.

TAP FLOAT에서는 플레이어가 피해야 할 오브젝트로 H빔, 상자, 운석이 존재하지만... 각각 한 번만 나온 뒤로 더 이상 등장하지 않는 것이 아니죠.

 

요컨데 앞서 나왔던 애가 똑같이 계속 등장하는 것인데, 그런 똑같은 애들이 등장할 때마다 0부터 100까지 '새롭게' 오브젝트를 그리려고 하면 프로그램에 부하가 많이 걸릴 거에요.

똑같은 녀석이라면 적당히 Ctrl + C / V를 할 수 있어야 한다는 거죠.

 

프리팹을 만들어두는 것이 바로 Ctrl + C. 즉, 복사를 하는 행동이랍니다.

그렇게 복사된(=프리팹이 된) 오브젝트를 C# 스크립트에서 자유롭게 Ctrl + V를 하면서 붙여쓰는 것이죠.

복붙 최고!

 

음....

저는 유니티의 프리팹을 대략 저런 식의 개념으로 이해하고 있습니다.

 

물론, 우연히라도 이 글을 보는 진짜 프로그래머님들은 '저게 대체 뭔소리야?' 라고 생각하시겠지만, 지금 저의 레벨에서는 이런 식으로 이해라도 하는 게 고작이랍니다.

 

모로 가든 서울만 가면 되는 거죠! 핫하!

어쨌거나 통하기만 하면 된다 이거야!

 

프리팹을 만드는 방법은 상당히 간단합니다.

유니티의 Hierarchy창에서 프리팹화 할 오브젝트를 드래그 & 드롭하여 Project 창으로 끌고 오면 끝나요.

선택하여 드래그 & 드롭!

저는 방해 요소의 이름을 'Enemy'라고 붙였었습니다. 적이라서요.

여하튼 그 녀석을 끌어다가 Project 창에 놓게 되면 자동으로 오브젝트의 이름을 따라 Enemy라는 프리팹이 만들어 질 것이고, Hierarchy창의 Enemy 오브젝트의 앞에도 파란 상자 아이콘이 붙으며 프리팹으로 변경된 것이 확인될 거에요!

파란 상자가 앞에 붙었어요. 프리팹으로 만들기 성공!

 

그럼, 이제는 기본적인 Enemy의 기능을 만들어야죠.

 

...사실은 순서가 좀 틀렸습니다.

Enemy를 먼저 다 만들어 놓은 뒤에 → 프리팹으로 만드는 것이 제대로 된 순서이긴 해요.

상식적으로 뭔갈 복사하려면 복사할 내용을 다 완성시켜 놓고 복사를 해야 맞는 거니까요.

 

하지만! 유니티는 순서 좀 틀렸다고 막 안되고 그런 거 없어요. 똑똑한 친구거든요.

그렇게 유니티의 능력을 믿으면서 Enemy의 기능을 아래처럼 정리해 보았습니다.

 

  1. 자동으로 아래로 이동한다.
  2. 화면을 벗어나면 삭제시킨다.
  3. 회전한다.
  4. PC랑 충돌할 수 있어야 한다.

 

총 4가지 기능인데, 마지막 항목인 '4. PC랑 충돌할 수 있어야 한다.' 같은 경우에는 충돌 영역(Collider)만 있으면 되는 것이라서 스크립트에서는 1 ~ 3번 항목만 구현하면 될 것 같아요.

 

그래서, 아래와 같이 C# 스크립트를 작성하였습니다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Enemy : MonoBehaviour
{
    [SerializeField]
    private float moveSpeed = 10f;
    private float delPosition = -8f;
    public Vector3 rotationSpeed = new Vector3(0, 0, 50);

    void Update()
    {
        transform.position += Vector3.down * moveSpeed * Time.deltaTime;
        transform.Rotate(rotationSpeed * Time.deltaTime);
        if (transform.position.y < delPosition)
        {
            Destroy(gameObject);
        }
    }
}

하나하나씩 해석 들어갑니다.

우선, [SerializeField]라는 것은 바로 밑에 있는 변수가 private. 즉, 외부에서 수정할 수 없도록 선언된 값임에도 외부에서 수정되게끔 만들어주는 선언문입니다.

 

지난 포스팅에도 잠깐 언급되었듯이 엔진 내에서 간편하게 변수값을 수정하도록 하려면 public이라는 접근 제한자로 변수를 선언해야 한다고 하였는데, public의 반대가 바로 private에요.

private이라는 접근 제한자로 선언된 변수는 유니티 엔진 안에서 값을 수정할 수 없게 되거든요.

하지만, 앞에 [SerializeField]를 붙이게 되면 private라 할 지라도 유니티 엔진 안에서 값을 수정할 수 있게 됩니다.

 

반대로, public이지만 [Hideininspector]를 붙이게 되면 유니티 엔진 안에서 값을 수정할 수 없게 되는 기능도 존재한답니다.

 

...그냥 그런게 있다 정도만 알아두면 될 것 같아요.

그래서, 변수로 지정해 둔 값은 총 3가지입니다.

    [SerializeField]
    private float moveSpeed = 10f;
    private float delPosition = -8f;
    public Vector3 rotationSpeed = new Vector3(0, 0, 50);

private이지만 값을 수정할 수 있는 변수. moveSpeed 변수는 방해요소 오브젝트의 이동 속도를 제어하는 것이고.

값을 수정할 수 없는 private 접근 제한자를 가진 delPosition 변수는 오브젝트가 사라지는 Y값 위치를 제어하는 것이며,

값을 수정할 수 있는 public 접근 제한자를 가진 rotationSpeed는 오브젝트가 회전하는 속도를 제어하는 것입니다.

 

각각 지정한 값들이 어떻게 작동되는지는 Update안에서 벌어집니다.

    void Update()
    {
        transform.position += Vector3.down * moveSpeed * Time.deltaTime;
        transform.Rotate(rotationSpeed * Time.deltaTime);
        if (transform.position.y < delPosition)
        {
            Destroy(gameObject);
        }
    }

역시나 저 내용을 대충 해석하자면, 이런 의미입니다.

 

이 오브젝트의 위치 값을 moveSpeed만큼 아래로 계속 더해줘. 사양이 좋던 나쁘던 모든 기계에서 똑같은 시간으로 계산되게끔.

이 오브젝트의 회전 값을 rotationSpeed만큼 계속 더해줘. 사양이 좋던 나쁘던 모든 기계에서 똑같은 시간으로 계산되게끔.

만약 이 오브젝트의 y축 위치 값이 delPosition. 즉, 오브젝트가 제거되어야 할 위치까지 오게 되면 이 오브젝트를 파괴시켜줘. 고마워.

 

오브젝트의 y값을 계속 더하면서 아래로 움직이게 만들고, 동시에 z축 값도 변경시키며 회전하게 만듭니다.

그 결과, 오브젝트의 y값이 delPosition에 도달하게 되면 이 오브젝트를 파괴시켜버리는 것이 이 스크립트의 내용이 되겠습니다.

 

스크립트를 붙인 뒤 게임을 실행하면 아래와 같은 모습을 볼 수 있습니다.

회전 + 아래로 이동 + 화면 벗어난 이후 삭제.

원하던 대로 완료되었습니다.

이제 이 오브젝트에게 Box Collider 2D 컴포넌트를 붙여넣는 것으로 충돌 영역을 지정해 주고, Is Trigger에 체크를 하는 걸로 마지막 기능까지 구현했습니다.

 

Is Trigger란, 충돌 영역이 일종의 트리거(=방아쇠)로써 작동하는가 여부를 판단하는 것입니다.

플레이어 캐릭터와 충돌하면 플레이어 캐릭터를 죽여야 하므로 트리거 역할을 하는 것이기에 저는 체크를 하였답니다.

Box Collider 2D의 Inspector 내용입니다.

 

여기까지, Enemy의 기본적인 기능 구현이 완료되었습니다.

이제 프리팹으로 만들어진 저 Enemy 녀석을 랜덤한 위치에서 마구마구 뿌려줘야 할 차례가 되었네요!

 

 

<다음 편에 계속>

반응형

'도전! N잡러! > 도전! 게임 개발!' 카테고리의 다른 글

TAP FLOAT 개발 일기 #9  (82) 2023.09.23
TAP FLOAT 개발 일기 #8  (68) 2023.09.22
TAP FLOAT 개발 일기 #6  (81) 2023.09.19
TAP FLOAT 개발 일기 #5  (60) 2023.09.17
TAP FLOAT 개발 일기 #4  (48) 2023.09.16

댓글