AI 이동을 좀 더 쓸만하도록 수정하겠습니다.
AI 가 이동할떄 한번에 끝까지 이동하며 도중에 다른 판단을 하지 않게되면 매우 어색하기 때문에 중간중간 갈 위치를 정해두고 매번 다시 평가하는 구조로 만들었습니다.
NodeCanvas의 MoveToward의 경우 A포인트에서 B포인트까지 한번에 일정속도로 이동하게 되는데 이떄 시간이 3초 걸린다면 3초동안 다른 평가를 하지 않기 때문에 이상하게 됩니다.
이럴경우 0.5초 로 이동할 수 있는 거리만큼 거리를 계산하고 그만큼만 이동하고 다시 평가를 진행합니다.
우선 기존에 진행 하였던 것들에 이어서 진행하지만 많은 변경점이 있어서 전체적으로 다시 적었습니다.
CubeController.cs
public class CubeController : MonoBehaviour
{
private GameObject currentTarget;
private Vector3 currentDestination;
private float speed = 3f;
private float stepTime = 0.5f;
public bool HasTarget;
public GameObject CurrentTarget { get => currentTarget; set => currentTarget = value; }
public Vector3 CurrentDestination { get => currentDestination; set => currentDestination = value; }
public float Speed { get => speed; set => speed = value; }
public float StepTime { get => stepTime; set => stepTime = value; }
public void SetTarget(GameObject target)
{
CurrentTarget = target;
HasTarget = (target != null);
}
}
CubeController 는 get / set 으로 모두 감싸놨습니다.
Unity 의 고질적인 문제인 public 일 경우 변수가 Inspector에 뜨면서 값이 안바뀌는 문제가 있어서 그냥 속편하게 get/set 처리 했습니다.
speed 는 속도로 1초에 갈수 있는 유닛(유니티의 단위)으로 생각하였습니다
stepTime은 다시 계산하는 시간 단위입니다. 0.5라면 0.5초마다 다시 계싼하는 것 입니다.
SetTarget()이라는 메소드를 추가하여 타겟이 있는지 판안 여부 까지 추가 되었습니다.
CheckObject.cs
private CubeController cubeController;
protected override string OnInit(){
cubeController = agent.GetComponent<CubeController>();
return null;
}
protected override void OnExecute(){
int max = 1;
Collider[] results = new Collider[max];
int resultNum = Physics.OverlapSphereNonAlloc(agent.transform.position, 10.0f, results, LayerMask.GetMask("Target"));
for(int i=0; i< resultNum; ++i)
{
cubeController.SetTarget(results[i].gameObject);
}
EndAction(true);
}
NodeCanvas Action으로 기존에서 거의 변경되지 않았지만 CubeController 의 CurrentTarget 할당하는 부분을 바로 할당하지 않고 SetTarget을 호출하도록 변경 되었습니다.
MoveDestination.cs
private CubeController cubeController;
private float stepDistance;
protected override string OnInit(){
cubeController = agent.GetComponent<CubeController>();
return null;
}
protected override void OnExecute(){
if(cubeController.HasTarget)
{
float distance = Vector3.Distance(agent.transform.position, cubeController.CurrentTarget.transform.position);
stepDistance = cubeController.StepTime * cubeController.Speed;
if(distance > stepDistance)
{
cubeController.CurrentDestination = agent.transform.position + ((cubeController.CurrentTarget.transform.position - agent.transform.position).normalized * stepDistance);
}
else
{
EndAction(true);
}
}
else
{
EndAction(true);
}
}
protected override void OnUpdate()
{
float tempA = (agent.transform.position - cubeController.CurrentDestination).magnitude;
if (tempA <= 0.1f)
{
Debug.Log("Finish Move");
EndAction();
}
else
{
agent.transform.position = Vector3.MoveTowards(agent.transform.position, cubeController.CurrentDestination, cubeController.Speed * Time.deltaTime);
}
}
새로 추가된 NodeCanvas Action입니다.
cubeController 얻는 법은 기존과 동일 합니다.
한틱에 실행이 끝나는 부분이 아니기 떄문에 OnExecute() 에서 EndAction()을 매번 호출하지 않고 OnUpdate()에서 EndAction()을 호출 해 주는 경우가 있습니다.
OnExecute()에서 stepDistance를 구하는데 1번의 계산으로 갈 수 있는 거리 입니다.
Speed는 1초동안 갈수 있는 거리고 StepTime은 계산하는 시간 단위 이기 때문에 이것으로 1계산으로 갈수 있는 거리를 얻을 수 있습니다.
실제 거리가 stepDistance가 보다 긴 경우는 계산으로 이동해도 되는 경우이고 stepDistance가 더 긴경우는 1번의 계산으로 이동하면 넘어가 버리는 경우라서 이동을 하지 않았습니다.
이 경우 추가적인 작업을 하지 않았지만 계산 시간을 줄이거나 하는 방법으로 보완을 해도 좋을 것 같습니다.
OnExecute() 에서 CurrentDestination을 구했다면 OnUpdate()에서 실질적인 이동을 처리하고 있습니다.
그리고 거리가 0.1f이하라면 EndAction()을 호출 해 줍니다.
실행을 해 본다면 잘 동작하는 것을 볼 수 있습니다.
물론 시간이 100%일치하지는 않을것 이지만 대충 이러한 느낌으로 발전하면 좀 더 자연스러운 AI를 만들 수 있을 것 입니다.
'Unity Engine' 카테고리의 다른 글
[Unity]CollisionFlag - CharacterController (0) | 2022.11.06 |
---|---|
[Unity]Physics 와 Layer (0) | 2022.11.01 |
Behavior Tree - Custom Action (0) | 2022.07.07 |
Behavior Tree - Node Canvas (2) (0) | 2022.07.02 |
Behavior Tree - Node Canvas (1) (0) | 2022.06.29 |