K-digital traning/Final Project

Gazzlers 개발일지 - Enemy 장애물 회피

내꺼블로그 2023. 12. 10. 00:22

일단 wheel collider를 떼기로 결심하였으니 wheel collider를 제거하고 기존 스크립트도 수정해보쟈.

 

 

WheelCollider Object를 제거

 

 

 

enemy에 부착된 RigidBody의 velocity와 rotation 값을 조절하는 방법으로 변경.

 

 

 

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;


    public class Enemy1Move : MonoBehaviour
    {
        private KeyValuePair<int, Transform> Target;
        private bool isReady;
        public float moveSpeed = 250f;
        public float rotSpeed = 250f;
        public float updateDirTime = 0.1f;
        public float changeTargetTime = 5f;
        public float offset = 0.3f;
        public System.Action<int> onChangeTarget;
        private Rigidbody rBody;

        private void FixedUpdate()
        {
            if (!this.isReady) return;
            this.rBody.velocity = this.transform.forward * moveSpeed * Time.deltaTime;
            Vector3 rotDir = this.Target.Value.position-this.transform.position;
            this.rBody.rotation = Quaternion.LookRotation(rotDir * this.rotSpeed*Time.deltaTime);
        }

        public void Init(Vector3 pos)
        {
            this.gameObject.SetActive(true);
            this.rBody = this.GetComponent<Rigidbody>();
            this.rBody.centerOfMass = Vector3.up * 0.3f;
            this.rBody.velocity = Vector3.back;
            this.transform.position = pos;
        }

        public void UpdateTargetPos(int idx, Transform targetTrans)
        {
            this.Target = new KeyValuePair<int, Transform>(idx, targetTrans);
            if (!isReady) { 
                isReady = true;
                this.StartCoroutine(this.CoChangeTarget());
            }
        }

        private IEnumerator CoChangeTarget()
        {
            while (true)
            {
                yield return new WaitForSeconds(this.changeTargetTime);
                this.onChangeTarget(this.Target.Key);
            }
        }

        private void OnDrawGizmos()
        {
            Gizmos.color = Color.red;
            Gizmos.DrawWireSphere(this.transform.position, 2f);
        }
    }

 

 

 

 

 

아직 구현하지 못한 부분 : 장애물 회피

고로 오늘은 요 아이를 어떻게든 구현해보려 한다.

 

 

※구현계획 : enemy의 앞방향으로 여러 개의 ray를 쏘고(이때 ray들은 각기 다른 방향으로 발사하여 반원 형태를 띄어야 한다.) ray에 감지되는 물체가 존재하면 감지되는 ray를 기준으로 어느 방향으로 이동할지 정하게 한다. 이 때 방향을 정하는 방법은 어느 방향이 enemy가 쫓을 target에 가까운지를 계산하여 더 가까운 쪽으로 가는 것이다.

 

 

 

 

 

1. enemy의 앞방향으로 여러 개의 ray를 쏘기

 

TestDetectObject 씬을 하나 새로 판 다음 간단하게 enemy를 큐브로 구현하고 바닥에 plane을 깐다.

 

 

 

 

 

 

 

큐브를 단순히 위로 올리면 Pivot의 위치값도 바뀌므로 Enemy라는 빈 오브젝트를 추가하여 Cube를 자식으로 부착한다.

 

 

 

 

 

 

for문에 따라 x값과 z값을 변경하는 형태로 구현해보았다.

현 enemy의 위치에서부터 x값으로 얼만큼, z값으로 얼만큼의 방향을 부여하여 enemy중심으로 발사된 ray가 반원형태를 이루게끔 작성해보았다.

 

 

 

 

 

단순히 x와 z값을 일정량만큼 더하고 빼는 형태로 구현하였더니 ray마다의 각도들이 일정치 않은 형태를 띄게 되었다.

각도가 일정치 않으므로 수정.

 

 

 

 

재귀를 사용하여 구현해보았다.

크기가 같은 두 벡터를 더한 벡터는 두 벡터를 이루는 각도의 딱 절반을 취하게 된다.

 

 

대충 요런 느낌

이걸 활용하여 두 각의 사이각을 구할 수 있도록 재귀로 두 벡터의 사이를 찍고, 두 벡터와 그 사이각을 또 각각 돌리는 형식으로 구현해보았다.

(일정치 이상(depth)이 되면 재귀X)

 

 

 

 

 

 

한결 깰꼼.

 

 

 

 

 

 

 

2. ray로 앞에 있는 물체 감지.

 

 

 

 

 

 

 

 

 

 

 

3. 감지된 ray 중 거리가 가장 짧은 값 찾기

 

 

 

 

 

 

 

 

 

 

결과

 

 

 

 

코드

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnemyDetect : MonoBehaviour
{
    private Vector3 offset = Vector3.up * 0.5f;
    public float length = 3f;
    public int depth = 3;
    private float minDis;

    void Update()
    {
        this.Detect();
    }

    private void Detect()
    {
        this.minDis = 1000000f;
        Vector3 pos1 = this.transform.right;
        Vector3 pos2 = -this.transform.right;
        this.DrawRay(pos1, this.transform.forward, 0);
        this.DrawRay(pos2, this.transform.forward, 0);
        Debug.LogFormat("<color=red>{0}</color>",minDis);
    }

    private void DrawRay(Vector3 pos1, Vector3 pos2, int depth)
    {
        RaycastHit hit;
        if (Physics.Raycast(this.transform.position + this.offset, pos1.normalized, out hit, this.length))
        {
            Debug.DrawRay(this.transform.position, pos1.normalized * this.length, Color.red);
            float dis = hit.distance;
            minDis = Mathf.Min(minDis, dis);
        }
        else
        {
            Debug.DrawRay(this.transform.position, pos1.normalized * this.length, Color.green);
        }
        if (Physics.Raycast(this.transform.position + this.offset, pos2.normalized, out hit, this.length))
        {
            Debug.DrawRay(this.transform.position, pos2.normalized * this.length, Color.red);
            float dis = hit.distance;
            minDis = Mathf.Min(minDis, dis);
        }
        else
        {
            Debug.DrawRay(this.transform.position, pos2.normalized * this.length, Color.green);
        }

        if (depth < this.depth)
        {
            this.DrawRay(pos1, (pos1 + pos2).normalized, depth + 1);
            this.DrawRay(pos2, (pos1 + pos2).normalized, depth + 1);
        }
    }
}

 

 

 

+ 가장 짧은 레이 구하기.

 

 

 

 

 

 

 

 

 

 

결과

 

 

코드

using System.Collections;
using System.Collections.Generic;
using UnityEngine;


    public class EnemyDetect : MonoBehaviour
    {
        private Vector3 offset = Vector3.up * 0.5f;
        public float length = 3f;
        public int depth = 3;
        private float minDis;
        private Ray minDisHitRay;

        void Update()
        {
            this.Detect();
        }

        private void Detect()
        {
            this.minDis = 1000000f;
            Vector3 pos1 = this.transform.right;
            Vector3 pos2 = -this.transform.right;
            this.DrawRay(pos1, this.transform.forward, 0);
            this.DrawRay(pos2, this.transform.forward, 0);
            Debug.LogFormat("<color=red>{0}</color>", this.minDis);
            if (this.minDis < 1000000f) Debug.LogFormat("<color=yellow>{0}: {1}</color>", this.minDisHitRay, this.minDisHitRay.direction);
        }

        private void DrawRay(Vector3 pos1, Vector3 pos2, int depth)
        {
            Ray ray = new Ray(this.transform.position + this.offset, pos1);
            RaycastHit hit;
            if (Physics.Raycast(ray, out hit, this.length))
            {
                Debug.DrawRay(this.transform.position, pos1.normalized * this.length, Color.red);
                float dis = hit.distance;
                this.minDis = Mathf.Min(minDis, dis);
                if (this.minDis == dis) minDisHitRay = ray;
            }
            else
            {
                Debug.DrawRay(this.transform.position, pos1.normalized * this.length, Color.green);
            }
            ray = new Ray(this.transform.position + this.offset, pos2);
            if (Physics.Raycast(ray, out hit, this.length))
            {
                Debug.DrawRay(this.transform.position, pos2.normalized * this.length, Color.red);
                float dis = hit.distance;
                minDis = Mathf.Min(minDis, dis);
                if (this.minDis == dis) minDisHitRay = ray;
            }
            else
            {
                Debug.DrawRay(this.transform.position, pos2.normalized * this.length, Color.green);
            }

            if (depth < this.depth)
            {
                this.DrawRay(pos1, (pos1 + pos2).normalized, depth + 1);
                this.DrawRay(pos2, (pos1 + pos2).normalized, depth + 1);
            }
        }
    }

 

 

 

 

 

 

 

 

 

 

+ 수정

같은 방향으로 ray를 쏘는 경우가 생기므로 코드 수정이 필요해보임.

 

 

 

 

 

 

 

 

 

코드

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

    public class EnemyDetect : MonoBehaviour
    {
        private Vector3 offset = Vector3.up * 0.5f;
        public float length = 3f;
        public int depth = 3;
        private float minDis;
        private Ray minDisHitRay;
        //private List<Ray> listHitRay;

        void Update()
        {
            this.Detect();
        }

        private void Detect()
        {
            this.minDis = 1000000f;
            Vector3[] pos = new Vector3[] {this.transform.right, -this.transform.right, this.transform.forward};
            for(int i=0;i<pos.Length; i++)
            {
                Ray ray = new Ray(this.transform.position + this.offset, pos[i]);
                DrawAndHitRay(ray);
            }
            for(int i = 0; i < 2; i++)
            {
                this.RecurDrawAndHitRay(pos[i], pos[2], 0);
            }
            Debug.LogFormat("<color=red>{0}</color>", this.minDis);
            if (this.minDis < 1000000f) Debug.LogFormat("<color=yellow>{0}: {1}</color>", this.minDisHitRay, this.minDisHitRay.direction);
        }

        private void RecurDrawAndHitRay(Vector3 pos1, Vector3 pos2, int depth)
        {
            Ray ray = new Ray(this.transform.position + this.offset, (pos1 + pos2).normalized);
            this.DrawAndHitRay(ray);
            if (depth < this.depth)
            {
                this.RecurDrawAndHitRay(pos1, (pos1 + pos2).normalized, depth + 1);
                this.RecurDrawAndHitRay(pos2, (pos1 + pos2).normalized, depth + 1);
            }
        }
        private void DrawAndHitRay(Ray ray)
        {
            RaycastHit hit;
            if(Physics.Raycast(ray, out hit, this.length))
            {
                Debug.DrawRay(ray.origin, ray.direction.normalized*this.length, Color.red);
                float dis = hit.distance;
                this.minDis = Mathf.Min(minDis, dis);
                if (this.minDis == dis) minDisHitRay = ray;
            }
            else
            {
                Debug.DrawRay(ray.origin, ray.direction.normalized * this.length, Color.green);
            }
        }
    }

 

 

 

 

 

4.  이동할 방향 구하기

hit을 감지하지 못한 레이들을 hit을 감지한 레이를 기준으로 집단으로 나눈다.

각각의 레이 집단 중에서 각도가 가장 넓은 곳으로 이동, 이 때 방향은 결정한 집단의 레이들의 평균값이 된다.

 

 

 

(1) hit을 감지한 레이와 감지하지 못한 레이 구분하기

 

 

 

 

 

 

 

 

 

순서가 뒤죽박죽이므로 정렬할 필요 ㅇㅇ

 

 

 

 

ray의 direction.x값 오름차순으로 정렬

 

 

 

 

 

 

 

 

 

 

코드

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;


    public class EnemyDetect : MonoBehaviour
    {
        private Vector3 offset = Vector3.up * 0.5f;
        public float length = 3f;
        public int depth = 3;
        private float minDis;
        private Ray minDisHitRay;
        private List<Ray> listHitRay = new List<Ray>();
        private List<Ray> listNotHitRay = new List<Ray>();
        private Dictionary<Ray, bool> listRays = new Dictionary<Ray, bool>();

        void Update()
        {
            this.Detect();
        }

        private void Detect()
        {
            //this.minDis = 1000000f;
            this.listHitRay.Clear();
            this.listNotHitRay.Clear();
            Vector3[] pos = new Vector3[] {this.transform.right, -this.transform.right, this.transform.forward};
            for(int i=0;i<pos.Length; i++)
            {
                Ray ray = new Ray(this.transform.position + this.offset, pos[i]);
                DrawAndHitRay(ray);
            }
            for(int i = 0; i < 2; i++)
            {
                this.RecurDrawAndHitRay(pos[i], pos[2], 0);
            }
            //Debug.LogFormat("<color=red>{0}</color>", this.minDis);
            //if (this.minDis < 1000000f) Debug.LogFormat("<color=yellow>{0}: {1}</color>", this.minDisHitRay, this.minDisHitRay.direction);
            List<Ray> newListHitRay = listHitRay.OrderBy(r=>r.direction.x).ToList();
            List<Ray> newListNotHitRay = listNotHitRay.OrderBy(r => r.direction.x).ToList();
            Debug.LogFormat("<color=yellow>hitRay count: {0}</color>", newListHitRay.Count);
            for (int i = 0; i < newListHitRay.Count; i++)
            {
                Debug.LogFormat("dir:{0}", newListHitRay[i].direction);
            }
            Debug.LogFormat("<color=blue>notHitRay count: {0}</color>", newListNotHitRay.Count);
            for (int i = 0; i < newListNotHitRay.Count; i++)
            {
                Debug.LogFormat("dir:{0}", newListNotHitRay[i].direction);
            }
        }

        private void RecurDrawAndHitRay(Vector3 pos1, Vector3 pos2, int depth)
        {
            Ray ray = new Ray(this.transform.position + this.offset, (pos1 + pos2).normalized);
            this.DrawAndHitRay(ray);
            if (depth < this.depth)
            {
                this.RecurDrawAndHitRay(pos1, (pos1 + pos2).normalized, depth + 1);
                this.RecurDrawAndHitRay(pos2, (pos1 + pos2).normalized, depth + 1);
            }
        }
        private void DrawAndHitRay(Ray ray)
        {
            RaycastHit hit;
            if(Physics.Raycast(ray, out hit, this.length))
            {
                Debug.DrawRay(ray.origin, ray.direction.normalized*this.length, Color.red);
                this.listHitRay.Add(ray);
                //float dis = hit.distance;
                //this.minDis = Mathf.Min(minDis, dis);
                //if (this.minDis == dis) minDisHitRay = ray;
            }
            else
            {
                Debug.DrawRay(ray.origin, ray.direction.normalized * this.length, Color.green);
                this.listNotHitRay.Add(ray);
            }
        }
    }

 

 

 

 

 

 

 

 

 

(2)hit을 감지하지 않은 ray들을 집단으로 나누기

 

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

    public class EnemyDetect : MonoBehaviour
    {
        private Vector3 offset = Vector3.up * 0.5f;
        public float length = 3f;
        public int depth = 3;
        //private float minDis;
        //private Ray minDisHitRay;
        //private List<Ray> listHitRay = new List<Ray>();
        //private List<Ray> listNotHitRay = new List<Ray>();
        private List<Tuple<Ray, bool>> listRays = new List<Tuple<Ray, bool>>();
        private List<List<Ray>> listNotHitRays = new List<List<Ray>>();

        void Update()
        {
            this.Detect();
        }

        private void Detect()
        {
            this.listRays.Clear();
            this.listNotHitRays.Clear();
            //this.minDis = 1000000f;
            //this.listHitRay.Clear();
            //this.listNotHitRay.Clear();
            Vector3[] pos = new Vector3[] { this.transform.right, -this.transform.right, this.transform.forward };
            for (int i = 0; i < pos.Length; i++)
            {
                Ray ray = new Ray(this.transform.position + this.offset, pos[i]);
                DrawAndHitRay(ray);
            }
            for (int i = 0; i < 2; i++)
            {
                this.RecurDrawAndHitRay(pos[i], pos[2], 0);
            }
            //Debug.LogFormat("<color=red>{0}</color>", this.minDis);
            //if (this.minDis < 1000000f) Debug.LogFormat("<color=yellow>{0}: {1}</color>", this.minDisHitRay, this.minDisHitRay.direction);
            //List<Ray> newListHitRay = listHitRay.OrderBy(r=>r.direction.x).ToList();
            //List<Ray> newListNotHitRay = listNotHitRay.OrderBy(r => r.direction.x).ToList();
            //Debug.LogFormat("<color=yellow>hitRay count: {0}</color>", newListHitRay.Count);
            //for (int i = 0; i < newListHitRay.Count; i++)
            //{
            //    Debug.LogFormat("dir:{0}", newListHitRay[i].direction);
            //}
            //Debug.LogFormat("<color=blue>notHitRay count: {0}</color>", newListNotHitRay.Count);
            //for (int i = 0; i < newListNotHitRay.Count; i++)
            //{
            //    Debug.LogFormat("dir:{0}", newListNotHitRay[i].direction);
            //}
            List<Tuple<Ray, bool>> newListRays = listRays.OrderBy(x => x.Item1.direction.x).ToList();
            for (int i = 0; i < newListRays.Count; i++)
            {
                Tuple<Ray, bool> tupleRay = newListRays[i];
                //Debug.LogFormat("dir: {0}, isHit: {1}", tupleRay.Item1.direction, tupleRay.Item2);

                if (!tupleRay.Item2)
                {
                    if (this.listNotHitRays.Count == 0)
                    {
                        this.listNotHitRays.Add(new List<Ray>());
                    }
                    this.listNotHitRays[this.listNotHitRays.Count - 1].Add(tupleRay.Item1);
                    if (i < newListRays.Count - 1 && newListRays[i + 1].Item2)
                    {
                        this.listNotHitRays.Add(new List<Ray>());
                    }
                }
            }
            int maxCount = -1;
            int maxCountIndex = 0;
            for (int i = 0; i < this.listNotHitRays.Count; i++)
            {
                if (this.listNotHitRays[i].Count > maxCount)
                {
                    maxCount = this.listNotHitRays[i].Count;
                    maxCountIndex = i;
                }
                Debug.LogFormat("<color=yellow>listNotHitRays[{0}] count: {1}</color>", i, this.listNotHitRays[i].Count);
                for (int j = 0; j < this.listNotHitRays[i].Count; j++)
                {
                    Debug.LogFormat("[{0}][{1}] dir: {2}", i, j, this.listNotHitRays[i][j].direction);
                }
            }

            Vector3 dir = Vector3.zero;
            for (int i = 0; i < this.listNotHitRays[maxCountIndex].Count; i++)
            {
                dir += this.listNotHitRays[maxCountIndex][i].direction;
            }
            dir /= maxCount;
            Debug.LogFormat("<color=cyan>dir: {0}</color>", dir);
            //Vector3 rotDir = dir - this.transform.position;
            this.transform.localRotation = Quaternion.LookRotation(dir);
        }

        private void RecurDrawAndHitRay(Vector3 pos1, Vector3 pos2, int depth)
        {
            Ray ray = new Ray(this.transform.position + this.offset, (pos1 + pos2).normalized);
            this.DrawAndHitRay(ray);
            if (depth < this.depth)
            {
                this.RecurDrawAndHitRay(pos1, (pos1 + pos2).normalized, depth + 1);
                this.RecurDrawAndHitRay(pos2, (pos1 + pos2).normalized, depth + 1);
            }
        }
        private void DrawAndHitRay(Ray ray)
        {
            RaycastHit hit;
            if (Physics.Raycast(ray, out hit, this.length))
            {
                Debug.DrawRay(ray.origin, ray.direction.normalized * this.length, Color.red);
                this.listRays.Add(new Tuple<Ray, bool>(ray, true));
                //this.listHitRay.Add(ray);
                //float dis = hit.distance;
                //this.minDis = Mathf.Min(minDis, dis);
                //if (this.minDis == dis) minDisHitRay = ray;
            }
            else
            {
                Debug.DrawRay(ray.origin, ray.direction.normalized * this.length, Color.green);
                this.listRays.Add(new Tuple<Ray, bool>(ray, false));
                //this.listNotHitRay.Add(ray);
            }
        }
    }

 

 

 

 

문제점 : 계속 업데이트를 돌리면서 detect하다보니 동시에 두 물체에 부딪힐 경우 와리가리 치는 경우 발생.

              부딪힌 ray의 끝 부분을 추가하지 않았다, lerp를 아직 쓰지 않았다. => 이 부분을 기준으로 다시 수정할 예정

              + enemy가 회전된 상태에서는 예상과 다른 값이 저장됨. 논리적 오류가 있는 거 같은데 그 이유를 모르겠,,,,,,,, 찾아봐야지,,,,,,,

 

 

 

 

 

이유를 알아냈다,,,,,,,,,

 

 

ray를 정렬할 때 direction.x값으로 오름차순해서 벌어진 일이었다.......

enemy가 rotation을 하게 되면 그에 따라 direction.x값도 바뀌게 되어 한 방향으로 순서대로 ray가 정렬되지 않게 되어버림...

(direction.x값만 작으면 앞순서로 들어가므로 this.transform.right 방향과 -this.transform.right방향이 차례로 들어오는 사태가 발생.)

 

고로,

요따구로 정렬하면 안되었던 것,,,,

 

 

이제 이걸 해결하면 그나마 수월할듯,,,,,,,, +굳이 offset을 둘 필요도 없을 것 같으니 제거.

 

 

 

ray의 direction과 this.transform.right의 값이 근접한 순으로 정렬.

모든 ray의 origin이 일치, direction은 length가 1이므로 this.transform.right과의 거리차로 비교해도 될 것이라 판단.

 

 

 

 

 

 

 

 

 

이제 된 거 같댜,,,,,,,,,,,,,,,,,,, 어휴

 

 

 

 

 

 

결과

 

 

 

 

5. Enemy 이동시키기

update에 요 코드 추가

 

 

 

 

 

 

코드

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;


    public class EnemyDetect : MonoBehaviour
    {
        [SerializeField] private Transform targetTrans;
        public float length = 1f;
        public int depth = 3;
        //private float minDis;
        //private Ray minDisHitRay;
        private List<Tuple<Ray, bool>> listRays = new List<Tuple<Ray, bool>>();

        void Update()
        {
            this.Detect();
        }

        private void Detect()
        {
            this.listRays.Clear();
            List<List<Ray>> listNotHitRays = new List<List<Ray>>();
            //this.minDis = 1000000f;
            Vector3[] pos = new Vector3[] { this.transform.right, -this.transform.right, this.transform.forward };
            for (int i = 0; i < pos.Length; i++)
            {
                Ray ray = new Ray(this.transform.position, pos[i]);
                DrawAndHitRay(ray);
            }
            for (int i = 0; i < 2; i++)
            {
                this.RecurDrawAndHitRay(pos[i], pos[2], 0);
            }
            //Debug.LogFormat("<color=red>{0}</color>", this.minDis);
            //if (this.minDis < 1000000f) Debug.LogFormat("<color=yellow>{0}: {1}</color>", this.minDisHitRay, this.minDisHitRay.direction);

            List<Tuple<Ray, bool>> newListRays = listRays.OrderBy(x => Vector3.Distance(x.Item1.direction, this.transform.right)).ToList();
            for (int i = 0; i < newListRays.Count; i++)
            {
                Tuple<Ray, bool> tupleRay = newListRays[i];

                if (!tupleRay.Item2)
                {
                    if (listNotHitRays.Count == 0)
                    {
                        listNotHitRays.Add(new List<Ray>());
                    }
                    listNotHitRays[listNotHitRays.Count - 1].Add(tupleRay.Item1);
                    if (i < newListRays.Count - 1 && newListRays[i + 1].Item2)
                    {
                        listNotHitRays.Add(new List<Ray>());
                    }
                }
            }


            int maxCount = 0;
            int maxCountIndex = 0;
            for (int i = 0; i < listNotHitRays.Count; i++)
            {
                if (listNotHitRays[i].Count > maxCount)
                {
                    maxCount = listNotHitRays[i].Count;
                    maxCountIndex = i;
                }
            }

            Vector3 dir = Vector3.zero;
            for (int i = 0; i < listNotHitRays[maxCountIndex].Count; i++)
            {
                dir += listNotHitRays[maxCountIndex][i].direction;
            }
            dir /= maxCount;
            this.transform.localRotation = Quaternion.Lerp(this.transform.localRotation, Quaternion.LookRotation(dir), 1f * Time.deltaTime);

            this.transform.Translate(Vector3.forward * Time.deltaTime);
        }

        private void RecurDrawAndHitRay(Vector3 pos1, Vector3 pos2, int depth)
        {
            Ray ray = new Ray(this.transform.position, (pos1 + pos2).normalized);
            this.DrawAndHitRay(ray);
            if (depth < this.depth)
            {
                this.RecurDrawAndHitRay(pos1, (pos1 + pos2).normalized, depth + 1);
                this.RecurDrawAndHitRay(pos2, (pos1 + pos2).normalized, depth + 1);
            }
        }
        private void DrawAndHitRay(Ray ray)
        {
            RaycastHit hit;
            if (Physics.Raycast(ray, out hit, this.length))
            {
                Debug.DrawRay(ray.origin, ray.direction.normalized * this.length, Color.red);
                this.listRays.Add(new Tuple<Ray, bool>(ray, true));
            }
            else
            {
                Debug.DrawRay(ray.origin, ray.direction.normalized * this.length, Color.green);
                this.listRays.Add(new Tuple<Ray, bool>(ray, false));
            }
        }
    }

 

 

 

 

 

 

6.타겟 설정하기

얼추 구현되어가는 중 룰루

하지만 여기서 끝내면 X, enemy는 player를 쫓아야 하는데 지금 현 상황에서는 enemy가 단순히 앞에 있는 물체를 피하면서 자신의 앞방향으로 이동하기만 함.

 

 

 

요런 상황도 발생하게 된댜,,,,,

따라서 target도 따로 지정해놓은 상태에서 enemy가 이동방향을 결정하도록 해야 함

 

 

 

구현계획

1. 물체를 감지한 레이가 있는 경우와 없는 경우가 존재.

2. 물체를 감지한 레이가 없을 경우에는 타겟을 향해 이동.

3. 물체를 감지한 레이가 존재할 경우 이동할 수 있는 방향을 찾아 rotation

 

 

 

 

 

 

 

 

 

 

 

코드

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;


    public class EnemyDetect : MonoBehaviour
    {
        [SerializeField] private Transform targetTrans;
        public float offset = 0.5f;
        public float length = 3.5f;
        public int depth = 3;
        public int leastRayCount = 7;
        public float moveSpeed = 2f;
        public float rotSpeed = 2f;
        //private float minDis;
        //private Ray minDisHitRay;
        private List<Tuple<Ray, bool>> listRays = new List<Tuple<Ray, bool>>();
        private List<GameObject> listDetectedObjects = new List<GameObject>();
        private Vector3 dir;
        private bool isDetected;
        private int listDetectedObjectsCount;

        void Update()
        {
            this.Detect();
            this.DecideDir();
            this.Move();
        }

        private void Detect()
        {
            this.listRays.Clear();
            this.isDetected = false;
            this.listDetectedObjectsCount = this.listDetectedObjects.Count;
            //this.minDis = 1000000f;
            Vector3[] pos = new Vector3[] { this.transform.right, -this.transform.right, this.transform.forward };
            for (int i = 0; i < pos.Length; i++)
            {
                Ray ray = new Ray(this.transform.position+Vector3.up*this.offset, pos[i]);
                DrawAndHitRay(ray);
            }
            for (int i = 0; i < 2; i++)
            {
                this.RecurDrawAndHitRay(pos[i], pos[2], 0);
            }
        }

        private void DecideDir()
        {
            List<List<Ray>> listNotHitRays = new List<List<Ray>>();
            List<Tuple<Ray, bool>> newListRays = listRays.OrderBy(x => Vector3.Distance(x.Item1.direction, this.transform.right)).ToList();
            for (int i = 0; i < newListRays.Count; i++)
            {
                Tuple<Ray, bool> tupleRay = newListRays[i];

                if (!tupleRay.Item2)
                {
                    if (listNotHitRays.Count == 0)
                    {
                        listNotHitRays.Add(new List<Ray>());
                    }
                    listNotHitRays[listNotHitRays.Count - 1].Add(tupleRay.Item1);
                    if (i < newListRays.Count - 1 && newListRays[i + 1].Item2)
                    {
                        listNotHitRays.Add(new List<Ray>());
                    }
                }
            }
            if (this.isDetected)
            {
                Debug.LogFormat("{0}, {1}", this.listDetectedObjects.Count, this.listDetectedObjectsCount);
                if (this.listDetectedObjects.Count > this.listDetectedObjectsCount)
                {
                    
                    int maxCount = 0;
                    int maxCountIndex = 0;
                    for (int i = 0; i < listNotHitRays.Count; i++)
                    {
                        if (listNotHitRays[i].Count > maxCount)
                        {
                            maxCount = listNotHitRays[i].Count;
                            maxCountIndex = i;
                        }
                    }



                    this.dir = Vector3.zero;
                    for (int i = 0; i < listNotHitRays[maxCountIndex].Count; i++)
                    {
                        this.dir += listNotHitRays[maxCountIndex][i].direction;
                    }
                    this.dir /= maxCount;
                }

                this.transform.localRotation = Quaternion.Lerp(this.transform.localRotation, Quaternion.LookRotation(this.dir), this.rotSpeed * Time.deltaTime);
            }
            else
            {
                this.dir = this.targetTrans.position - this.transform.position;
                this.transform.rotation = Quaternion.Lerp(this.transform.rotation, Quaternion.LookRotation(this.dir), this.rotSpeed*0.3f*Time.deltaTime);
            }
        }

        private void Move()
        {
            this.transform.Translate(Vector3.forward * Time.deltaTime*this.moveSpeed);
        }

        private void RecurDrawAndHitRay(Vector3 pos1, Vector3 pos2, int depth)
        {
            Ray ray = new Ray(this.transform.position+Vector3.up*this.offset, (pos1 + pos2).normalized);
            this.DrawAndHitRay(ray);
            if (depth < this.depth)
            {
                this.RecurDrawAndHitRay(pos1, (pos1 + pos2).normalized, depth + 1);
                this.RecurDrawAndHitRay(pos2, (pos1 + pos2).normalized, depth + 1);
            }
        }
        private void DrawAndHitRay(Ray ray)
        {
            RaycastHit hit;
            if (Physics.Raycast(ray, out hit, this.length))
            {
                this.isDetected = true;
                Debug.DrawRay(ray.origin, ray.direction.normalized * this.length, Color.red);
                this.listRays.Add(new Tuple<Ray, bool>(ray, true));
                bool isDetectedObject = false;
                Debug.LogFormat("hit: {0}", hit.collider.gameObject);
                for(int i = 0; i < this.listDetectedObjects.Count; i++)
                {
                    if(hit.collider.gameObject == listDetectedObjects[i])
                    {
                        isDetectedObject = true;
                        break;
                    }
                }
                if (!isDetectedObject)
                {
                    listDetectedObjects.Add(hit.collider.gameObject);
                    Debug.LogFormat("listDetectedObjects count: {0}", this.listDetectedObjects.Count);
                }
            }
            else
            {
                Debug.DrawRay(ray.origin, ray.direction.normalized * this.length, Color.green);
                this.listRays.Add(new Tuple<Ray, bool>(ray, false));
            }
        }
    }

 

 

 

 

 

 

합친 씬

 

 

 

 

 

코드

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Unity.Collections;
using UnityEngine;


    public class Enemy1Move : MonoBehaviour
    {
        private KeyValuePair<int, Transform> target;
        private bool isReady;
        public float moveSpeed = 5.5f;
        public float rotSpeed = 8f;
        public float changeTargetTime = 5f;

        public float trackingTargetTime = 2f;
        private float elapsedTime = 3f;

        public float offset = 0.5f;
        public float length = 3.5f;
        public int depth = 3;
        public Action<int> onChangeTarget;
        private Rigidbody rBody;
        private List<Tuple<Ray, bool>> listRays = new List<Tuple<Ray, bool>>();
        private Vector3 dir;
        private bool isDetected;

        private void FixedUpdate()
        {
            if (!this.isReady) return;
            this.Detect();
            this.DecideDir();
            this.Move();
        }

        public void Start()
        {
            this.rBody = this.GetComponent<Rigidbody>();
        }

        public void Init(Vector3 pos)
        {
            this.transform.position = pos;
            this.gameObject.SetActive(true);
        }

        private void Detect()
        {
            this.listRays.Clear();
            this.isDetected = false;
            Vector3[] pos = new Vector3[] { this.transform.right, -this.transform.right, this.transform.forward };
            for (int i = 0; i < pos.Length; i++)
            {
                Ray ray = new Ray(this.transform.position + Vector3.up * this.offset, pos[i]);
                DrawAndHitRay(ray);
            }
            for (int i = 0; i < 2; i++)
            {
                this.RecurDrawAndHitRay(pos[i], pos[2], 0);
            }
        }
        private void Move()
        {
            float curSpeed = this.moveSpeed;
            float posZ = this.transform.position.z;
            float targetPosZ = this.target.Value.position.z;
            if (posZ > targetPosZ + 15)
            {
                curSpeed = this.moveSpeed * 1.5f;
            }
            else if (posZ > targetPosZ + 7)
            {
                curSpeed = this.moveSpeed;
            }
            else if (posZ > targetPosZ + 3)
            {
                curSpeed = this.moveSpeed * 0.9f;
            }
            if (this.isDetected) curSpeed *= 0.8f;
            this.rBody.velocity = this.transform.forward * curSpeed;
        }


        private void DecideDir()
        {
            List<List<Ray>> listNotHitRays = new List<List<Ray>>();
            List<Tuple<Ray, bool>> newListRays = listRays.OrderBy(x => Vector3.Distance(x.Item1.direction, this.transform.right)).ToList();
            for (int i = 0; i < newListRays.Count; i++)
            {
                Tuple<Ray, bool> tupleRay = newListRays[i];

                if (!tupleRay.Item2)
                {
                    if (listNotHitRays.Count == 0)
                    {
                        listNotHitRays.Add(new List<Ray>());
                    }
                    listNotHitRays[listNotHitRays.Count - 1].Add(tupleRay.Item1);
                    if (i < newListRays.Count - 1 && newListRays[i + 1].Item2)
                    {
                        listNotHitRays.Add(new List<Ray>());
                    }
                }
            }
            if (this.isDetected)
            {
                this.elapsedTime = 0f;

                int maxCount = 0;
                int maxCountIndex = 0;
                for (int i = 0; i < listNotHitRays.Count; i++)
                {
                    if (listNotHitRays[i].Count > maxCount)
                    {
                        maxCount = listNotHitRays[i].Count;
                        maxCountIndex = i;
                    }
                }



                this.dir = Vector3.zero;
                for (int i = 0; i < listNotHitRays[maxCountIndex].Count; i++)
                {
                    this.dir += listNotHitRays[maxCountIndex][i].direction;
                }
                this.dir /= maxCount;
                this.transform.rotation = Quaternion.Lerp(this.transform.localRotation, Quaternion.LookRotation(this.dir), this.rotSpeed * Time.deltaTime);
            }
            else
            {
                this.elapsedTime += Time.deltaTime;
                if (this.elapsedTime > this.trackingTargetTime)
                {
                    this.dir = this.target.Value.position - this.transform.position;
                    //Debug.LogFormat("<color=red>{0}</color>", Quaternion.LookRotation(this.dir));
                    this.transform.rotation = Quaternion.Lerp(this.transform.rotation, Quaternion.LookRotation(this.dir), this.rotSpeed * Time.deltaTime * 0.6f);
                }
                else
                    this.transform.rotation = Quaternion.Lerp(this.transform.localRotation, Quaternion.LookRotation(this.dir), this.rotSpeed * Time.deltaTime);
            }
        }

        private void RecurDrawAndHitRay(Vector3 pos1, Vector3 pos2, int depth)
        {
            Ray ray = new Ray(this.transform.position + Vector3.up * this.offset, (pos1 + pos2).normalized);
            this.DrawAndHitRay(ray);
            if (depth < this.depth)
            {
                this.RecurDrawAndHitRay(pos1, (pos1 + pos2).normalized, depth + 1);
                this.RecurDrawAndHitRay(pos2, (pos1 + pos2).normalized, depth + 1);
            }
        }
        private void DrawAndHitRay(Ray ray)
        {
            int layerMask = 1 << 3 | 1 << 6 | 1 << 7;
            RaycastHit hit;
            if (Physics.Raycast(ray, out hit, this.length, layerMask))
            {
                this.isDetected = true;
                Debug.DrawRay(ray.origin, ray.direction.normalized * this.length, Color.red);
                this.listRays.Add(new Tuple<Ray, bool>(ray, true));
                Debug.LogFormat("<color=blue>hit</color>: {0}", hit.collider.gameObject);
            }
            else
            {
                Debug.DrawRay(ray.origin, ray.direction.normalized * this.length, Color.green);
                this.listRays.Add(new Tuple<Ray, bool>(ray, false));
            }
        }

        public void UpdateTargetPos(int idx, Transform targetTrans)
        {
            this.target = new KeyValuePair<int, Transform>(idx, targetTrans);
            if (!isReady)
            {
                isReady = true;
                this.StartCoroutine(this.CoChangeTarget());
            }
        }

        private IEnumerator CoChangeTarget()
        {
            while (true)
            {
                yield return new WaitForSeconds(this.changeTargetTime);
                this.onChangeTarget(this.target.Key);
            }
        }

        private void OnDrawGizmos()
        {
            Gizmos.color = Color.red;
            Gizmos.DrawWireSphere(this.transform.position, 2f);
        }
    }