유니티 캐릭터 이동에 관한 글입니다.
1. Transform
Transform을 사용하여 캐릭터 이동을 구현합니다.
transform.position, transform.Translate()의 차이점은 아래의 글을 참조.
사용자의 입력을 받아 캐릭터 이동을 구현합니다.
사용자의 입력을 받는 방법은 다양합니다.
Input.GetAxis("Horizontal"), Input.GetAxis("Vertical")
이는 키보드의 상하좌우 화살표 키 또는 ASDW 키 입력으로 구현된다.
이는 유니티 기본 설정 값이다.
Edit → Project Settings → Input Manager 설정에서 변경 가능하다.
1.1 Input.GetAxis() vs Input.GetAxisRaw()
Input.GetAxis()는 -1.0f ~1.0f 사이의 값을 받습니다.
Input.GetAxisRaw()는 -1, 0, 1의 값을 받습니다.
Input.GetAxis()는 사용자가 버튼 입력 시, 0부터 점차 값이 증가하여 최종적으로 1의 값을 지속적으로 반환합니다.
버튼 입력을 중지하면, 버튼을 뗀 순간 값부터 점차 감소하여 최종적으로 0의 값을 반환합니다.
예로, 자동차의 이동을 들 수 있습니다.
Input.GetAxisRaw()는 일정한 값을 반환하므로, 일정한 이동을 하는 곳에 사용하면 됩니다.
예로, 테트리스의 이동을 들 수 있습니다.
1.2 Input.GetAxis()
Input.GetAxis()를 사용하여 이동을 구현합니다.
Horizontal 은 가로 이동
Vertical 은 세로 이동
moveSpeed는 캐릭터의 이동 속도를 조절하기 위한 값입니다.
Time.deltaTime은 지난 프레임이 완료되는 데 까지 걸린 시간을 나타냅니다. (Frame rate)
실제 프로그램이 10 프레임당 5m를 이동하도록 설계되었다 가정하겠습니다.
가령, 온라인 상에 두 사용자가 있을 때, A의 컴퓨터 성능이 좋아 10 프레임당 6m를 가고, B는 10 프레임당 1m를 갑니다. 이는 의도와는 다릅니다. 이를 보완해주기 위해 Time.deltaTime을 사용합니다.
Time.deltaTime을 곱해 A는 5m에 근접하도록 낮추고, B는 5m에 근접하도록 올려줍니다.
※ Time.deltaTime은 보정.
public class CharacterMove : MonoBehaviour
{
// //은 주석 입니다.
// Vector2는 X, Y 값을 가집니다.
// Vector3는 X, Y, Z 값을 가집니다.
public float moveSpeed = 2.0f;
void Update()
{
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
transform.position += new Vector3(h, 0, v);
}
}
1.3 Input.GetKey()
Input.GetAxis(), Input.GetAxisRaw() 이외에도 Input.GetKey()를 이용하여 캐릭터 이동을 구현합니다.
public class CharacterMove : MonoBehaviour
{
// //은 주석 입니다.
// Vector2는 X, Y 값을 가집니다.
// Vector3는 X, Y, Z 값을 가집니다.
public float moveSpeed = 3.0f;
void Start()
{
}
void Update()
{
if(Input.GetKey(KeyCode.LeftArrow))
{
transform.Translate(Vector3.left * moveSpeed * Time.deltaTime);
}
if(Input.GetKey(KeyCode.RightArrow))
{
transform.Translate(Vector3.right * moveSpeed * Time.deltaTime);
}
if(Input.GetKey(KeyCode.UpArrow))
{
transform.Translate(Vector3.forward * moveSpeed * Time.deltaTime);
}
if(Input.GetKey(KeyCode.DownArrow))
{
transform.Translate(Vector3.back * moveSpeed * Time.deltaTime);
}
}
}
1.4 Input Manager
Input.GetKey(Keycode)에 사용하는 KeyCode는 KeyCode.LeftArrow 이외에도 다양한 키 값들이 있습니다.
Input.GetAxis(string AxisName)에 사용하는 AxisName은 위에 언급한 것 이외에도 다양한 값들이 있습니다.
Input Manager에서 이를 관리하며, 프로그램에 맞게 구현할 수 있습니다.
아래의 위치에서 추가 및 삭제 등 관리가 가능합니다.
Edit → Project Settings → Input Manager
2D 이동은 Horizontal 은 X 값 입력 , Vertical 은 Y 값 입력을 이용하면 됩니다.
public class CharacterMove : MonoBehaviour
{
// //은 주석 입니다.
// Vector2는 X, Y 값을 가집니다.
// Vector3는 X, Y, Z 값을 가집니다.
public float moveSpeed = 5.0f;
void Start()
{
}
void Update()
{
transform.position += new Vector3(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"), 0)
* moveSpeed * Time.deltaTime;
}
}
2. Rigidbody
Rigidbody는 월드 좌표를 사용합니다.
Rigidbody를 이용한 이동 역시 내부적으로는 Transform을 사용합니다.
rigidbody의 position을 이용한 방법은 위의 transform과 동일합니다.
단지, rigidbody의 position을 사용합니다.
2D 이동은 X, Y 좌표를 사용하고, 3D 이동은 X, Z 좌표를 사용합니다.
public class CharacterMove : MonoBehaviour
{
public float moveSpeed = 10.0f;
Rigidbody body;
void Start()
{
body = GetComponent<Rigidbody>();
// 중력해제
body.useGravity =false;
}
void FixedUpdate()
{
body.position += new Vector3(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"), 0)
* moveSpeed * Time.fixedDeltaTime;
}
}
2.1 Velocity
2.1.1 월드 좌표를 기준으로 velocity(속도)를 주어 캐릭터를 이동합니다.
public class CharacterMove : MonoBehaviour
{
// //은 주석 입니다.
// Vector2는 X, Y 값을 가집니다.
// Vector3는 X, Y, Z 값을 가집니다.
public float moveSpeed = 10.0f;
Rigidbody body;
void Start()
{
body = GetComponent<Rigidbody>();
// 중력해제
body.useGravity =false;
}
void FixedUpdate()
{
body.velocity = new Vector3(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"), 0)
* moveSpeed * Time.fixedDeltaTime;
}
}
2.1.2 로컬 좌표를 기준(캐릭터 기준)으로 velocity를 주어 캐릭터를 이동합니다.
벡터의 normalize( 정규화 )는, 해당 벡터가 1의 magnitude를 갖도록 설정합니다.
Vector와 normalized Vector의 크기는 다르고, 벡터의 방향은 같습니다.
transform.forward는 월드 좌표 기준 오브젝트의 회전 값을 반영한 normalized된 값을 반환합니다.
현재 캐릭터의 forward와 입력받은 값(방향)을 곱하여, 캐릭터를 기준(로컬 좌표)으로 캐릭터를 이동합니다.
public class CharacterMove : MonoBehaviour
{
public float moveSpeed = 10.0f;
Rigidbody body;
void Start()
{
body = GetComponent<Rigidbody>();
}
void FixedUpdate()
{
float h = Input.GetAxis("Horizontal")
float v = Input.GetAxis("Vertical");
Vector3 direction = new Vector3(h, 0, v);
direction = transform.forward * direction.z + transform.right * direction.x;
body.velocity = direction * moveSpeed * Time.fixedDeltaTime;
}
}
3. Rotation
회전 각도를 구합니다.
float angle = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg;
float angle = Vector3.Angle(Vector3 from, Vector3 to);
3.1 transform.rotation
월드 좌표를 기준으로 회전을 합니다.
transform.rotation은 Quaternion값을 사용합니다.
angle을 Quaternion값으로 변환하여, 이를 대입해 캐릭터를 회전 합니다
public class CharacterMove : MonoBehaviour
{
public float moveSpeed = 10.0f;
public float rotationSpeed = 5.0f;
Rigidbody body;
void Start()
{
body = GetComponent<Rigidbody>();
// 중력해제
body.useGravity =false;
}
void FixedUpdate()
{
float h = Input.GetAxis("Horizontal")
float v = Input.GetAxis("Vertical");
Vector3 direction = new Vector3(h, 0, v);
if(direction != Vector3.zero)
{
float angle = Mathf.Atan2(velocity.x, velocity.z) * Mathf.Rad2Deg;
// 즉시 회전
body.rotation = Quaternion.Euler(0, angle, 0);
// 부드러운 회전
// body.rotation = Quaternion.Slerp(body.rotation, Quaterniton.Euler(0, angle, 0), rotationSpeed * Time.fixedDeltaTime);
}
}
}
3.2 transform.rotate()
로컬 좌표를 기준으로 회전을 합니다.
public class CharacterMove : MonoBehaviour
{
void Update()
{
float h = Input.GetAxis("Horizontal")
float v = Input.GetAxis("Vertical");
Vector3 direction = new Vector3(h, 0, v);
if(direction != Vector3.zero)
{
float angle = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg;
trnasform.Rotate(Vector3.Up, angle);
}
}
}
4. 이동과 회전
캐릭터를 움직이는 방법은 다양합니다. 어떠한 목적을 가지고 캐릭터를 이동할 것인지를 선택해야 합니다.
또한, 다양한 게임들이 존재합니다. FPS 게임, RPG 게임, 디펜스 게임 등 이러한 게임들은 각자의 목적에 맞는 방식으로 이동을 구현합니다. 1인칭 시점으로 캐릭터를 기준으로 이동을 구현, 3인칭 시점으로 월드를 기준으로 이동을 구현합니다.
앞서 1~3장에서는 이동과 회전에 관하여 각각 구분하여 다루었습니다.
이번에는 이동과 회전을 동시에 하도록 구현합니다.
4.1 월드 좌표
월드 좌표를 기준으로 캐릭터를 이동합니다.
public class CharacterMove : MonoBehaviour
{
public float moveSpeed = 10.0f;
public float rotationSpeed = 5.0f;
Rigidbody body;
void Start()
{
body = GetComponent<Rigidbody>();
// 중력해제
body.useGravity =false;
}
void FixedUpdate()
{
float h = Input.GetAxis("Horizontal")
float v = Input.GetAxis("Vertical");
Vector3 direction = new Vector3(h, 0, v);
if(direction != Vector3.zero)
{
float angle = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg;
// 즉시 회전
body.rotation = Quaternion.Euler(0, angle, 0);
// 부드러운 회전
// body.rotation = Quaternion.Slerp(body.rotation, Quaterniton.Euler(0, angle, 0), rotationSpeed * Time.fixedDeltaTime);
}
body.position += direction * moveSpeed * Time.fixedDeltaTime;
}
}
4.2 로컬 좌표
캐릭터를 기준으로 이동 및 회전을 합니다.
입력받은 값을 기준으로 회전 각도가 90도보다 작은 경우에는 회전을 하며, 90도 보다 큰 경우에는
회전을 하지 않고, 이동만 하도록 구현합니다. (-90 ~ 90 )
뒤로 이동하는 경우, 180도는 회전만 앞 뒤로 반복하는 문제가 발생하기 때문에,
90도로 한정하여 구현합니다.
public class CharacterMove : MonoBehaviour
{
public float moveSpeed = 10.0f;
public float rotationSpeed = 5.0f;
Rigidbody body;
void Start()
{
body = GetComponent<Rigidbody>();
// 중력해제
body.useGravity =false;
}
void Update()
{
float h = Input.GetAxis("Horizontal")
float v = Input.GetAxis("Vertical");
Vector3 direction = new Vector3(h, 0, v);
if(direction != Vector3.zero)
{
float angle = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg;
if(90.0f > angle && angle > -90.0f )
{
angle = angle * rotationSpeed * Time.deltaTime;
trnasform.Rotate(Vector3.Up, angle);
}
}
transform.Translate(direction * moveSpeed * Time.deltaTime);
}
}
5. 마우스 이동
마우스로 이동하는 방법은 다음 글에서 이어갑니다.
참고자료
https://answers.unity.com/questions/44199/how-do-i-make-rigidbodyvelocity-in-local-space.html
https://docs.unity3d.com/Manual/class-InputManager.html
'유니티 > 기초' 카테고리의 다른 글
유니티 캐릭터 점프 [기초 6] (0) | 2020.09.13 |
---|---|
유니티 캐릭터 마우스 이동[기초 4] (0) | 2020.09.07 |
유니티 카메라 이동 및 회전 [기초 5] (1) | 2020.05.05 |
유니티 오브젝트 회전 [기초 2] (0) | 2020.05.04 |
유니티 오브젝트 이동 [기초 1] (0) | 2020.05.01 |