캐릭터를 조종하는 방법

 

1. Character Controller

  • Move
  • SimpleMove

2. Rigidbody

  • position
  • MovePosition

3. Transform

 

 

1. Character Controller

 

Character Controller는 물리적 특성을 사용하지않는 1인칭 3인칭 플레이어 제어에 사용

자체적으로 Collider가있다.

Slope Limit    콜라이더가 표시된 값 이하의 기울기만을 오르도록 제한
Step Offset   표시된 값보다 지면에 가까운 경우에만 캐릭터가 계단을 오름
Skin width    두 콜라이더가 Skin Width와 동일한 깊이에서 서로 관통
Min Move  Distance 캐릭터가 표시된 값 미만으로 움직이려 해도 움직이지 않음
Center  캡슐 콜라이더의 중심 위치
Radius  캡슐 콜라이더 반경
Height  캐릭터의 Capsule Collider 높이

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Controller : MonoBehaviour
{
    CharacterController controller;
    float Speed = 5.0f;

    void Start()
    {
        controller = GetComponent<CharacterController>();
    }
    void Update()
    {
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");
        Debug.Log(h + " " + v);
        Vector3 move = new Vector3(h, 0, v);

        //controller.Move(move * Speed * Time.deltaTime);
        //controller.SimpleMove(move * Speed);
    }

}
Move() SimpleMove()
특성
중력 계산 x
float 인자 (방향 * 속도 * deltaTime)
특성
중력 계산 o
float 인자 (방향 * 속도)

단점
Y축 속도가 무시된다.

여기서 점프를 구현하려면 조금 더 복잡해진다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Controller : MonoBehaviour
{
    CharacterController controller;
    float Speed = 5.0f;
    float Gravity = -9.8f;
    float JumpSpeed = 10.0f;
    Vector3 velocity;

    void Start()
    {
        controller = GetComponent<CharacterController>();
        velocity = new Vector3(0f, 0f, 0f);
    }
    void Update()
    {
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");
        Vector3 move = new Vector3(h, 0, v);

        if (move != Vector3.zero)
            transform.forward = move;

        velocity.y += Gravity * Time.deltaTime;
        controller.Move(velocity * Time.deltaTime);


        Debug.Log(controller.isGrounded);
        if (controller.isGrounded && velocity.y < 0)
            velocity.y = 0f;

        if (Input.GetButtonDown("Jump") && controller.isGrounded)
            velocity.y += Mathf.Sqrt(JumpSpeed * -2f * Gravity);

        controller.Move(move * Speed * Time.deltaTime);
    }

}

controller에 중력을 추가해주고 땅에 닿았을때는 중력을 다시 끄며 물리적 연산을 직접 구현해주어야한다.

콘솔 창은 isGrounded값인데 버그가 좀 많아서 잘 안쓰고 Raycast를 사용하여 체크한다고한다.

 

-> Character Controller는 경사면 제어에있어서 유리하지만 물리연산을 일일히 구현해 주어야하기때문에 양이 많아진다.

 

 

2. RigidBody

RigidBody가 부착된 GameObject는 물리적 제어 하에 동작하게된다.

Mass 오브젝트의 질량 설정 (상대적)
Drag 오브젝트의 저항력
0이면 공기 저항이 0이고, 무한대면 오브젝트는 즉시 움직임을 멈춤
Angular Drag 오브젝트의 회전에 대한 저항력
0이면 공기 저항이 0이고, 높을수록 회전을 더 많이 늦춤
이 값을 무한대(infinity)로 설정하는 것만으로는 오브젝트의 회전을 즉시 중지할 수 없다는 점에 유의해야 한다.
Use Gravity  활성화하면 오브젝트는 중력의 영향을 받는다.
Is Kinematic 활성화하면 오브젝트는 물리적 효과를 받지않음
(물리 엔진에 의해 주도되지 않으며, Transform에서만 조작될 수 있다. )
Interpolate Rigidbody의 움직임이 어색할 때 다음 옵션 중 선택할 수 있다.
 - None         보간을 적용하지 않는다.
 - Interpolate 이전 프레임의 Transform에 따라 Transform 스무딩.
 - Extrapolate 다음 프레임의 Transform에 따라 Estimated Transform 스무딩.
Collision Detection  충돌 감지 방법
 - Discrete 정상 충돌에 사용된다.
 - Continuous  동적 충돌 자 (강체가 있는 경우)와 정적 MeshColliders (강체가 없는 경우)에 대한
 연속 충돌 감지를 사용하여 개별 충돌 감지를 사용한다.
 - Continuous Dynamic 연속 및 연속 동적 충돌로 설정된 개체에 대해 연속 충돌 검색을 사용한다. 
 또한 고정 된 MeshColliders (rigidbody없이)에 대해 지속적인 충돌 감지를 사용한다. 빠르게 움직이는 물체에 사용된다.
Constraints
rigidbody의 동적 제한
Freeze Position 
 Rigidbody가 world 좌표의 X, Y 및 Z 축을 선택적으로 이동하는 것을 막는다.
Freeze Rotation Rigidbody가 로컬 X, Y 및 Z 축을 중심으로 회전하는 것을 막는다.

Update() - 화면 갱신 주기

FixedUpdate() - 물리 갱신 주기

 

물리적 연산은 고정된 시간간격의 호출인 FixedUpdate에서 더 적합하다고 한다.

 

이 두 함수는 똑같은 만큼 움직일 것이다.

void Update()
{
    rigidBody.MovePosition(rigidBody.position + move * Speed * Time.deltaTime);
}

void FixedUpdate()
{
    rigidBody.MovePosition(rigidBody.position + move * Speed * Time.fixedDeltaTime);
}

하지만 불규칙한 호출의 Update는 물리엔진 충돌 검사가 제대로 되지않을수도있으므로 사용하는걸 지양한다.

 

키 입력은 Update(), 물리 효과 검사는 FixedUpdate()

isGrounded는 raycast혹은 Physics.CheckSphere 등을 이용하여 검사할수있다. (이 코드에선 생략)

public class Controller2 : MonoBehaviour
{
    Rigidbody rigidBody;
    float Speed = 5.0f;
    float Gravity = -9.8f;
    float JumpSpeed = 10.0f;
    Vector3 move;

    void Start()
    {
        rigidBody = GetComponent<Rigidbody>();
    }

    void Update()
    {
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");
        move = new Vector3(h, 0, v);

        if (move != Vector3.zero)
            transform.forward = move;

        if (Input.GetButtonDown("Jump"))
        {
            // 질량 무시하는 순간적인 힘 (프레임에 종속되지않음) 
            rigidBody.AddForce(Vector3.up * Mathf.Sqrt(JumpSpeed * -2f * Gravity), ForceMode.VelocityChange);
        }
    }

    void FixedUpdate()
    {
        rigidBody.MovePosition(rigidBody.position + move * Speed * Time.fixedDeltaTime);
    }
}

Character Controller과 비교하였을때 두 스크립트 모두 점프하는 코드지만 물리적 힘이 적용될때는 RigidBody가 훨씬 구현하기 쉬울것이다.

 

 

 

 

Rigidbody의 Position 과 MovePosition 의 자세한 차이는 이 블로그를 통해 공부하였다.

 

[출처] [유니티]Rigidbody. position과 MovePosition()의 차이와 성능. 그리고 결론?|작성자 sabotduke

 

[유니티]Rigidbody. position과 MovePosition()의 차이와 성능. 그리고 결론?

스크립트 최적화에 간단히 쓸 생각이었다가 깜박 잊었다. 처음 유니티 배울 때 position과 MovePosition(...

blog.naver.com

-> rigidbody.position을 사용할 때는 순간이동 , MovePosition은 이동

 

 

 

 

3. Transform

 

Transform.position같은경우는 오브젝트를 움직일때 연결된 모든 콜라이더들이 rigidbody의 위치를 다시계산한다고한다.

위의 참조한 블로그에서 어느 개발자가 50만번을 실행한 결과 

 

Object with NO Collider and NO rigidbody:

transform.position: 2.540ms

Object with Collider and NO rigidbody:

transform.position: 11.296ms

Object with Collider and Rigidbody:

transform.position: 20.429ms

rigidbody.position: 1.689ms

rigidbody.MovePosition(): 1.701ms

 

정말 부득이한 상황아니면 rigidbody가 붙어있을때는 position(순간이동)과 MovePosition(이동)을 사용하도록하는것이 성능면에있어서 좋다.

'유니티' 카테고리의 다른 글

유니티 Input System  (0) 2025.02.18
Atan  (0) 2025.01.06
Coroutine  (0) 2022.04.07
Photon Animator View (Trigger)  (0) 2022.04.06
오브젝트 풀링 (Object Polling)  (0) 2022.03.16

+ Recent posts