ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 가까운 적 찾기
    유니티/유니티 메인 2024. 2. 28. 00:51

    오늘은 키보드 버튼에 따라  3D 오브젝트를 이동시키는 스크립트

    사정거리 안에 들어오면 적을 감지하는 스크립트

    여러 적이 있을때 가장 가까이 있는 적을 출력하는 스크립트 크게 총 3가지의 중요 스크립트를 구현해보겠다

     

    우선 각각 Player와 Monster의 이름으로 빈공간에 각각 맞는 프리펩을 넣어준다

    이번에는 상속을 이용하기보다는 여러개의 컴포넌트를 용도에 따라 분류하여 사용하도록 미리 만들어주었다

     

    우선 키보드를 눌렀을때 인식해줄 CInput 스크립트에 코드를 넣어주자

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class CInput : MonoBehaviour
    {
        public Vector3 dir;
        void Update()
        {
            //키보드의 입력을 받는다
            float h = Input.GetAxisRaw("Horizontal");
            float v = Input.GetAxisRaw("Vertical");
            dir = new Vector3(h, 0, v);
            Debug.Log(dir);
        }
    }

    이런 키보드 입력으로 이동시키기 좋은 GetAxisRaw 함수를 사용한다

     

    여기서 주의할건 Horizontal, Vertical 철자 틀리지 않게 조심하자. 아까전에 한글자 틀려서 한참 해맸다

     

            float h = Input.GetAxisRaw("Horizontal");

            float v = Input.GetAxisRaw("Vertical");

    float 형식의 변수 h와 v에는 각각 수평, 수직으로 이동하는 값을 받게 된다

     

            dir = new Vector3(h, 0, v);

            Debug.Log(dir);

     

    이제 Vector3 형식의 변수 dir에 값을 넣어준뒤 출력한다

    여기서 선언을 Update안에서 해주지 않게 밖으로 빼준 이유는

    다른 스크립트에서 dir의 값이 필요할때 사용할 수 있도록 위함이다

     

    이제 CInput 스크립트를 키보드 입력을 받아 움직일 Player에게 적용시킨뒤 실행을 해보자

     

     

    Update에 넣어줬으니 프레임마다 실시간으로 dir의 값이 출력되는것을 볼 수 있다

     

    이제 CInput에 입력받은 값으로 Player를 움직이는 코드를 CMove안에 넣어보자

     

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.Windows;
    
    public class CMove : MonoBehaviour
    {
        public void Update()
        {
            CInput cinput = GetComponent<CInput>();
            transform.Translate(cinput.dir*Time.deltaTime);
        }
    }

     

     

            CInput cinput = GetComponent<CInput>();

    우선 CInput 스크립트에 있는 dir변수를 가져와야 하니 컴포넌트를 불러오자

     

            transform.Translate(cinput.dir*Time.deltaTime);

    이건 간단하다 transform.Translate에 Vector3값의 dir을 넣어주기만 하면 된다

     

    여기서 한번더 짚고 넘어가 Time.deltaTime을 곱해주는 이유는 

    초당 호출되는 프레임이 디바이스마다 다르기 때문에 시간에 따라 동일한 거리를 움직이게 하기 위함이다

     

    키보드에 입력하는대로 캐릭터가 움직이는것을 확인할 수 있다

     

    이번엔 몬스터 주변반경에 원을 CSightVisualizer 스크립트에서 만들어보자

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class CSightVisualizer : MonoBehaviour
    {
        public float radius = 3;
        private void OnDrawGizmos()
        {
            GizmosExtensions.DrawWireArc(this.transform.position, this.transform.forward, 360, radius);
        }
    }

    우선 위 코드를 짜기전 3개의 스크립트가 미리 준비되어 있어야한다

    정확히 다 이해할순 없지만 360가 반경각도, radius로 집어넣은게 반경길이라는것 정도만 알아두자

    radius 변수도 굳이 밖에 빼준 이유는 후에 다른 스크립트에서 사용할 예정이기 때문

     

    근데 여기서 문제가 생겼다. 분명 스크립트를 플레이어에게 입혔는데 원이 전혀 뜬금없는곳에 생성되었다

    아까 했을땐 제대로 나왔는데 뭐가 문제인지 모르겠다. 고쳐진다면 추후에 수정하기로 하겠다

    사실 radius의 값을 받는것만 제외하면 그냥 비쥬얼적인 부분이기 때문에 크게 중요하진 않긴 하다

     

    이제 반경안에 들어오는순간 반경안에 들어왔다는 메세지를 출력하는 스크립트를 CDetect에 써준뒤 Monster안에 넣어주자

     

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class CDetect : MonoBehaviour
    {
        private GameObject playerGo;
        private CSightVisualizer sightVisualizer;
        private void Start()
        {
            playerGo = GameObject.Find("Player");
            sightVisualizer = GetComponent<CSightVisualizer>();
        }
    
        void Update()
        {
            //첫번째 방법
            //Vector3 c = playerGo.transform.position - this.transform.position;
            //float distance = c.magnitude;
            
            //두번째 방법
            float distance = Vector3.Distance(playerGo.transform.position , this.transform.position);
    
            if (sightVisualizer.radius >= distance)
            {
                Debug.Log("시야에 들어옴");
            }
            else 
            {
                Debug.Log("시야에서 벗어남");
            }
        }
    }

     

    우선 radius의 값과 Player를 찾기위한 코드를 작성해준다

     

    public class CDetect : MonoBehaviour
    {
        private GameObject playerGo;
        private CSightVisualizer sightVisualizer;
        private void Start()
        {
            playerGo = GameObject.Find("Player");
            sightVisualizer = GetComponent<CSightVisualizer>();
        }

     

     

     

     이제 Player와의 위치와 Monster 위치 사이의 거리를 구해야하는데 방법은 총 2가지이다

     

     1.       Vector3 c = playerGo.transform.position - this.transform.position;
               float distance = c.magnitude;
         

    2.       float distance = Vector3.Distance(playerGo.transform.position, this.transform.position);

     

    그리고 만약 Monster가 감지하는 길이가 둘 사이의 길이보다 길다면 반경안에 들어왔다는 의미이므로

    "시야에 들어옴" 출력, 반대의 경우에는 "시야에서 벗어남"을 출력한다

     

     if (sightVisualizer.radius >= distance)
            {
                Debug.Log("시야에 들어옴");
            }
            else
            {
                Debug.Log("시야에서 벗어남");
            }

    위치에 따라 출력하는 지문이 달라지는것을 확인할 수 있다

     

    이번엔 CMove를 상속받아 각각 PlayerMove와 MonsterMove를 구현해보겠다

     

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.Windows;
    
    public class CMove : MonoBehaviour
    {
        protected Vector3 dir;
    
        void Update()
        {
            //이동하다
            Debug.LogFormat("{0}으로 이동 합니다.", dir);
            //방향 * 속도 * 시간 
            this.transform.Translate(dir * 1 * Time.deltaTime);
        }
    }

     

    우선 CMove에서  

     

    public class CMove : MonoBehaviour
    {
        CInput cInput;

        private void Start()
        {
            cInput = GetComponent<CInput>();
        }

     

    이 부분을 전부 지워버리고

     

    protected Vector3 dir

     

    로 대체한다. 뒤에 나오겠지만 PlayerMove, MonsterMove 각각 CMove의 상속을 받아올 예정이다

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class CPlayerMove : CMove
    {
        private void Start()
        {
            dir = GetComponent<CInput>().dir;
        }
    }

    먼저 CPlayerMove이다

     

    CMove에서 상속을 받고, 변수 dir은 CInput의 dir의 값 받는다

     

    참고로 CInput의 dir은 

    dir = new Vector3(h, 0, v);

    사용자가 직접 키보드로 누른 값이다

     

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class CMonsterMove : CMove
    {
        private void Start()
        {
            dir = GetComponent<CDetect>().dir;
        }
    }

    CDetect이다. 마찬가지로 CMove의 상속을 받고

    CDetect의 dir값을 받아오지만 아직 CDetect안에서 가져올 dir이라는 변수가 없다

     

    그러니 CDetect 스크립트를 수정해주자

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class CDetect : MonoBehaviour
    {
        private GameObject playerGo;
        private CSightVisualizer sightVisualizer;
        public Vector3 dir;
    
        private void Start()
        {
            playerGo = GameObject.Find("Player");
            sightVisualizer = GetComponent<CSightVisualizer>();
        }
    
        void Update()
        {
            //플레이어와 나와의 거리를 계산 (벡터의 뺄셈 : 방향과 거리)
            //Vector3 c = playerGo.transform.position - this.transform.position;
            //float distance = c.magnitude;
            float distance = Vector3.Distance(playerGo.transform.position , this.transform.position);
    
            if (sightVisualizer.radius >= distance)
            {
                Debug.Log("시야에 들어옴");
                Vector3 c = playerGo.transform.position - this.transform.position;
                dir = c.normalized;
            }
            else 
            {
                Debug.Log("시야에서 벗어남");
                dir = Vector3.zero; 
            }
        }
    }

    수정된 부분은 크게 3가지이다

     

    1. CMonsterMove에 쓰일 dir변수 선언

        public Vector3 dir;

     

    2. 반경안에 들어온다면 변수 c안에 Player와 Monster의 거리를 정규화하여 dir안에 넣어줌

     if (sightVisualizer.radius >= distance)
            {
                Debug.Log("시야에 들어옴");
                Vector3 c = playerGo.transform.position - this.transform.position;
                dir = c.normalized;
            }

     

    3. 반경밖으로 나갈경우 변수 dir의 값을 0으로 초기화한다 (Vector3.zero = Vector3(0,0,0))

    else 
            {
                Debug.Log("시야에서 벗어남");
                dir = Vector3.zero; 
            }

     

    이제 CPlayerMove와 CMonsterMove를 보완해보겠다

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.Windows;
    
    public class CMove : MonoBehaviour
    {
        public void Move(Vector3 dir)
        {
            this.transform.Translate(dir * 1 * Time.deltaTime);
        }
    }

    수정한점은 2가지

    1. 따로 선언해준 Vector3 dir변수를 없애고

    2. Update 대신 Move라는 메서드를 따로 만들어 dir을 입력받으면 이동하게끔 수정한다

     

    또 이렇게 되면  CMove를 상속받아 그안에 dir을 쓰는 CPlayerMove와 CMonsterMove에 에러가 생길수밖에 없다

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class CPlayerMove : CMove
    {
        private CInput cInput;
        private void Start()
        {
            cInput = GetComponent<CInput>();
        }
    
        private void Update()
        {
            Debug.Log("CPlayerMove의 Update: " + cInput.dir);
            Move(cInput.dir);
        }
    }
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class CMonsterMove : CMove
    {
        private CDetect cDetect;
        private void Start()
        {
            cDetect = GetComponent<CDetect>();
        }
    }

    그러니 둘다 수정해주자

    기존에는 dir을 받아와서 사용했다면 이번엔 둘다 각각 변수를 가져와야하는 컴포넌트를 불러온다

     

     

    마지막으로 가장 가까이에 있는 Monster를 출력해주는 PlayerController 스크립트를 만들자

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class PlayerController : MonoBehaviour
    {
        public MonsterController[] monsters;
    
        void Start()
        {
            
        }
    
        // Update is called once per frame
        void Update()
        {
            
        }
    }

    우선 이렇게만 코드를 짜 Player안에 넣어주면

    Player의 Inspector창에 monsters를 넣을수 있는 창이 뜬다. Monster를 3개 더 복사한뒤 넣어주자

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class PlayerController : MonoBehaviour
    {
        public MonsterController[] monsters;
    
        void Start()
        {
            List<MonsterController> list = new List<MonsterController>();
    
            //배열요소 출력 
            for (int i = 0; i < monsters.Length; i++) {
                //배열요소 접근 
                MonsterController controller = monsters[i];
                //나와 거리계산 
                float distance = Vector3.Distance(this.transform.position, controller.transform.position);
                if (distance <= 8) {
                    //Debug.LogFormat("{0} -> {1}", controller.gameObject.name, distance);
                    list.Add(controller);
                }
            }
    
    
            for (int i = 0; i < list.Count; i++) {
                MonsterController controller = list[i];
                float distance = Vector3.Distance(this.transform.position, controller.transform.position);
                Debug.LogFormat("<color=cyan>{0}</color> : {1}", controller.gameObject.name, distance);
            }
    
            float nearestDistance = Mathf.Infinity;
            MonsterController nearest = null;
    
            for (int i = 0; i < list.Count; i++)
            {
                MonsterController controller = list[i];
                float distance = Vector3.Distance(this.transform.position, controller.transform.position);
                if (distance < nearestDistance) {
                    nearest = controller;
                    nearestDistance = distance;
                }
            }
    
            Debug.LogFormat("<color=yellow>{0}</color> : {1}", nearest.gameObject.name, nearestDistance);
        }

    마지막으로 코딩을 작성해주자

     

     

    '유니티 > 유니티 메인' 카테고리의 다른 글

    충돌판정  (1) 2024.03.04
    직렬화를 이용하여 저장 객체 저장  (1) 2024.02.15
    json 응용  (0) 2024.02.14
    레벨 디자인, Debug.DrawRay, RayCast, RayCastHit  (0) 2024.02.07
    3D(Terrian, 파티클, Ray 클래스)  (1) 2024.02.05
Designed by Tistory.