내일배움캠프/TIL

내일배움캠프 27일차 TIL - 3D 게임에서 지상 확인하기

서보훈 2024. 10. 24. 20:36

3D 숙련강의중, 캐릭터가 지상에 닿아있는지를 확인하기위해 이러한 코드를 사용합니다.

//캐릭터가 땅에 닿았는지 확인
private bool IsGrounded()
{
    Ray[] rays = new Ray[4]
    {
        new Ray(transform.position + (transform.forward * 0.2f) + (transform.up * 0.01f), Vector3.down),
        new Ray(transform.position + (-transform.forward * 0.2f) + (transform.up * 0.01f), Vector3.down),
        new Ray(transform.position + (transform.right * 0.2f) + (transform.up * 0.01f), Vector3.down),
        new Ray(transform.position + (-transform.right * 0.2f) + (transform.up * 0.01f), Vector3.down)
    };

    for(int i = 0; i < rays.Length; i++)
    {
        if (Physics.Raycast(rays[i], 0.1f, groundLayerMask))
        {
            return true;
        }
    }

    return false;
}

 

레이 배열을 선언 후 레이가 지정한 레이어의 오브젝트에 닿을경우 지상으로 판정하고 true를 반환하는 코드입니다.

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

여기서 이 레이들의 위치를 알 필요가 있습니다.

 

먼저 첫번째 레이는 이 스크립트가 들어간 오브젝트의 피벗의 전방에 위치합니다.

자신의 position + Vector3(0, 0 , 0.2) + Vector3(0, 0.01, 0) 위치에서 시작하여, 해당 위치의 -z 축 을 확인하며,

Physics.Raycast() 에서 지정한 0.1f 의 길이 내에 지정한 레이어 오브젝트가 있으면 true를 반환합니다.

위 이미지에서, 붉은 선에 Ground 레이어의 오브젝트가 닿으면 땅에 서있는것으로 판단하게 되는것입니다.

 

더보기

유니티 에디터 상에 오브젝트 선택시, 이 스크립트가 붙어있을경우 선등의 그림을 그려주는 메서드 입니다.

private void OnDrawGizmosSelected()
{
    Color greenColor = Color.green;
    Color redColor = Color.red;

    if(IsGrounded() )
    {
        Gizmos.color = greenColor;
    }
    else
    {
        Gizmos.color= redColor;
    }
    Vector3 a = transform.position + (transform.forward * 0.2f);
    Vector3 b = transform.position + (-transform.forward * 0.2f);
    Vector3 c = transform.position + (transform.right * 0.2f);
    Vector3 d = transform.position + (-transform.right * 0.2f);

    Gizmos.DrawLine(a + (transform.up * 0.01f), a - (transform.up * 0.1f));
    Gizmos.DrawLine(b + (transform.up * 0.01f), b - (transform.up * 0.1f));
    Gizmos.DrawLine(c + (transform.up * 0.01f), c - (transform.up * 0.1f));
    Gizmos.DrawLine(d + (transform.up * 0.01f), d - (transform.up * 0.1f));

}

 

이와 같이, 해당 레이들은 각각 앞, 뒤, 양옆 에서 땅에 닿은 여부를 판정하게됩니다.

모든 레이 판정을 그리면 이렇게 됩니다.

이 4개의 선중, 하나라도 바닦을 인식할경우 바닥에 닿았다는 판정을 발생시킵니다.

 

임시로 Cube 에 Ground 레이어를 적용시킨 뒤, 플레이어를 올려둔 모습입니다.

땅에 닿아있을경우, 색이 녹색이 되도록 만들어주었습니다.

이러한 충돌 판정의 경우, 한가지 문제가 발생합니다.

 

아래의 사진의 경우, 플레이어는 땅에 닿아있는 판정이 발생합니다.

하지만 여기서 시선을 45도 돌릴경우, 땅에 닿지 않는 판정이 됩니다.

 

이런 현상을 방지하기 위해, 바닥의 감지를 원형으로 할 수 있습니다.

//캐릭터가 땅에 닿았는지 확인
private bool IsGrounded()
{
    //Ray[] rays = new Ray[4]
    //{
    //    new Ray(transform.position + (transform.forward * 0.2f) + (transform.up * 0.01f), Vector3.down),
    //    new Ray(transform.position + (-transform.forward * 0.2f) + (transform.up * 0.01f), Vector3.down),
    //    new Ray(transform.position + (transform.right * 0.2f) + (transform.up * 0.01f), Vector3.down),
    //    new Ray(transform.position + (-transform.right * 0.2f) + (transform.up * 0.01f), Vector3.down)
    //};

    //for(int i = 0; i < rays.Length; i++)
    //{
    //    if (Physics.Raycast(rays[i], 0.1f, groundLayerMask))
    //    {
    //        return true;
    //    }
    //}

    Vector3 spherePosition = new Vector3(transform.position.x, transform.position.y + 0.01f, transform.position.z);
    if (Physics.CheckSphere(spherePosition, 0.1f, groundLayerMask, QueryTriggerInteraction.Ignore))
    {
        return true;
    }

    return false;
}
더보기
private void OnDrawGizmosSelected()
{
    Color greenColor = Color.green;
    Color redColor = Color.red;

    if(IsGrounded() )
    {
        Gizmos.color = greenColor;
    }
    else
    {
        Gizmos.color= redColor;
    }
    Gizmos.DrawSphere(new Vector3(transform.position.x, transform.position.y + 0.01f, transform.position.z), 0.1f);

}