멈추지 않고 끈질기게
[Unity][포트폴리오] 이동 발판(Moving Platform) 본문
※ 해당 포스팅은 개인의 공부 정리용 글입니다. 틀린 내용이 있다면 추후 수정될 수 있습니다.
※ 해당 포스팅은 Unity 2021.3.15f1 버전을 기준으로 작성되었습니다.
※ 개발 중인 유니티 3D 포트폴리오의 소스 코드를 포함하고 있습니다.
1. 이동 발판(Moving Platform)
using UnityEngine;
public class MovingPlatform : MonoBehaviour
{
Rigidbody rigid;
[SerializeField] float moveSpeed;
Vector3 moveVec;
Vector3 dirVec;
우선 움직임을 Rigidbody로 구현할 예정이기에 Rigidbody 변수를 선언하였고, 속도 조절용 변수 moveSpeed는 인스펙터 창에서 조절할 수 있도록 SerializeField로 선언하였습니다. 방향 벡터용 변수 dirVec와 이동 속도 등을 적용한 실제 이동벡터용 변수 moveVec을 선언하였습니다.
public enum MoveDirection
{
Vertical,
Horizontal,
UpperLeft_LowerRight,
UpperRight_LowerLeft
}
[SerializeField] MoveDirection myDir;
void Awake()
{
rigid = GetComponent<Rigidbody>();
}
void Start()
{
// 방향 벡터 초기화
switch(myDir)
{
case MoveDirection.Vertical:
dirVec = Vector3.forward;
break;
case MoveDirection.Horizontal:
dirVec = Vector3.right;
break;
case MoveDirection.UpperLeft_LowerRight:
dirVec = new Vector3(1, 0, -1).normalized;
break;
case MoveDirection.UpperRight_LowerLeft:
dirVec = new Vector3(1, 0, 1).normalized;
break;
}
}
void FixedUpdate()
{
moveVec = dirVec * moveSpeed * Time.fixedDeltaTime;
rigid.MovePosition(transform.position + moveVec);
}
이동 방향 4가지를 열거형으로 선언한 뒤, 해당 열거형 변수를 SerializeField로 선언하여 인스펙터 창에서 이동 방향을 선택할 수 있도록 작성했습니다. Awake()에서 Rigidbody 변수 초기화, Start()에서 선택한 방향에 따라 방향벡터(dirVec)를 초기화하도록 했습니다. 발판의 움직임은 FixedUpdate()에서 moveVec 계산(방향 벡터 * 이동 속도 * fixedDeltaTime) 후 Rigidbody.MovePosition()을 통해 구현했습니다.
다만, 여기까지 구현하고 보니 해결해야 될 내용 두가지가 있었습니다.
- 왕복 로직(어디까지 이동한 후 방향 전환할지)
- 캐릭터 이동(이동 발판 위의 캐릭터가 함께 따라가도록)
2. 왕복 로직
왕복 로직은 이동 발판의 하위 오브젝트로 보이지 않는 범퍼(Bumper)를 붙여 해결했습니다.
using UnityEngine;
// 이동 발판의 이동 범위 조절
// 범퍼가 다른 플랫폼에 닿으면 OnBumped() 호출
public class Bumper : MonoBehaviour
{
[SerializeField] MovingPlatform m_platform;
void OnTriggerEnter(Collider other)
{
if(other.CompareTag(Tags.Platform))
{
m_platform.OnBumped();
}
}
}
이동 발판에 하위 오브젝트 생성 후 사진과 같은 BoxCollider를 Trigger 체크하여 붙여주었습니다. 해당 하위 오브젝트에 Bumper 클래스를 붙여 부모 오브젝트(이동 발판)의 클래스(MovingPlatform)를 등록하게 한 뒤, 범퍼의 콜라이더가 다른 플랫폼과 충돌 시 OnBumped() 함수를 호출하도록 했습니다.
public void OnBumped()
{
dirVec *= -1;
}
OnBumped() 함수는 MovingPlatform 클래스의 함수로, 호출 시 방향 벡터에 -1을 곱하여 반대로 움직이도록 했습니다.
Bumper가 Platform 태그의 오브젝트와 충돌하면 반대로 움직이는 로직이므로, 다음과 같이 보이지 않는 콜라이더를 설치하여 왕복 기준으로 만들 수 있었습니다.
3. 캐릭터 이동
이제 발판의 이동은 문제없이 동작하는데, 위에 플레이어가 올라가도 발판 혼자 움직이는 문제가 있었습니다. 따라서 Player 클래스에 이동 발판 관련 로직을 추가하였습니다. 발판에서 플레이어를 움직이는 방법과 플레이어가 발판 위일 때를 인식해서 추가로 움직이는 방법 두가지를 생각했는데, 발판에서 다수의 플레이어를 움직이게 될 경우 비효율적일 것으로 판단되어 후자를 선택했습니다(추후 포톤 서버를 붙여 멀티플레이 구현할 예정).
MovingPlatform movingPlat;
MovingPlatform 클래스를 저장해두기 위한 변수 movingPlat을 추가하였고,
void OnCollisionEnter(Collision coll)
{
// 움직이는 발판 위에 있는 경우
if (coll.gameObject.CompareTag(Tags.MovingPlatform))
{
movingPlat = coll.gameObject.GetComponent<MovingPlatform>();
return;
}
}
void OnCollisionExit(Collision coll)
{
// 움직이는 발판에서 나갈 경우
if (coll.gameObject.CompareTag(Tags.MovingPlatform))
{
movingPlat = null;
return;
}
}
OnCollisionEnter()에서 이동 발판에 착지한 경우 해당 발판의 클래스를 movingPlat에 저장, OnCollisionExit()에서 이동 발판을 벗어날 경우 movingPlat을 null로 초기화하도록 했습니다.
void PlayerMove()
{
moveVec = inputVec * moveSpeed * Time.fixedDeltaTime;
// 움직이는 발판 위일 경우
if (movingPlat != null)
{
rigid.MovePosition(rigid.position + moveVec + movingPlat.GetMoveVec());
return;
}
// 일반 발판
rigid.MovePosition(rigid.position + moveVec);
}
// 플레이어에 발판의 이동벡터 전달
public Vector3 GetMoveVec()
{
return moveVec;
}
위쪽의 PlayerMove는 플레이어 이동용 함수로, inputVec은 Player Input 시스템에서 방향키 입력값을 받는 벡터입니다. 해당 벡터와 이동속도, fixedDeltaTime을 곱해 이동 벡터를 계산하고, movingPlat이 null이 아니라면(현재 이동 발판 위에 있다면) 원래 이동할 값에 movingPlat의 GetMoveVec() 함수의 반환값을 추가하고 있습니다.
GetMoveVec()은 MovingPlatform 클래스의 함수로, FixedUpdate()에서 계산한 발판의 이동 벡터(moveVec)를 반환하는 함수입니다. 이제 이동 발판 위의 플레이어가 발판과 함께 잘 움직입니다.
4. 결론
위와 같이 구현하면서 다음과 같은 이점을 얻을 수 있었습니다.
- 인스펙터 창에서 간단하게 이동 방향 선택 가능
- 범퍼의 콜라이더 크기를 조정하여 왕복거리 조절 가능
다만 아직 아래와 같은 단점이 있으므로, 추후 더 좋은 방법이 떠오르면 수정해야겠습니다.
- 결합도(Player 클래스에서 MovingPlatform을 저장하지 않고 구현할 순 없었을지)
- 범퍼(방향에 따라 범퍼 콜라이더를 수정하는 불편함 존재)
'포트폴리오' 카테고리의 다른 글
[Unity][포트폴리오] 멀티플레이 관련 이슈 (0) | 2023.06.11 |
---|---|
[Unity][포트폴리오] 진짜 혹은 가짜(True or False) 발판 (0) | 2023.06.10 |
[Unity][Photon][포트폴리오] 채팅 구현 (0) | 2023.06.06 |
[Unity][포트폴리오] 태그 관리용 클래스 / WaitForSeconds 관리용 클래스 (0) | 2023.05.31 |
[Unity][포트폴리오] 회전 발판(Rotating Platform) (0) | 2023.05.23 |