본문 바로가기
유니티 게임 개발

[유니티 강좌] 2D RPG 게임 만들기 - 6 / Translate의 문제점과 대안

by 호일이 2020. 6. 6.

지금까지 이동은 Translate라는 함수로 했습니다. 이 함수는 물리 제어로 움직이게 하는 것이 아니라서 약간의 문제가 있습니다. 예를 들어 Collider가 붙어있다고 가정했을 때 만약 어떤 물체와 맞닿은 상태에서 Translate로 계속 부딪히려고 한다면 밀어내는 것과 움직이는 것이 불규칙적으로 반복되어서 진동하는 것처럼 보이게 됩니다.

 

Update에서 Translate 사용

이것은 Collider가 밀어내는 것(FixedUpdate)과 움직이는 것(Update)이 일정하지 않아서 발생하는 현상이므로, Translate를 FixedUpdate에서 사용하게 된다면 진동하지 않습니다.

더보기

0.02초당 한번씩(초당 50번) 고정적으로 실행되는 메시지입니다. 

물리 시스템은 모두 FixedUpdate로 처리됩니다.

FixedUpdate에서 Translate 사용

움직일 때 약간 벽쪽으로 들어가고 움직이지 않으면 원위치로 밀어지게 됩니다. Translate의 속도에 따라 더 들어가거나, 아예 뚫고 갈 수도 있습니다. 들어가는 것을 최소화 시키려면 Rigidbody의 Collision Detection(충돌 감지)을 Continuous(연속)으로 바꿔주면 됩니다.

(원래 고속으로 움직이는 오브젝트가 벽을 빠져나오는 것을 방지하기 위해 사용하는 것으로, 이 방법은 물리연산 성능에 영향을 크게 준다고 합니다. 테스트 해보지 않아서 어느정도 영향이 있는지 모르겠지만 주의하면 될 것 같습니다.)

Collision Detection이 Discrete일 땐 0.012정도가 벽에 들어갔는데 Continuous로 바꾸고 실행하면 0.0004정도 들어가게 됩니다. 

별로 신경쓰지 않아도 되는 게임이라면 위와 같은 방법으로 할 수 있지만 벽에 박히는 일이 없어야 하는 게임이라면 지금까지 나온 것들은 사용할 수 없습니다.

 

지금까지 실험으로 Translate는 Collider끼리의 충돌처리에서 잘 맞지 않는다는 것을 알았습니다.

따라서 물체의 이동에 대한 다른 방법들이 필요합니다.

 

이동 방법 소개

대표적인 이동 방법

1. Rigidbody.MovePosition

2. Rigidbody.AddForce

3. Rigidbody.velocity

 

먼저 주의해야 할 것이 있습니다.

Rigidbody를 사용한 이동FixedUpdate에서 처리되어야 합니다. 그리고 키 입력Update에서 받아야 합니다.

저도 알고 많이 혼란스러워했지만 키 입력과 물리적 처리는 나뉘는게 안정적이라고 하더군요.

만약 FixedUpdate에서 키입력을 처리하면 FixedUpdate에서 처리되지 않고 입력 감지에 실패할 수 있습니다.

특히 KeyDown, keyUp이 그렇습니다. GeyKey는 연속적으로 입력하기 때문에 처음 입력이 안됐다고 해도 문제없이 실행될 수 있습니다. 다만 GetKeyDown이나 GetKeyUp은 한번만 입력이 되기 떄문에 손실이 발생할 수 있습니다.

 

관련 토론 글입니다.

 

Check for User Input in FixedUpdate?

Hi everyone! I'm new around these here parts! I've been doing some searches on the forums and I've read at least a couple of times that we should...

forum.unity.com

그럼 GetKey는 둘 중 어디에서나 사용해도 큰 문제가 없다는 것이니 고민을 되게 많이 했습니다.

결국 저는 통일성을 위해 모든 입력은 Update에서 받도록 했습니다.

 

bool inputRight = false;
bool inputLeft = false;
Rigidbody2D rigid2D;
void Start()
{
	rigid2D = GetComponent<Rigidbody2D>();
}
void Update() 
{
    if (Input.GetKey(KeyCode.RightArrow))
    {
        inputRight = true;
        transform.localScale = new Vector3(-1, 1, 1);
        animator.SetBool("moving", true);
    }
    else if (Input.GetKey(KeyCode.LeftArrow))
    {
        inputLeft = true;
        transform.localScale = new Vector3(1, 1, 1);
        animator.SetBool("moving", true);
    }
    else animator.SetBool("moving", false);
}

입력을 확인하기 위한 bool 변수를 선언했고, rigidbody를 사용하기 위해 변수를 선언하고 받아왔습니다.

Update에서는 물리처리를 제외한 코드를 실행시킵니다.

위 코드는 바뀌지 않고 밑의 코드와 같이 실행했습니다.

Rigidbody.MovePosition

void FixedUpdate() 
{
    if (inputRight)
    {
    	inputRight = false;
        rigid2D.MovePosition(rigid2D.position + Vector3.right * moveSpeed * Time.deltaTime);
    }
    if (inputLeft)
    {
    	inputLeft = false;
        rigid2D.MovePosition(rigid2D.position + Vector3.left * moveSpeed * Time.deltaTime);
    }
}

moveSpeed는 2로 설정했습니다.

잘 움직이고 벽을 뚫지 못하지만, 움직일 때 중력이 제대로 적용이 안됩니다.

MovePosition은 Rigidbody의 BodyType이 Kinematic일 때, 즉 중력이 필요 없을 때 사용하면 좋을 것 같습니다.

자세한 설명은 레퍼런스에서 확인해주세요.

 

Unity - Scripting API: Rigidbody.MovePosition

If the rigidbody has isKinematic set to false, it works like transform.position=newPosition and teleports the object to the new position (rather than performing a smooth transition).

docs.unity3d.com

Rigidbody.AddForce

if (inputRight)
{
    inputRight = false;
    rigid2D.AddForce(Vector2.right * moveSpeed);
}
if (inputLeft)
{
    inputLeft = false;
    rigid2D.AddForce(Vector2.left * moveSpeed);
}

// 속도 제한
if (rigid2D.velocity.x >= 2.5f) rigid2D.velocity = new Vector2(2.5f, rigid2D.velocity.y);
else if (rigid2D.velocity.x <= -2.5f) rigid2D.velocity = new Vector2(-2.5f, rigid2D.velocity.y);

moveSpeed는 5로 설정했습니다.

AddForce는 가속도가 잘 붙어서 속도에 제한을 걸었습니다.

x축의 속도가 일정이상 넘으면 속도를 어느정도 유지하는 코드입니다.

moveSpeed를 높이면 속도가 제한 속도를 넘어버려서 완벽히 제어는 힘듭니다.

벽을 뚫지 않고 중력도 적용이 됩니다.

 

혹시 완벽히 제어할 수 있는 방법을 알고 계시다면 공유 부탁드립니다.

 

Rigidbody.velocity

if (inputRight)
{
    inputRight = false;
    rigid2D.velocity = new Vector2(moveSpeed, rigid2D.velocity.y);
}
if (inputLeft)
{
    inputLeft = false;
    rigid2D.velocity = new Vector2(-moveSpeed, rigid2D.velocity.y);
}

moveSpeed는 2로 설정했습니다.

강체의 속도를 직접적으로 변경하는 방법입니다.

직접 수정하는 것이니만큼 잘못 만지면 이상해질 수 있으니 주의해야 합니다.


실험을 위해 점프를 먼저 구현했습니다.

감사합니다.

반응형

댓글