[Unity] Collision을 통한 맵에 생성된 오브젝트 확인
요약
게임에서 오브젝트의 위치를 확인하기 위해서 Create empty를 만든 후 컴포넌트 BoxCollider, Rigidbody, 자체 스크립트를 상속한 프리팹을 ObjectDetectionBox 만들어 충돌을 통해 오브젝트 위치를 확인할 것이다.
하지만 Awake()에 ObjectDetectionBox를 충돌시켜서 다시 회수하였지만, 자체 스크립트에 있는 OnCollisionEnter에는 충돌 값이 Null이었다.
해당 문제는 유니티 라이프 사이클과 연관이 있었는데 유니티에서는 함수 Awake -> OnCollisionXXX -> Update로 움직이기 때문에 Awake()에서는 충돌 값이 Null이었다.
Object Detection Box 프리팹
프리팹을 만들기 위해서는 먼저 Hierarchy에서 우클릭이나 +버튼을 통해 Create Empty를 해주고 컴포넌트 BoxCollider, Rigidbody를 추가해 준 후 자신이 만들어 놓은 파일에 드래그 앤드 드롭으로 넣어주면 프리팹이 완성된다.
Rigidbody를 추가하는 이유는 2개의 오브젝트가 충돌할 때 어느 한쪽에는 Rigidbody를 가지고 있어야 충돌 관련 함수가 작동한다.
프리팹을 만들 때 Resources 파일 안에 만들어두면 스크립트 상에서 오브젝트의 파일 위치를 작성할 때 사용할 수 있다.
이를 이용하지 않으면 Inspector에서 드래그 앤드 드롭으로 넣어줘야 한다.
최종적으론 Scale XYZ 값을 0.8로 만들어 오브젝트가 0.001이라도 튀어나왔는데 오브젝트 옆의 Object Detection Box가 감지하는 것을 막도록 하였다. 또한 Rigidbody의 Constraints에 Position, Rotation을, Freeze를 걸어 충돌 때문에 Object Detection Box가 돌아가거나 움직이는 것을 막았다.
Object Detection Box.cs
using UnityEngine;
public class ObjectDetectionBox : MonoBehaviour
{
Collision box;
public Collision Box { get { return box;} set{ box = value;} }
public void OnCollisionEnter(Collision collision)
{
box = collision;
}
}
OnCollisionEnter을 통해 충돌되면 바로 collision 값을 box에 저장하였다.
Map.cs 핵심
만들어진 Object Detection Box를 활용하기 위해서 Map.cs 코드를 만들었다.
using System.Collections.Generic;
using UnityEngine;
public class Map : MonoBehaviour
{
protected GameSupporter gameSupporter;
protected GameObject odbParent;
protected GameObject odbPrefab;
protected MiniMap miniMap;
// odb: object Detection Box
protected List<GameObject> odbList = new List<GameObject>();
// 맵에 사용할 블록 생성
private void Awake()
{
// 필요한 컴포넌트, 프리펩 생성
gameSupporter = FindObjectOfType<GameSupporter>();
miniMap = FindObjectOfType<MiniMap>();
odbParent = GameObject.Find("Object Detection Box");
odbPrefab = Resources.Load("Prefab/Object Detection Box", typeof(GameObject)) as GameObject;
// object Detection Box 맵 크기만큼 생성
for (int i = 0; i < gameSupporter.MapSizeX * gameSupporter.MapSizeZ; i++)
{
odbList.Add(Instantiate(odbPrefab, new Vector3(0, -100, 0), Quaternion.Euler(Vector3.zero), odbParent.transform));
odbList[i].SetActive(false);
}
gameSupporter.Map2D = new int[gameSupporter.MapSizeX, gameSupporter.MapSizeZ];
// 게임에 필요한 Object생성
SetCheckBox();
// 오브젝트 확인
CheckBox();
}
}
해당 코드를 보면 생성 후 SetCheckBox()로 Object Detection Box를 위치시킨 후 CheckBox()를 통해 오브젝트를 충돌시켜 위치를 확인한다. 이때 Object Detection Box에 OnCollisionEnter에 값이 Null로 나오게 된다.
Object Detection Box 오류 해결
이 문제를 해결하기 위해 조사해 본 결과 유니티는 실행되는 함수에 순서가 존재한다. 이 순서는 유니티 라이프 사이클이라 불린다.
이벤트 함수의 실행 순서 - Unity 매뉴얼
Unity 스크립트를 실행하면 사전에 지정한 순서대로 여러 개의 이벤트 함수가 실행됩니다. 이 페이지에서는 이러한 이벤트 함수를 소개하고 실행 시퀀스에 어떻게 포함되는지 설명합니다.
docs.unity3d.com
위의 링크에 있는 Unity Documentation을 보면 함수 실행 순서가 나타나 있다. 순서는 Awake -> OnCollisionXXX -> Update로 이루어져 있다. 그래서 코드를 다음과 같이 수정해야 한다.
using System.Collections.Generic;
using UnityEngine;
public class Map : MonoBehaviour
{
// 생략
private bool MapCheck = true;
// odb: object Detection Box
protected List<GameObject> odbList = new List<GameObject>();
// 맵에 사용할 블록 생성
private void Awake()
{
// 생략
// 게임에 필요한 Object생성
SetCheckBox();
}
private void Update()
{
//맵을 탐색 한번만 작동
if (MapCheck)
{
map.CheckBox();
MapCheck = false;
}
}
}
이로써 맵에 있는 오브젝트를 확인하여 플레이어나 몬스터를 움직이게 할 수 있다.