Rigidbody 와 Collider로 캐릭터 이동 / Character Controller로 캐릭터 이동
# 3D환경에서 캐릭터를 이동하는 방식은 RigidBody와 Collider 컴포넌트를 이용하는 방법과 Character Controller 컴포넌트를 이용하는 방법이 있다. 이 두가지의 차이점과 장단점을 공부하는 중이다.
두 조건 모두 같은 연출을 만들고 차이를 비교해보자.
1. RigidBody와 Collider 컴포넌트를 이용
* RigidBody
https://docs.unity3d.com/kr/2021.3/Manual/class-Rigidbody.html
리지드바디 - Unity 매뉴얼
Rigidbody 는 GameObject 가 물리 제어로 동작하게 합니다. 리지드바디는 힘과 토크를 받아 오브젝트가 사실적으로 움직이도록 해줍니다. 리지드바디가 포함된 모든 게임 오브젝트는 중력의 영향을
docs.unity3d.com
- Transform을 사용하여 움직이는 것과는 차이가 있다. 오브잭트의 움직임을 물리엔진이 제어하게되는 것.
Transform과의 가장 큰 차이점은 "힘의 사용(AddForce(),AddTorque()", Transform과 동시에 사용하게 된다면 충돌 및 기타 연산에 문제가 발생할 수 있음. 오브잭트에 Collider 컴포넌트가 부착되어 있지 않다면 물리연산 불가능.
*Collider
https://docs.unity3d.com/kr/2018.4/Manual/CollidersOverview.html
콜라이더 - Unity 매뉴얼
콜라이더(Collider) 컴포넌트는 물리 충돌 처리를 위한 오브젝트의 형태를 정의합니다. 콜라이더는 보이지 않는 요소이므로 오브젝트의 메시와 정확히 동일한 모양일 필요는 없으며, 실제로는 게
docs.unity3d.com
- 물리 충돌 처리를 위한 오브잭트의 형태를 정의함.
2. Character Controller 컴포넌트를 이용
*Character Controller
https://docs.unity3d.com/kr/2021.1/Manual/class-CharacterController.html
캐릭터 컨트롤러 - Unity 매뉴얼
캐릭터 컨트롤러(Character Controller) 는 Rigidbody 물리를 활용하지 않는 3인 또는 1인 플레이어에 주로 사용됩니다.
docs.unity3d.com
3. 물리엔진 사용/ 사용하지 않음의 장단점과 사용예시
*장단점
1. 물리 엔진을 사용:
*장점: 물리 엔진을 사용하면 자연스러운 움직임과 반응을 구현할 수 있습니다. 물리적 특성을 고려한 캐릭터 이동, 충돌, 점프 등이 가능합니다.
충돌 처리와 물리 효과가 자동으로 처리되므로, 캐릭터가 주위 환경과 상호작용할 때 더 현실적인 느낌을 줄 수 있습니다.
*단점: 물리 엔진을 사용하면 CPU와 GPU 자원을 많이 사용하게 됩니다. 특히, 캐릭터의 수가 많거나 복잡한 물리 시뮬레이션을 사용할 경우 성능 문제가 발생할 수 있습니다.
일부 상황에서 원하는 동작을 정확하게 제어하기 어려울 수 있습니다. 특히, 플레이어가 움직임을 완전히 제어하고 싶을 때 물리 엔진을 사용하는 것은 쉽지 않을 수 있습니다.
2. 물리 엔진을 사용하지 않는:
*장점: 물리 엔진을 사용하지 않으면 더 예측 가능하고 정확한 캐릭터 제어가 가능합니다. 특히, 플레이어가 직접 조작하는 캐릭터의 경우, 물리 시뮬레이션보다 더 정확한 조작이 필요할 수 있습니다.
물리 엔진을 사용하지 않으면 게임의 성능이 향상될 수 있습니다. 복잡한 물리 시뮬레이션을 사용하지 않기 때문에 자원 소모가 줄어들 수 있습니다.
*단점: 물리 엔진을 사용하지 않으면 캐릭터의 움직임이 덜 현실적일 수 있습니다. 특히, 점프와 충돌처리를 물리적으로 다루기 어려울 수 있습니다.
일부 특수한 상황에서는 물리 엔진을 사용해야 하는데, 이런 경우에는 직접 구현해야 하므로 추가적인 개발 비용과 시간이 소요될 수 있습니다.
*사용예시
1. 물리 엔진을 사용
- 3DSphere에 Rigidbody와 Collider적용
*Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class SphereGo : MonoBehaviour
{
public float jumpForce = 10f;
public float maxJumpHeight = 10f;
public float minJumpHeight = 1f; //최소 점프 높이
public float bounceDamping = 0.2f; //튀기는 힘 감쇠 비율
public float rollingSpeed = 3f;
public int maxJumpCount = 1; //최대 점프 횟수
private int currentJumpCount = 0; //현재 점프 횟수
public float rollingDamping = 0.1f; // 좌우 움직임 감쇠 비율
private bool isJumping = false;
private Rigidbody rb;
private void Start()
{
rb = GetComponent<Rigidbody>();
}
private void Update()
{
this.rb.AddForce(new Vector3(0f, 0f, this.rollingSpeed));
if (Input.GetKeyDown(KeyCode.Space) && !isJumping)
{
Jump();
}
}
private void Jump()
{
rb.velocity = new Vector3(rb.velocity.x, Mathf.Sqrt(2f * maxJumpHeight * Mathf.Abs(Physics.gravity.y)), rb.velocity.z) * bounceDamping;
isJumping = true;
currentJumpCount++;
}
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.CompareTag("Ground"))
{
isJumping = false;
if (currentJumpCount >= maxJumpCount)
{
rb.velocity = new Vector3(rb.velocity.x, Mathf.Sqrt(2f * minJumpHeight * Mathf.Abs(Physics.gravity.y)), rb.velocity.z) * bounceDamping;
}
currentJumpCount = 0;
}
}
private void FixedUpdate()
{
float moveDirection = Input.GetAxis("Horizontal");
Vector3 moveVelocity = new Vector3(moveDirection, 0f, 0f) * rollingSpeed;
rb.velocity = new Vector3(moveVelocity.x, rb.velocity.y, rb.velocity.z);
rb.AddForce(new Vector3(-rb.velocity.x * (1 - rollingDamping), 0f, 0f), ForceMode.VelocityChange);
}
}
2. 물리 엔진을 사용하지 않은
- Charater Controller 적용
*Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerGo : MonoBehaviour
{
public float moveSpeed = 5f;
public float jumpForce = 10f;
public float maxJumpHeight = 10f;
public int maxJumpCount = 1;
private CharacterController characterController;
private float currentJumpHeight = 0f;
private int currentJumpCount = 0;
private bool isJumping = false;
private Vector3 moveDirection = Vector3.zero;
private void Start()
{
characterController = GetComponent<CharacterController>();
}
private void Update()
{
float horizontalInput = Input.GetAxis("Horizontal");
moveDirection = new Vector3(horizontalInput, 0f, 1f).normalized;
moveDirection *= moveSpeed;
if (Input.GetKeyDown(KeyCode.Space) && !isJumping)
{
Jump();
}
if (isJumping)
{
ApplyGravity();
}
characterController.Move(moveDirection * Time.deltaTime);
}
private void Jump()
{
currentJumpHeight = maxJumpHeight;
isJumping = true;
currentJumpCount++;
}
/// <summary>
/// 물리효과 구현
/// 중력 구현, 점프 횟수 초기화
/// </summary>
private void ApplyGravity()
{
float gravity = Mathf.Sqrt(2f * Mathf.Abs(Physics.gravity.y) * currentJumpHeight);
characterController.Move(Vector3.up * gravity * Time.deltaTime);
//점프 높이 감소(중력의 영향)
currentJumpHeight -= gravity * Time.deltaTime;
currentJumpHeight = Mathf.Max(currentJumpHeight, 0f); //음수 값을 방지하기 위해 0으로 최소값을 설정
//점프가 끝났을 때 처리(점프 횟수 초기화, isJumping 초기화)
if (currentJumpHeight <= 0f)
{
isJumping = false;
if (currentJumpCount >= maxJumpCount)
{
currentJumpCount = 0;
}
}
}
}