[240314]TIL

응징·2024년 3월 14일
0

TIL

목록 보기
35/36
post-thumbnail

CharacterController

IsGround 충돌체크

CharacterController는 collider에 비해 충돌체크가 무척이나 별로이다. 그래서 Ground 와 충돌을 체크할때 CharacterController의 내장되어있는 메소드로는 부족하다

때문에 Raycast로 충돌체크를 더 해 주었다.

 public bool IsGrounded()
 {
     Ray[] rays = new Ray[4]
     {
         new Ray(transform.position + (transform.forward * 0.2f) + (Vector3.up * 0.02f) , Vector3.down),
         new Ray(transform.position + (-transform.forward * 0.2f)+ (Vector3.up * 0.02f), Vector3.down),
         new Ray(transform.position + (transform.right * 0.2f) + (Vector3.up * 0.02f), Vector3.down),
         new Ray(transform.position + (-transform.right * 0.2f) + (Vector3.up * 0.02f), Vector3.down),
     };

     for (int i = 0; i < rays.Length; i++)
     {

         Debug.DrawRay(rays[i].origin, rays[i].direction * 0.1f, Color.red, 5f);

         if (Physics.Raycast(rays[i], 0.1f, groundLayerMask))
         {
             Debug.Log("그라운드");
             return true;
         }
     }

     return false;
 }

Slope Limit

  • Slope Limit :콜라이더가 명시된 값보다 작은 경사를 오르도록 제한
  • Step Offset :명시된 값보다 계단이 땅에 가까울 경우에만 캐릭터가 계단을 오른다
  • Skin width :두 콜라이더가 서로 스킨 너비 만큼 관통할 수 있다. 스킨 너비가 클수록 지터링이 감소
  • Min Move Distance :캐릭터가 지정한 값보다 낮게 움직이려고 할 경우 아예 움직이지 않게 된다.
  • Center : 월드 공간에서 캡슐 콜라이더를 오프셋
  • Radius 캡슐 콜라이더의 반지름 길이

기능은 위와 같다. 리지드바디에도 있는지는 모르겠지만 CharacterController 컴포넌트는 Slope Limit가 장점인것 같다.

원래는 이정도 낙차에도 지나가지 못해 플레이어를 점프하게 만들었으나, Slope Limit값을 높게 조정하여 이정도 낙차는 올라갈 수 있게 만들 수 있다. Step Offset 으로도 비슷한 효과를 낼수 있다. 이건 낙차로 구분하는 것이 아닌 오브젝트의 지면이 입력값 범위에 있는지 판단하여 올라갈지를 판단하여 이것이 좀더 컨트롤 하기 쉬울 수 있으나 그냥 작은 낙차정도만 올라간다면 Slope Limit로 충분하다.

아이템 가져오기

[로직]

  • 위 행동은 Idle 상태에서만 실행하기
  • 충돌한 오브젝트에서 Item 스크립트가 있는지 확인하기
  • Item 스크립트가 있다면 게임오브젝트를 인벤토리 오브젝트 하위에 두기
  • Item오브젝트에 정의되어 있는 vecter3로 아이템 오브젝트 Transform setting하기

때문에 Idle 상태일때만 아래 함수를 실행할 수 있도록하였다.

  • 입력값을 받았을 경우
  protected override void OnInteractionStarted(InputAction.CallbackContext context)
  {
      base.OnInteractionStarted(context);
      stateMachine.Player.interaction.SetInventory();
  }
  • 충돌체크
public class PlayerInteraction : MonoBehaviour
{
    //private Player player;
    //private PlayerInput Input;

    [SerializeField] Transform RayStart;
    [SerializeField] GameObject Inventory;

    public float radius = 5f;
    public int numberOfRays = 36;
    public float height = 0.25f;

    Item item;
    GameObject _object;

    private void Start()
    {
        //player = GetComponent<Player>();
        //Input = GetComponent<Player>().Input;
    }

    private void Update()
    {
        DeleteCheak();
    }

    public void DeleteCheak()
    {
        if(!CastCylinderRays())
        {
            item = null;
            _object = null;
        }
    }

    public bool CastCylinderRays()
    {
        for (int ii = 0; ii<5;ii++)
        {
            for (int i = 0; i < numberOfRays; i++)
            {
                // 각도를 계산
                float angle = (i * 180f) / numberOfRays;
                Vector3 direction = Quaternion.Euler(0, angle, 0) * transform.forward;

                Vector3 rayStart = transform.position + Vector3.up * (height*ii);
                RaycastHit hit;

                if (Physics.Raycast(rayStart, direction, out hit, radius))
                {
                    if(_object == hit.collider.gameObject) return true; //GetComponent 자제 시키기위한 처리

                    item = hit.collider.GetComponent<Item>();
                   

                    if (item != null && !item.Data.IsHave)
                    {
                        Debug.DrawLine(rayStart, hit.point, Color.red);

                        _object = hit.collider.gameObject;

                        Debug.Log("상호작용");
                        return true;

                    }
                }
                else
                {
                    Debug.DrawRay(rayStart, direction * radius, Color.green);
                }
            }
        }
        return false;
    }

    public void SetInventory()
    {
        if(item != null)
        {
            Debug.Log("장착");
            _object.transform.SetParent(Inventory.transform);
            _object.transform.position = item.Data.setPosition;
            _object.transform.rotation = Quaternion.Euler(item.Data.setRotation);
            _object.transform.localScale = item.Data.setScail;

            //_object.GetComponent<Rigidbody>().enabled = false;
            return;
        }
    }
}

아이템 세팅

플레이어가 잡을땐 플레이어를 가리지 않는 배치가 필요하기 때문에 각 아이템에 집었을때 어떻게 배치할것인가를 결정해줘야 한다. 필요하다면 Scail도 변경할 수 있다.

그래서 플레이어가 쏜 레이캐스트에 충돌한 오브젝트에 Item 스크립트가 있는지 확인한다. Item 스크립트를 넣는 작업은 맵 배치 해주시는 분이 노가다 해주기로 하였다.

튜터님의 조언

재경 튜터님의 질문: 왜 Collider를 안쓰고 레이로 어렵게 충돌체크를 하였는가?

이유:

콜라이더는 모든 충돌체들을 체크하기때문에 만약 Item 충돌체가 두개가 생기면 두개다 집어질것이라고 생각했기 때문이다.

그래도 RayCast를 사용했으나 UPdate문으로 확인하기 때문에 무분별한 GetComponent를 줄이기 위해서 저장되어 있는 오브젝트와 현재 충돌체가 같은 오브젝트라면 GetComponent를 하지 않고 중단하게 조치했다.

만약 콜라이더로 먼저 충돌된 오브젝트만 받으면 나머지는 행동은 하지 않고 빠져나갈 수 있는 방법이 있다면 콜라이더로 하는게 더 효율적일 것이다.

&

[내일 할 일]

Invatory 아이템 던지기 로직
Invatory 아이템 던지기 애니메이션
Invantory 아이템 내려놓기 로직
Invantory 아이템 내려놓기 애니메이션

점프하는 도중 벽에 부딫히면 점프력이 죽는 문제 해결하기

profile
Unity 개발 위주로 정리합니다

0개의 댓글