어김없이 AI 개발중..
시도
지금은 AI끼리 서로 길을 막거나 미는 현상을 수정중이다.
AI Object에 Collider와 Rigidbody(isKinematic => true)를 넣은 후
OnTriggerEnter로 일정 범위안에 다른 AI가 들어오면 AI를 멈추도록 구현해봤다.
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag("AI"))
{
if (_agent.destination == _tycoonManager.CustomerCreatePos.position)
{
StartCoroutine(StopFewSeconds());
}
}
}
...
IEnumerator StopFewSeconds()
{
_agent.isStopped = true;
_animator.SetBool("IsIdle", true);
yield return new WaitForSeconds(2f);
_agent.isStopped = false;
_animator.SetBool("IsIdle", false);
}
_tycoonManger.CustomerCreatePos.position에 도달하면 AI는 Destroy되어 식당을 나간걸로 간주된다.
부딪힌 AI가 둘 다 멈추면 안되고, 한 개의 AI만 멈춰야 하므로 AI 중에 _tycoonManager.CustomerCreatePos.position이 Destination인 AI만 멈추고, 식탁에 앉으러 가는 AI는 원래 가던 길 갈 수 있도록 코드를 짰다.
또, 자리에 앉으러 가는 AI의 우선순위가 식당을 나가는 AI보다 낮아서 식당을 나가는 AI를 피해갈 것이다.
하지만 해결되지 않았다.
Destination position의 y값이 다른 문제
Debug.Log로 _tycoonManager.CustomerCreatePos.position을 찍어봤다.
내가 CustomerCreatePos에 넣어준 값은 (0,0,-9.6) 인데, Destination은 (0, 0.04, -9.6) 이다.
이 현상은 NavMesh가 bake될 때 Surface로 지정된 GameObject에 딱 붙어서 bake되지 않고, 좀 떨어져서 bake되는 현상때문인 듯 하다. (추측)
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag("AI"))
{
if (_agent.destination.x == _tycoonManager.CustomerCreatePos.position.x
&& _agent.destination.z == _tycoonManager.CustomerCreatePos.position.z)
{
StartCoroutine(StopFewSeconds());
}
}
}
그래서 x, z값만 받아서 비교하도록 수정해줬다.
두 AI가 서로 멈추는 문제
구현하고 나니 내가 간과한 게 있었다.
두 AI 모두 식당을 나가는 AI라면 서로 멈춘다.
이 부분은 한 AI만 멈출 수 있도록 OnTriggerEnter에서 조건을 더 줘서 해결했다. (otherAgent.isStopped == false)
그리고 _agent.destination과 _tycoonManger.CustomerCreatePos.position의 값을 그냥 비교하면 오차로 작동이 이상해질 수 있으니 Approximately를 사용했다.
또 Agent의 isStopped을 사용하면 목적지는 있는 상태로 멈추기 때문에,
AI가 걷는 애니메이션을 실행하고 있는 상태로 멈춘다.
그래서 IsIdle 파라미터를 생성해 목적지로 가다 멈췄을 때, Idle 애니메이션으로 전환해줬다.
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag("AI"))
{
NavMeshAgent otherAgent = other.gameObject.GetComponent<NavMeshAgent>();
if (Mathf.Approximately(_agent.destination.x, _tycoonManager.CustomerCreatePos.position.x)
&& Mathf.Approximately(_agent.destination.z, _tycoonManager.CustomerCreatePos.position.z)
&& otherAgent.isStopped == false)
{
_agent.isStopped = true;
_animator.SetBool("IsIdle", true);
}
}
}
OnTrigger함수들을 사용하게 되면서, 멈춰서 몇 초 동안 기다리던 방식이 필요없어져
원래 사용하던 StopFewSeconds코루틴은 삭제했다.
-> 한 AI만 잘 멈춘다 !
또 한 개의 AI만 충돌할 거란 보장이 없으므로 현재 충돌중인 AI를 담을 List<GameObject> 변수를 생성했다.
private List<GameObject> collidingAIs = new();
OnTriggerEnter로 충돌이 시작되었을 때 List에 충돌된 AI를 넣어준다.
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag("AI"))
{
NavMeshAgent otherAgent = other.gameObject.GetComponent<NavMeshAgent>();
if (Mathf.Approximately(_agent.destination.x, _tycoonManager.CustomerCreatePos.position.x)
&& Mathf.Approximately(_agent.destination.z, _tycoonManager.CustomerCreatePos.position.z)
&& otherAgent.isStopped == false
&& !_collidingAIs.Contains(other.gameObject))
{
_collidingAIs.Add(other.gameObject);
_agent.isStopped = true;
_animator.SetBool("IsIdle", true);
}
}
}
그리고 OnTriggerExit에서 List에서 Remove 시켜줬다.
private void OnTriggerExit(Collider other)
{
if (other.gameObject.CompareTag("AI"))
{
RemoveList(other);
}
}
private void RemoveList(Collider other)
{
if (_collidingAIs.Contains(other.gameObject))
{
_collidingAIs.Remove(other.gameObject);
}
if (collidingAIs.Count == 0)
{
_agent.isStopped = false;
_animator.SetBool("IsIdle", false);
}
}
AI가 다른 AI가 움직일 때까지 기다리는 문제
이제는 충돌된 상태로 한 AI가 식탁에 앉게되면 나가던 AI가 다른 AI의 식사가 끝날 때까지 기다리는 현상이 생겼다.
- OnTriggerStay와 agent의 isStopped로 해결했다.
지금 충돌하고 있는 다른 AI가 만약 식탁에 앉는다면 그 AI의 Agent.isStopped를 true로 바꿔준다.
if (!_isOrderFood)
{
...
_agent.isStopped = true;
}
_isOrderFood: 식탁에 앉았는지(주문을 했는지) 확인해주는 bool값 변수
그리고 OnTriggerStay에서 이 값을 확인해 만약 다른 AI의 isStopped이 true일 경우 충돌하고 있는 AI들을 담고 있던 List인 _collidingAIs에서 그 AI Object를 삭제한다.
private void OnTriggerStay(Collider other)
{
if (other.gameObject.CompareTag("AI"))
{
NavMeshAgent otherAgent = other.gameObject.GetComponent<NavMeshAgent>();
if (otherAgent.isStopped == true)
{
RemoveList(other);
}
}
}
-> 해결 !
해결하고 나니 또 다른 문제가 생겼다.
AI의 transform이 바뀌는 문제
손님 AI들은 Object Pool로 생성, 제거되는데
풀에서 생성하고 처음 불러와서 사용할 때는 문제가 없지만, 두 번째부터는 Position과 Rotation값이 변경된다..
내 추측으로는
AI끼리 충돌되면서 아예 안 밀지는 않으니 살짝 밀릴 때 transform값이 바뀌든가,
움직이고 있는 상태에서 풀로 반환될 때, transform 값이 변환된 상태로 풀로 반환되어서 그런게 아닌가싶다.
결국엔 정확한 원인은 찾지 못해 일단 OnEnable에 transform값들을 고정시켜줬다.
transform.rotation = Quaternion.identity;
_animator.gameObject.transform.localPosition = Vector3.zero;
_animator.gameObject.transform.localRotation = Quaternion.identity;
-> 일단은 해결됐다.
현재는 잘 작동하지만 어째선지 ObjectPool에서 손님 AI를 두 번째로 불러왔을 때 식당을 나가지 않는 현상이 생겼다..
내일 고치는걸로 !
-> 빠른 테스트를 위해 손님 AI의 음식 기다리는 시간을 0으로 설정해서 일어난 문제.
Collider를 사용하지 않고 다른 AI를 체크하려고 Chat GPT의 힘을 빌려 시도해본 것들
//else
//{
// Collider[] nearbyAgents = Physics.OverlapSphere(transform.position, 0.5f);
// foreach (Collider agentCollider in nearbyAgents)
// {
// if (agentCollider.gameObject != gameObject && agentCollider.gameObject.tag == "AI")
// {
// Vector3 avoidanceDirection = transform.position - agentCollider.transform.position;
// Vector3 newDestination = transform.position + avoidanceDirection.normalized * 0.5f;
// StartCoroutine(StopFewSeconds(newDestination));
// }
// }
//}
//else
//{
// GameObject[] ais = GameObject.FindGameObjectsWithTag("AI");
// if (!isStop)
// {
// foreach (GameObject aiObject in ais)
// {
// if (aiObject == gameObject)
// continue;
// if (10f > Vector3.Distance(aiObject.transform.position, transform.position)
// && _agent.destination == _tycoonManager.CustomerCreatePos.position)
// {
// StartCoroutine(StopFewSeconds());
// }
// }
// }
//}
'내일배움캠프(Unity)' 카테고리의 다른 글
실전 프로젝트 중간발표 (0) | 2024.02.05 |
---|---|
TIL (0) | 2024.02.01 |
TIL - NavMeshAgent(Base Offset) (0) | 2024.01.25 |
TIL - 오류 수정 (0) | 2024.01.24 |
TIL - 조리된 음식 프리팹 만들기, 몇 가지 오류 (1) | 2024.01.23 |