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;
}
기능은 위와 같다. 리지드바디에도 있는지는 모르겠지만 CharacterController 컴포넌트는 Slope Limit가 장점인것 같다.
원래는 이정도 낙차에도 지나가지 못해 플레이어를 점프하게 만들었으나, Slope Limit값을 높게 조정하여 이정도 낙차는 올라갈 수 있게 만들 수 있다. Step Offset 으로도 비슷한 효과를 낼수 있다. 이건 낙차로 구분하는 것이 아닌 오브젝트의 지면이 입력값 범위에 있는지 판단하여 올라갈지를 판단하여 이것이 좀더 컨트롤 하기 쉬울 수 있으나 그냥 작은 낙차정도만 올라간다면 Slope Limit로 충분하다.
[로직]
때문에 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 아이템 내려놓기 애니메이션
점프하는 도중 벽에 부딫히면 점프력이 죽는 문제 해결하기