경사면을 감지하여 플레이어를 미끄러지게 하는 것은 게임의 현실성을 높이기 위해 사용됩니다. 또한, 개발자가 의도하지 않은 공간으로 이동하지 못하도록 제한할 수 있습니다.

 

💬 서론

  • RigidBody는 자동으로 경사면 미끄러짐을 지원합니다. 하지만 캐릭터 컨트롤러는 지원하지 않기에 캐릭터 컨트롤러에서 경사면 미끄러짐을 구현합니다.
  • 이 기능은 캐릭터 컨트롤러 (CharacterController) 컴포넌트를 사용하는 대상에서 적용이 가능합니다.

 

📺 미끄러짐 유무 차이

  • 미끄러짐 기능이 없을때는 플레이어가 무한정으로 점프를하여 이동할 수 있습니다.
  • 하지만 미끄러짐 기능을 추가하면 플레이어는 미끄러질때 더 이상 점프를 할 수 없어 의도하지 않은 곳으로 이동할 수 없습니다.

 

✅ 구현(함수)

  • 기존 게임 프로젝트에서 사용하는 PlayerController에 미끄러짐 기능을 추가한 스크립트이기에 본 글의 주제인 미끄러짐에 관련된 내용만 요약하여 스크립트를 재구성하였습니다.

 

//PlayerController.cs

...

public class PlayerController : BaseFSM
{
    ...
    
    public static bool IsSliding { private set; get; } = false;

    [Header("미끄러질경우 미끄러짐 속도")]
    [SerializeField] private float mSlideSpeed = 3f; // 미끄러질 때의 속도

	...

    [HideInInspector] public CharacterController CharController;  //캐릭터 컨트롤러
    [HideInInspector] public Animator Animator;   //애니메이터

    ...

    private void Awake()
    {
        CharController = GetComponent<CharacterController>();
        
        ...

        IsSliding = false;
    }

	...

    /// <summary>
    /// 미끄러짐 검사 및 미끄러질경우 이동
    /// </summary>
    public void SlidingCharacter()
    {
        if(!CharController.isGrounded)
            return;

        RaycastHit hit;
        if (Physics.Raycast(transform.position, Vector3.down, out hit, CharController.height / 2))
        {
            // 경사면의 기울기가 slopeLimit 보다 클경우 미끄러짐 처리
            if (Vector3.Angle(hit.normal, Vector3.up) > CharController.slopeLimit)
            {
                //애니메이션 블렌드 트리의 값 설정
                Animator.SetFloat("_MovementSpeed", CharController.velocity.magnitude);

                //이동
                CharController.Move(-Vector3.ProjectOnPlane(-Physics.gravity, hit.normal).normalized * Time.deltaTime * mSlideSpeed);

                IsSliding = true;
                return;
            }
        }

        IsSliding = false;
    }

    ...
    
}

 

[Header("미끄러질경우 미끄러짐 속도")]
[SerializeField] private float mSlideSpeed = 3f; // 미끄러질 때의 속도
  • 미끄러짐 속도를 설정합니다.

 

public static bool IsSliding { private set; get; } = false;
  • 현재 플레이어가 미끄러지고있는 상태인지 확인합니다.
  • 플레이어가 미끄러지고있다면, 이 값은 TRUE가 됩니다.
  • 이 값이 TRUE일경우 점프를 제한하는 등 여러가지 기능을 제한할 수 있습니다.

 

/// <summary>
/// 미끄러짐 검사 및 미끄러질경우 이동
/// </summary>
public void SlidingCharacter()
{
    if(!CharController.isGrounded)
        return;

    RaycastHit hit;
    if (Physics.Raycast(transform.position, Vector3.down, out hit, CharController.height / 2))
    {
        // 경사면의 기울기가 slopeLimit 보다 클경우 미끄러짐 처리
        if (Vector3.Angle(hit.normal, Vector3.up) > CharController.slopeLimit)
        {
            //애니메이션 블렌드 트리의 값 설정
            Animator.SetFloat("_MovementSpeed", CharController.velocity.magnitude);

            //이동
            CharController.Move(-Vector3.ProjectOnPlane(-Physics.gravity, hit.normal).normalized * Time.deltaTime * mSlideSpeed);

            IsSliding = true;
            return;
        }
    }

    IsSliding = false;
}
  • 미끄러짐을 검사하고, 조건에 맞을경우 플레이어를 미끄러지게 합니다.
  • Raycast를 바닥으로 쏘아 플레이어가 서있는 경사면을 감지합니다.
  • Vector3.Angle(hit.normal, Vector3.up)을 통해 경사면의 각도를 얻습니다. 이 각도가 캐릭터 컨트롤러의 경사각보다 크다면 미끄러짐을 적용합니다.
  • ProjectOnPlane 함수를 이용하여 경사면의 수직인 벡터를 얻습니다.
 

Unity - Scripting API: Vector3.ProjectOnPlane

A Vector3 stores the position of the given vector in 3d space. A second Vector3 is given by planeNormal and defines a direction from a plane towards vector that passes through the origin. Vector3.ProjectOnPlane uses the two Vector3 values to generate the p

docs.unity3d.com

  • 그 후에 캐릭터 컨트롤러를 이동시킵니다.

 

✅ 사용 예시

public override void Execute(PlayerController entity)
{
    //떨어짐 체크
    entity.CheckFalling();
    entity.SlidingCharacter();
}
  • 매 프레임마다 미끄러짐을 계산합니다.
  • 미끄러짐이 활성화되면 IsSliding이 참이됩니다.

 

/// <summary>
/// 점프 키를 눌러 점프를 하도록 한다.
/// </summary>
/// <returns>점프를 한 경우에 true가 반환된다.</returns>
public bool Jump()
{
    //현재 커서가 언락 상태라면? false 리턴
    if(UtilityManager.IsCursorUnlock || IsSliding) { return false; }

    if (CharController.isGrounded && Input.GetButton("Jump"))
    {
        mMoveDir.y = mJumpForce;
        CharController.Move(mMoveDir * Time.deltaTime);

        return true;
    }

    return false;
}
  • 플레이어가 점프를 시도하기 위한 함수입니다.
  • IsSliding이 참인경우 리턴하여 미끄러지고있는 상태에서 더 이상 점프를 하지 못하게 합니다.
bonnate