Prosto

따라하는 유니티 2D 프로젝트ⓐ -2 본문

Programing/Unity 3D

따라하는 유니티 2D 프로젝트ⓐ -2

Prosto 2016.09.14 03:20

 

따라하는 유니티 2D 프로젝트 강좌 두 번째 시간입니다.

이번에 다룰 내용은 저번에 만들었던 캐릭터 이동을 배경이동으로 전환시키고,

오브젝트 풀을 이용하여 정해진 수를 반복 사용하는 방법을 적용하여 해볼 것입니다.

(중요 : 프리팹 만들기/사용, Instantiate 오브젝트 생성, 배열과 구조체 사용 )

 

 

 

 

그럼 시작하겠습니다.

 

가장 먼저 저번에 작업했던 프로젝트를 실행합니다. 그리고 순서대로 직접 해보며 따라오시면 됩니다.

 

Scripts 하위에 Map이라는 폴더를 생성한 후 MoveMap 스크립트를 추가합니다.

 

 

MoveMap.cs에 소스를 작성하여 줍니다.

여기에서 Instantiate는 오브젝트 생성에 사용하는 함수입니다.

Instantiate(생성할 오브젝트, 생성될 좌표, 각도) 정보를 입력합니다.

 

그리고 추가적으로 줄을 그어둔 부분이 오브젝트 풀(Object Pool)을 위한 부분입니다.

오브젝트 풀이라는 것은 게임에서 사용할 오브젝트들을 미리 생성하여두고

실제 게임에서는 활성/비활성화 시켜가며 사용하는 방법입니다.

(이러한 방법을 쓰는 이유는 오브젝트를 생성할 때와 파괴(제거)할 때 비용이 크기 때문에 사용됩니다.)

오브젝트 풀이라고 특별할 것은 없고 미리 배열에 필요한 만큼 생성하고,

 필요할 때 tiles[i].SetActive(true); 필요 없을 때 tiles[i].SetActive(false); 하여 사용하면 됩니다.

 

 

Hierarchy 패널에 마우스 우측 클릭하여 새로운 Empty Object를 추가해줍니다.

해당 오브젝트의 이름을 GameManager로 변경합니다.

오브젝트의 이름 변경 방법은 두 가지 중 편한 방법을 사용하시면 됩니다.

1. 오브젝트를 더블클릭한 후 변경한다.

2. 오브젝트를 선택한 상태에서 F2를 눌러 변경한다.

 

 

GameManager를 선택한 상태에서

아까 작성해두었던 Scripts - Map에 있는 MoveMap을 드래그 앤 드랍해줍니다.

(저번에 1번 강좌에서 스크립트를 추가했던 방법과 같은 방법입니다.)

 

 

이번에는 프리팹을 만들어볼 것입니다.

일단 폴더를 새로 만들어 Prefabs라는 이름을 붙여줍니다.

 

원래 있었던 tile 오브젝트를 클릭하여 Prefabs에 드래그앤드랍해줍니다.

(폴더 이름은 상관없고 그냥 프리팹화 시키고 싶은 오브젝트를

드래그앤드랍으로 Project 탭으로 보내는 게 포인트입니다.)

 

 

이번에는 아까 작성했던 스크립트에서 타일을 받아 사용하기 위한 작업입니다.

먼저 Hierarchy 탭에서 GameManager를 선택한 후

Prefabs에 있는 tile을 MoveMap스크립트의 Tile 자리에 드래그앤드랍해줍니다.

 

 

이렇게 바뀌었다면 정상적으로 완료된 것입니다.

 

 

이제 과거의 타일들은 필요가 없어졌기에 지워줍니다.

오브젝트를 제거하는 방법은 선택 - 마우스 우측 클릭 - Delete

혹은 선택한 후 키보드의 Delete키를 이용하면 됩니다.

tile, tile (1), tile (2)를 선택하여 제거해줍니다.(쉬프트, 컨트롤 여러 개 선택 가능[윈도우와 같음])

 

 

제거가 완료된 모습입니다.

 

 

이제 한번 실행을 해봅시다.

오류 없이 정상적으로 실행이 되고,

Hierarchy 탭에 tile(Clone)이 세 개 추가된 것을 확인할 수 있습니다.

이때 보면 글자가 회색인데요. 이렇게 보이는 게 비활성화 된 오브젝트들입니다.

(아까 스크립트의 SetActive(false)때문입니다. )

 

 

게임 실행 중인 상태에서 해당 tile(Clone)들을 눌러 오브젝트를 활성화하면(밑의 네모칸을 누르면 됨)

우리가 아까 만들었던 타일과 같은 타일이 존재하는 것을 확인할 수 있습니다.

 

 

껐다가 다시 실행시켰습니다.

(지금까지 저와 똑같이 진행했다면 이부분은 안 해도 됩니다.)

이제 각각 위치값들을 확인하기위한 작업을 할 차례입니다.

볼이 알맞게 위에 올라가있어야 하기 때문에 블록을 이동시켜 Y값을 확인합니다.

 

해보니 적당한 Y값은 -5.1입니다.

 

 

이번엔 블록 간격을 확인하기 위하여 테스트해보았습니다.

 

블록1과 블록2의 간격은 81.9네요. (타일 하나 크기가 81.9라는 말)

 

 

이번에는 블록이 충분히 보이지 않는 곳으로 갔을 때 제거(혹은 바로 재사용)해주기 위하여

충분히 보이지 않는 위치를 찾습니다.

 

X : -60이네요.

 

 

그럼 이제 마지막으로 스크립트를 좀 더 좋게 바꿔주고 실행하면 됩니다.

따라서 작성하면 되고, 추가적인 부분은 번호를 달아뒀습니다.

1.struct.. tile에 사용될 구조체를 만들어줍니다.

gameobject, transform, bool, Vector3를 가지고있습니다.

(gameObject, bool active는 활성화 비활성화를 위하여 만들었었는데,

화면 밖으로 나가면 마지막 블록 바로 뒤로 붙이게 만들어서 필요가 없어졌네요..)

 

tranform은 position을 계속 바꿔줘야하기 때문에 넣었고,

pos는 위치 정보를 저장해두고 사용하기 위하여 넣었습니다.

gameobject.transform.position 같은 식으로 너무 잦게 참조하면 성능상 안 좋습니다.

 

위치변경에 자주 사용하기 때문에 별도로 transform을 받고,

position도 각각 가지고 있게 한 후 그 값만 변경하여 tf.position에 셋팅하였습니다.

 

2.구조체 배열을 만들었습니다. 오브젝트 풀이 확장되었죠.

아까 스크립트에서는 단순히 GameObject들만 배열로 되었지만

지금은 구조체 내부에 GameObject가 위치하고 있고

그 구조체를 배열화시켰습니다.(obj와 관련 정보 갖는 배열)

 

 

FixedUpdate 부분입니다. 현재 이동 관련 부분이죠.

고정 프레임마다 for문을 이용하여 타일들의 위치를 변경시키기 위한 소스죠.

1.아까 측정해둔 tileEndPoint를 기준점으로 놓고 그것보다 크면 위치 조정을 해줍니다.

2.반대로 tileEndPoint보다 작아졌다면(넘어갔다면) 가장 마지막 블록으로 위치시키는 작업을 해줍니다.

 

주석으로 설명이 있으니 나머지는 생략하겠습니다.

 

 

CreateTiles도 바뀐 것을 확인할 수 있습니다.

단순 GameObject[]에서 구조체[]로 바뀌었으니 설정해줄 것들이 늘어났을 뿐이죠.

GameObject를 만드는 방법은 똑같습니다.

 

마지막에 lastTileNum=2;라는 부분이 추가되었네요.

이 부분으로 현재 마지막 타일이 어떤 것인지 판단합니다.

(마지막 타일 뒤에 재활용 타일을 붙여줘야하기 때문..)

 

 

중간에 변경되기도 했고, 소스 코드도 길기 때문에 복사할 수 있도록 남기겠습니다.

소스 보기

 

 

이제 소스 코드 작성은 끝났고, 실행하기 전에

원래 사용하고 있었던 GameObject에 존재하는 PlayerMove(Script)에 체크를 해제해줍니다.

(이것도 비활성화로 실제 게임에서 작동하려면 별도로 켜둬야합니다. 추후 PlayerMove는 완전 제거)

 

 

그리고 1.GameObject는 현재 고정적인 위치에 있으니 GameObject 하위에 존재하던 main Camera도 밖으로 빼줍니다.

2.실행버튼을 눌러 게임을 실행해봅니다.

3.실제 게임화면에서 타일이 재활용되며 진행되는 모습을 봅니다...

 

이렇게 배경(현재는 타일만)의 움직임으로

캐릭터가 앞으로 나아가는 것과 같은 효과를 낼 수 있습니다.

특히나 런닝(or 2D 레이싱)게임에 적합하겠죠?

 

 

 

지금까지 따라하는 유니티 2D 프로젝트ⓐ -2번 강좌를 보셨습니다.

이번에 많은 것들을 해봤습니다.

프리팹 생성부터 실제 게임 중에 오브젝트 생성, 또한 오브젝트 풀, 구조체, 배열 사용 등등.

장애물까지 넣으려고 했지만 새로운 것들도 많았고, 그래서 생각보다 작업하는데 많은 시간이 걸렸습니다.

그래도 이제 움직임이 있으니 게임 같아지기 시작했네요. 강좌를 지날 수록 더욱 게임 같아질 것입니다.

 

 

다음 유니티 2D 프로젝트ⓐ의 세 번째 강의에서는..

장애물을 추가하고,

볼에 간단한 점프기능을 넣어볼 것입니다.(일단 화면 클릭시 점프하도록 예정)

 

 

그 프로젝트를 진행하며 새로운 것들에 대해서는 지금처럼 또 설명하며 진행하도록 하겠습니다.

 

 

궁금한 점 있으시면 댓글이나 따로 메일로 질문하시면 시간되는 대로 답변드리겠습니다. ( 연락 )

 

 

저작자 표시 비영리 변경 금지
신고
13 Comments
  • 프로필사진 2016.12.02 23:58 신고 블록 위치정하는 부분을 모르겠네요ㅠㅠ 계속 블록 1과 블록 2의 사이가 벌어져 있고 3은안쓰면 블록 1과 2 사이에 간격은 상관없나요
  • 프로필사진 Peterkimzz 2016.12.03 16:09 신고 다시 해보고 있는데 궁금한게 있어요! tiles[i].obj.transform.position.x -= speed; 이렇게 하면 오류가 뜨는데요, 이해가 잘 안가요. tiles[i].tf.position에 tiles[i].pos 을 대입해줘야만 땅이 움직이게 되던데, 제 생각은 tiles[i].pos 자체는 tiles[i].obj.tf.position 랑 완전히 같은 코드라고 생각하는데 왜 pos에 값을 넣는 것이 직접 오브젝트를 움직이지 못하는거죠..?? 강의에 적어주신 코드는 tf.position 에 내 위치를 확정시킨다 ! 라는 식인거 같은데 제가 봤을 땐 둘다 똑같은 코드인데 pos는 안움직이고 tf.position 은 움직이니까 이해가 잘 안갑니다 ㅠ

    코드 보면서 좀 더 생각해봤는데 제 생각입니다.. obj.transform.position 에는 온전한 Vector3 xyz 값을 가지고 있는 것을 대입할 수 있는 것 같아서 오류가 나는 것 같은데 그렇다면 또 의문은 tiles[i].pos.x -= speed; 는 결국은 vector3 형식이라서 오류가 나야할 것 같은데 나질 않네요.. ㅠㅠ
  • 프로필사진 Prosto 2016.12.03 19:04 신고 안녕하세요. 위에 작성하신 댓글과 같은 분이신가요?
    일단 어떤 걸 보고 말씀하시는지 알겠습니다.

    tiles[i].pos와 tiles[i].obj.transform.position이 어떤 게 다른지와 obj.transform.position.x -= speed;가 왜 안 되는지에 대한 부분이 맞나요?

    - 일단 tiles[i].pos와 tiles[i].obj.transform.position는 다릅니다!

    구조체 TilesStruct{~} 안에 보시면
    Vector3로 pos 변수가 있고, GameObject로 obj, Transform으로 tf가 있죠? (그 외에도 있고요.)
    일단 먼저 알아야 할 것은 Vector3와 GameObject의 차이점입니다.
    잘 알고있겠지만, Vector3 타입에는 3차원 위치 정보(x,y,z) 값을 저장할 수 있고
    GameObject에는 생성된(혹은 생성한) 게임오브젝트를 저장할 수 있습니다.

    그러면 여기서 다시 돌아와, 조금 확장해보겠습니다.
    Vector3 tempPos = tiles[i].obj.transform.pos;
    이라고 했다면
    obj.transform.position와 tempPos는 같은 걸까요?
    그에 대한 대답은 그렇다고 볼 수도 있고, 아닐 수도 있습니다. 보는 시점에 따라 달라지죠.
    *단순히 대입한 후 바로 현재 값이 같은지에 대한 물음이라면 같습니다!
    *하지만 tempPos = obj.transform.position;이라고 했다고해서 tempPos 값을 바꿔준다고 해서 obj.transform.position 값이 바뀌지는 않죠. 그 이유는 값(value)를 주는 거지, 주소(reference)를 주는 게 아니기 때문입니다.
    결국 tempPos에는 일시적으로 현재 obj.transform.position 값을 저장했을 뿐이죠.

    이와 같은 맥락입니다.
    tiles[i].pos는 단순히 Vector3의 값을 가질 수 있는 변수이고(그냥 임시로 담아둘 곳),
    tiles[i].obj.transform.position은 현재 obj의 하위 transform의 하위 positon의 값을 나타내는 것입니다.

    (글로 보자면 obj(해당 GameObject)의 Transform(게임 씬에서 특정 오브젝트 선택하면 우측 Hierarchy 탭에 Transform이 있죠? 그 부분..)의 Position 값 입니다.)

    (위는 생각해보니 obj.transform.position보다 tf.position으로 접근해야죠? 그러려고 Transform을 만들어뒀으니..)


    - 그렇다면 여기서 왜 굳이 타일 구조체에 obj(GameObject), tf(Transform)도 두고, pos도 뒀느냐하면 성능상 비용때문입니다.
    당연하게도 어딘가로 접근을 많이 할 수록 비용이 발생되겠죠?
    (gameObject에서 transform으로 접근 = 비용, transform에서 position으로 접근 = 비용)
    임시로 현재 위치 값을 pos에 저장해두면 현재 position 값을 안 받아와도 됩니다.

    예를 들어 이런 차이죠.

    Vector3 tempVec;
    void Awake(){
    }
    void Update(){
    tempVec = transform.position;
    tempVec.x += 0.5f;
    transform.position = tempVec;
    }

    Vector3 tempVec;
    void Awake(){
    tempVec = transform.position;
    }
    void Update(){
    tempVec.x += 0.5f;
    transform.position = tempVec;
    }
    은 무슨 차이가 있나요?
    tempVec = transform.position의 호출 수 차이만 있고 나머지는 실행상 아무차이가 없습니다.
    그렇다면 가능하면 같은 처리를 할 때 비용을 줄여주는 게 좋겠죠?

    음... 그래서 글이 길어졌지만, 결과적으로 임시로 값을 저장해주는 용도로 있었던 게 pos입니다. 실제 transform.position과 같지 않습니다!


    - 그리고 다음으로, obj.transform.position.x -= speed;가 왜 안 되는지에 대한 부분입니다.
    (일단 tf.position으로 사용해야 Transform 값을 저장하고 받아둔 의미가 있습니다!)
    그리고 transform.position.x -= speed;가 안 되는 이유는
    Transform에 대한 값(position, rotation, scale)을 수정할 때는 통째(Vector3 - x,y,z)로 수정해줘야 한다는 규칙 때문입니다. (교체는 되지만, 일부 수정은 안 됨 : 하고싶다면 현재 값을 Vector3에 받은 후 값을 수정하여 다시 넣어줘야 함.)

    자세한 이유까지는 알아보지 않았지만 아마 예외 상황이 발생할 것을 염두해 그렇게 만들지 않았을지.. 추측!합니다... 이 부분은 궁금하시다면 구글링해보시는 것도 좋겠네요!
  • 프로필사진 2016.12.04 20:21 신고 아녀 블록 1과 블록 2의 좌표라 하나? 위치를 정확하게 알려주셨으면 좋겠습니다 답글로 좀 남겨주세요 ㅎㅎ
  • 프로필사진 Prosto 2016.12.05 22:13 신고 블록1과 2의 좌표라고 하시면...
    음... 질문이 애매하네요ㅠㅠ

    일단 블록1과 2의 위치 차이는 x축만 차이나고, 이 값이 tileGap에 저장된 81.9입니다.
    이 간격을 계속 유지하며 왼쪽으로 이동하게 되어있죠!(모든 블록을 동일한 크기만큼 왼쪽으로 이동)
    블록 1의 x값이 0일땐
    블록 2는 x값이 81.9겠죠?

    (그리고 참고로 이 간격은 이미지 크기 * 게임 오브젝트 크기입니다.
    이미지 크기(x:1024) * 오브젝트 크기(x:8)이니 8192고 여기서 0.02는 여유 값으로 버린 후 81.9가 되었습니다..)

    혹 다른 질문이셨다면
    자세히 말씀해주시면 좋겠네요!
    (설명에 사진이 필요하다면 메일로 보내주시고요!)
  • 프로필사진 오류 2016.12.20 18:29 신고 유니티를 껏다가 들어가면 Hierarchy란에 있는게 초기화됩니다..
    나머지는 그대로있는데
  • 프로필사진 Prosto 2016.12.20 21:36 신고 Ctrl+S 단축키를 이용해 씬을 저장해야 합니다!
    (작업 중간 중간 저장해주세요!)

    + Scenes 폴더에 저장해주세요!
  • 프로필사진 sad 2017.01.09 22:57 신고 Clone이 생성되고 x값을 -60 y값을 5.2로 설정해놔도 다시 게임시작하면 x 168 ,y -5.1로 바뀌는데 어떡하죠
  • 프로필사진 Prosto 2017.01.10 02:39 신고 CreateTiles() 함수 부분에서
    tempVec 부분의 지정이 제대로 되지 않아 그런게 아닐지 생각이 듭니다.
    (정확하게는 소스와 실행 결과를 봐야 말씀드리겠지만요.)

    그런 때에는 의심이 되는 부분에
    (지금은 CreateTiles()의 for문 안의 tempVec과 tileGap)
    Debug.Log("확인 : " + tempVec);
    식으로 콘솔을 통해 값이 제대로 들어가는지 찍어보시면 좋을 것 같습니다.
  • 프로필사진 Hello 2017.04.01 18:39 신고 전 강좌에서 만든 코드인 공을 계속 0.5씩 이동시키는건 삭제해야 하는건가요?
  • 프로필사진 Prosto 2017.04.02 23:24 신고 네 말씀하신 게 맞습니다.

    이 강좌 마지막에 체크를 해제하여 보여주지만,
    완전 삭제하셔도 됩니다.
    (이동 방식 모두 보시라는 의미에서..)

    그리고 이렇게 사용하는 이유가
    멀리 쭉 나아갈 건데
    Vector값은 float들로 이루어져있어
    멀리가면 멀리갈 수록 오차가 많이 생기는 걸 방지하기 위한 이유입니다!
  • 프로필사진 hiiiii 2017.08.20 16:54 신고 왜 MoveMap(script)아래에 Tile이라는 항목이 없죠...?

    using System.Collections;
    using UnityEngine;

    public class MoveMap : MonoBehaviour {
    public GameObject tile;//바닥에 사용할 GameObject(프리팹)

    void Awake()
    {
    CreateTiles();
    }

    private GameObject[] tiles;
    private int tileNum = 3;
    void CreateTiles()
    {
    tiles = new GameObject[tileNum];
    for(int i = 0; i < tileNum; i++)
    {
    tiles[i] = Instantiate(tile, Vector3.zero, Quaternion.identity) as GameObject;//생성
    tiles[i].SetActive(false);//비활성화
    }
    }

    코드 다 잘 작성한 것 같은데... 도와주세요
  • 프로필사진 Prosto 2017.08.20 18:48 신고 tile이라는 항목이 없다는 말씀은 실행해보면 NullException이
    tiles[i] = Instantiate(tile, Vector3.zero, Quaternion.identity) as GameObject;//생성

    이쪽에 나온다는 말씀이신가요?

    이거라면 아마
    public GameObject tile;//바닥에 사용할 GameObject(프리팹)
    선언하신 후에 유니티에서 등록을 안해주신 게 아닐지요..


    이 글에서 Ctrl+F로 아래 내용 검색해서 나오는 부분입니다.
    ----
    이번에는 아까 작성했던 스크립트에서 타일을 받아 사용하기 위한 작업입니다.
    ----
    MoveMap Script에 Tile 등록해주는 부분..

    혹 다른 문제라면 콘솔창에 에러 내용도 같이 부탁 드릴게요!
댓글쓰기 폼