게임클라이언트 프로그래밍/3D Project

Rigidbody 와 Collider로 캐릭터 이동 / Character Controller로 캐릭터 이동

Game Client Lee Hwanguk 2023. 7. 31. 23:28

# 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 적용

 

*게임이 시작되면 캐릭터는 자동으로 전진. Character 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;
            }
        }
    }
}