유니티 심화/복습

[복습 & 추가] HeroShooter - 몬스터 Indicator.. 잘모르는 것들

다모아 2023. 8. 25. 01:12

뭔가 혼자 해보고 해보다가 절대 모르겠어서 카페에 나와있는 코드를 보고 따라해봤다.

따라해봤을 때는 이해가 되는 것들도 있었지만 이해가 되지 않는 것들이 훨씬 많았다.

잘 모르는 것들

https://docs.unity3d.com/ScriptReference/Object.FindObjectOfType.html

 

Unity - Scripting API: Object.FindObjectOfType

Object.FindObjectOfType will not return Assets (meshes, textures, prefabs, ...) or inactive objects. It will not return an object that has HideFlags.DontSave set. Please note that this function is very slow. It is not recommended to use this function every

docs.unity3d.com

https://learn.microsoft.com/ko-kr/dotnet/api/system.linq.enumerable.firstordefault?view=net-7.0 

 

Enumerable.FirstOrDefault 메서드 (System.Linq)

시퀀스의 첫 번째 요소를 반환하거나, 요소가 없으면 기본값을 반환합니다.

learn.microsoft.com

https://learn.microsoft.com/ko-kr/dotnet/api/system.collections.generic.dictionary-2?view=net-7.0

 

Dictionary<TKey,TValue> 클래스 (System.Collections.Generic)

키와 값의 컬렉션을 나타냅니다.

learn.microsoft.com

 

 

뭔가 아직까지는 잘 모르겠다..


 

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

public class Stage1SceneMain : MonoBehaviour
{
    //플레이어
    [SerializeField]
    private PlayerController playerController;
    //몬스터
    [SerializeField]
    private MonsterController target;
    //조이스틱
    [SerializeField]
    private CJoystick joystick;
    //왼쪽 문
    [SerializeField]
    private DoorController leftDoorController;
    //오른쪽 문
    [SerializeField]
    private DoorController rightDoorController;
    //페이드인
    [SerializeField]
    private Image dim;
    //NPC 텍스트
    [SerializeField]
    private Text text;
    //TargetQuad
    [SerializeField]
    private GameObject targetQuadGo;
    //반지름
    private float radius = 30f;
    //딕셔너리
    private Dictionary<GameObject, float> dic = new Dictionary<GameObject, float>();
    // Start is called before the first frame update
    void Start()
    {
        //씬 전환시 페이드인
        this.StartCoroutine(this.CoFadeInAndSceneManager());
        //포탈에 닿았을 때
        this.playerController.reachPortal = () =>
        {
            //포탈 삭제하고
            //Destroy(this.portalGo.gameObject);
            //문 열기
            //this.leftDoorController.OpenDoor();
            //this.rightDoorController.OpenDoor();
        };

        //문에 닿았을 때
        this.playerController.reachDoor = () =>
        {
            //씬 전환하기
            //this.FadeOutAndSceneManager();
        };
    }

    // Update is called once per frame
    void Update()
    {
        //수평, 수직 좌표 구하고 moveDir에 넣기
        float h = this.joystick.Horizontal;
        float v = this.joystick.Vertical;
        Vector3 moveDir = new Vector3(h, 0, v);
        //moveDir이 Vector3.zero일 경우
        if (moveDir == Vector3.zero)
        {
            //정지 애니메이션 실행
            this.playerController.PlayAnimation(EnumState.ePlayerState.Idle);
        }
        else // (0,0,0)이 아닐 경우
        {
            this.playerController.Move(moveDir);
        }
        //OverlapSphere로 몬스터 인식하기
        int layerMask = 1 << LayerMask.NameToLayer("Monster");
        Collider[] hitColliders = Physics.OverlapSphere(this.playerController.transform.position, this.radius, layerMask);

        this.dic.Clear();
        foreach (Collider cols in hitColliders)
        {
            //딕셔너리에 거리차이와 게임오브젝트 추가
            this.dic.Add(cols.gameObject, Vector3.Distance(cols.transform.position, this.playerController.transform.position));
        }

        //만약 dic.Count가 0보다 크다면
        if (dic.Count > 0)
        {
            //작은 값을 short_distance에 넣기
            float short_distance = this.dic.Values.Min();
            //몬스터한 키값 넣어주기
            var target = this.dic.FirstOrDefault(x => x.Value == short_distance).Key;
            //dir에 몬스터와 플레이어의 거리 빼준 값 넣기
            Vector3 dir = this.target.transform.position - this.playerController.transform.position;
            this.playerController.MonsterRay(dir, this.target);
        }
    }

    private IEnumerator CoFadeInAndSceneManager()
    {
        Color color = this.dim.color;
        while (true)
        {
            color.a -= 0.01f;
            this.dim.color = color;

            if (color.a <= 0)
            {
                break;
            }
            yield return null;
        }
    }
}

 

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

public class PlayerController : MonoBehaviour
{
    //캐릭터 움직임 속도
    [SerializeField]
    private float moveSpeed = 5f;
    //애니메이션
    private Animator anim;
    //포탈태그했을 때
    public Action reachPortal;
    //문에 닿았을 때
    public Action reachDoor;
    //rayOffsetY
    private float rayOffsetY = 0.5f;

    void Start()
    {
        this.anim = this.GetComponent<Animator>();
    }

    // Update is called once per frame
    void Update()
    {

    }

    public void Move(Vector3 moveDir)
    {
        //달리는 애니메이션 실행
        this.PlayAnimation(EnumState.ePlayerState.RunF);
        //Mathf.Atan2와 Mathf.Rad2Deg를 이용해서 회전시키기
        float angle = Mathf.Atan2(moveDir.x, moveDir.z) * Mathf.Rad2Deg;
        //AngleAxis를 이용하기
        Quaternion q = Quaternion.AngleAxis(angle, Vector3.up);
        //캐릭터에 회전 적용
        this.transform.rotation = q;
        //캐릭터 움직이기
        this.transform.Translate(Vector3.forward * this.moveSpeed * Time.deltaTime);
    }
    public void PlayAnimation(EnumState.ePlayerState state)
    {
        this.anim.SetInteger("State", (int)state);
    }

    private void OnCollisionEnter(Collision collision)
    {
        if (collision.collider.tag == "Door")
        {
            //대리자 사용해서 씬 전환하기
            this.reachDoor();
        }
        else if (collision.collider.tag == "Portal")
        {
            //대리자 사용해서 문 열고, 포탈 제거하기
            this.reachPortal();
        }
    }

    private void OnDrawGizmos()
    {
        Gizmos.DrawWireSphere(this.transform.position, 30f);
    }

    public void MonsterRay(Vector3 dir, MonsterController target)
    {
        //레이로 플레이어의 살짝 위에서 몬스터까지의 거리 찍기
        Ray ray = new Ray(this.transform.position + (Vector3.up * this.rayOffsetY), dir);
        //그려보기
        Debug.DrawRay(ray.origin, ray.direction * 100f, Color.green);
        //layerMask.NameToLayer로 레이어 구별해주기
        var layerMask = 1 << LayerMask.NameToLayer("Monster") | 1 << LayerMask.NameToLayer("Car") | 1 << LayerMask.NameToLayer("Wall");
        RaycastHit hit;
        if(Physics.Raycast(ray, out hit, 100f, layerMask))
        {
            Debug.Log(hit.collider.tag);

            if(hit.collider.CompareTag("Monster"))
            {
                if(target != null)
                {
                    target.HideIndicator();
                }
                var monsterController = hit.collider.gameObject.GetComponent<MonsterController>();
                monsterController.ShowIndicator();
                target = monsterController;
            }
            else
            {
                MonsterController[] monsterContorllers = GameObject.FindObjectsOfType<MonsterController>();
                Debug.Log(monsterContorllers.Count());

                foreach (MonsterController monster in monsterContorllers)
                {
                    monster.HideIndicator();
                }
                target = null;
            }
        }
    }
}