Unity-플레이어 조작 구현 연습-이동하기

-Test_PlayerControlScene을 만들어주었다.

3dObject-Plane ,Player 생성. 플레이어는 에셋의 프리팹을 가져오고 unpack해주었다.

-Player의 애니메이션 컨트롤러를 복사해 (수정시 문제가 생기는것을 막기위해) 새로 만들어주고 넣어주었다.(MyDogControl)

 

-Player의 오브젝트명을 Hero로 변경

-모두를 관리하는 스크립트 Test_PlayerControlSceneMain 생성, 빈 오브젝트를 만들어 스크립트를 넣어준다.

:Input을 받는다

스크립트 확인

*메인 카메라는 태그에 Main이 붙어있어야한다.

레이 생성 후 테스트

-ray충돌검사

레이 충돌검사

-HeroController추가

namespace설정
Move메서드 작성
PlayerControlSceneMain 코드 수정

https://docs.unity3d.com/kr/560/Manual/Coroutines.html

 

코루틴 - Unity 매뉴얼

함수를 호출하면 값을 반환하기 전에 실행 완료됩니다. 이는 함수에서 수행되는 모든 액션이 하나의 프레임 업데이트 내에서 발생해야 한다는 것을 의미합니다. 시간이 지남에 따라 절차식 애

docs.unity3d.com

 

-StartCoroutine을 사용해서 메서드를 호출: 메서드를 써서 메서드를 호출

Update 메서드는 우리가 원할 때 호출하거나 멈출 수 없다. => 코루틴 사용

코루틴 연습을 위해 Fade함수를 구현해 보았다.

 

코루틴 fade 연습

canvas inactive하고 Hero Controller를 수정해보자.

원래 update에 쓰고자 한다면 다음과같이 작성할 것이다.

if문 이하 내용은 Update()에 작성된것.Move구현

-this.transform.position을 this.targetPosition으로 써야 잘 작동한다 고쳤는데 이전이미지이다.

코루틴을 통해 바꿔 구현

추가 수정

-애니메이션을 넣기위해 Animator 창에서 parameters 추가하고 transition을 추가해 1을 넣어주었다.

HasExitTime 

https://docs.unity3d.com/Manual/class-Transition.html

 

Unity - Manual: Animation transitions

Animation transitions Animation transitions allow the state machineThe set of states in an Animator Controller that a character or animated GameObject can be in, along with a set of transitions between those states and a variable to remember the current st

docs.unity3d.com

 

-Has Exit Time 체크 안하면 앞에 애니메이션이 끝나는 시간이 추가된다.(해제하고 해야 순서대로 딱딱 나온다.)

 

-코드 수정

-몬스터 클릭하면 몬스터에게 이동을 구현하기 위해 몬스터를 추가하고 태그와 Collider를 추가하였다.

-MonsterController 추가

-GizmosExtensions.cs추가(바닥에 원을 그리기 위해 사용)

https://gist.github.com/luisparravicini/50d044a20c67f0615fdd28accd939df4

 

Method to draw an arc with Unity's Gizmos

Method to draw an arc with Unity's Gizmos. GitHub Gist: instantly share code, notes, and snippets.

gist.github.com

-MonsterController에도 추가

 

-클릭하면 이동하다가 (공격)사거리 안에 들어오면 공격한다.

-heroController수정

-main도 수정

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

public class MonsterController : MonoBehaviour
{
    public float radius=1;
    // Start is called before the first frame update
    void Start()
    {
        
    }

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

    private void OnDrawGizmos()
    {//바닥에 원을 그린다.
        GizmosExtensions.DrawWireArc(this.transform.position, Vector3.forward, 360, radius);
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Test;
using UnityEngine.UI;

public class Test_PlayerControlSceneMain : MonoBehaviour
{
    [SerializeField]
    private HeroController heroController;
    [SerializeField]
    private Image image;
    // Start is called before the first frame update
    void Start()
    {
        //var heroGo = GameObject.Find("Hero");
        //this.heroController = heroGo.GetComponent<HeroController>();
        //this.heroController = GameObject.FindObjectOfType<HeroController>();
        this.StartCoroutine(CoFade());//코루틴 함수 호출
        this.heroController.onMoveComplete = () =>
        {
            Debug.Log("<color=cyan>이동을 완료했습니다.</color>");
        };
    }

    // Update is called once per frame
    void Update()
    {
        //화면을 클릭하면 클릭한 위치로 Hero가 이동
        if (Input.GetMouseButtonDown(0))
        {
            //Debug.Log("Down");
            //Ray를 만든다.
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);//태그가 메인인 카메라를 찾는다.
            float maxDistance = 100f;
            //화면에 Ray 출력
            Debug.DrawRay(ray.origin,ray.direction*maxDistance,Color.red,3f);

            //충돌검사       
            RaycastHit hit;//raycast에서 out키워드 사용을 위해 변수선언을 먼저해준다.
            if (Physics.Raycast(ray, out hit, maxDistance))
            {
              //  Debug.Log(hit.point);//충돌정보가 hit변수에 담김/월드상의 충돌 지점 위치(Vector3)
                                     //hit.point는 널이 나올 수 없다.
                                     //hit.point가 타입이 Vector3로 구조체-값형식이기 때문에 힙을 쓰지않아 널이 될 수 없다.
                                     //this.heroGo.transform.position = hit.point;//hero 게임 오브젝트의 위치에 hit.point를 넣어줌           
                if (hit.collider.tag == "Monster")//몬스터 클릭시
                {//태그를 통해 몬스터에 접근
                    Debug.LogFormat("Monster: {0}", hit.collider.tag);
                    //거리를 구한다.
                    Vector3 targetPosition = hit.collider.gameObject.transform.position;
                    float distance = Vector3.Distance(this.heroController.gameObject.transform.position, targetPosition);                  
                    MonsterController monsterController = hit.collider.gameObject.GetComponent<MonsterController>();
                   //각 반지름 더한거와 비교
                   float sumRadius = this.heroController.radius + monsterController.radius;
                    //Debug.Log(distance);
                    if (distance <= sumRadius)//사거리 안에 들어옴 => attack
                    {
                        Debug.Log("Attack");
                    }
                    else//이동
                    {
                        this.heroController.MoveToMonster(monsterController);
                    }
                   

                }
                else if (hit.collider.tag == "Ground")
                {
                    Debug.Log(hit.point);
                    this.heroController.Move(hit.point);
                }

            }


        }
    }
  
    IEnumerator CoFade()//코루틴 함수
    {
        for (float f = 1f; f >= 0; f -= 0.01f)
        {
            Color c = this.image.color;
            c.a = f;
            this.image.color = c;
            //다음 프레임
            yield return null;
        }
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

 namespace Test{
    public class HeroController : MonoBehaviour
    {
        //bool isMoveStart = false;
        private Vector3 targetPosition;
        private Coroutine moveRoutine;
        private Animator anim;
        public System.Action onMoveComplete;
        public float radius = 1;
        private MonsterController target;

        // Start is called before the first frame update
        void Start()
        {
            //targetPosition = this.transform.position;
            this.anim = GetComponent<Animator>();
        }

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

        }

        public void Move(Vector3 targetPosition)
        {
            //타겟을 지움 
            this.target = null;
            this.targetPosition = targetPosition;//이동할 목표 지점을 저장
            Debug.Log("Move");
            //이동시작
            if (this.moveRoutine != null)
            {
                //이미 코루틴이 실행중이다 -> 중지 
                this.StopCoroutine(this.moveRoutine);
            }
            this.moveRoutine = StartCoroutine(this.CoMove());
            this.anim.SetInteger("State",1);//Run Animation
        }
        public void MoveToMonster(MonsterController target)
        {
            this.target = target;
            this.targetPosition =this.target.gameObject.transform.position;//이동할 목표 지점을 저장
          
            Debug.Log("MoveToMonster");
           // Debug.LogFormat("targetPosition: {0},{1}",targetPosition,this.target.gameObject.transform.position);
            //이동시작      
            if (this.moveRoutine != null)
            {
                //이미 코루틴이 실행중이다 -> 중지 
                this.StopCoroutine(this.moveRoutine);
            }
            this.moveRoutine = StartCoroutine(this.CoMove());
            this.anim.SetInteger("State", 1);//Run Animation
        }

        private IEnumerator CoMove()
        {
            while (true)
            {
                this.transform.LookAt(this.targetPosition);//방향을 바라봄               
                this.transform.Translate(Vector3.forward * 1f * Time.deltaTime);//이미 바라봤으니까 정면으로 이동
                                                                                //목표지점과 나와의 거리를 잼
                float distance = Vector3.Distance(this.transform.position, this.targetPosition);
                //Debug.LogFormat("target :{0}",this.target);//타겟이 있는지 확인
               // Debug.Log(distance);              
                if (this.target != null)  //타겟이 있을 경우
                {//this.targetPosition은 Vector3라 null과 비교하면안된다. 주의.
                    if (distance <= (1f + 1f))
                    {
                        break;
                    }
                }
                else//타겟이 없을 경우
                {
                   // Debug.Log(distance);
                    if (distance <= 0.1f)//** 0이 될수 없다 가까워질때 도착한것으로 판단 하자 
                    {//도착

                        break;//isMoveStart 같은 변수를 안쓸 수 있다.
                    }
                }
                yield return null;//** 다음 프레임 시작
            }         
            Debug.Log("<color=yellow>도착!</color>");
            this.anim.SetInteger("State", 0);//Idle Animation :Stop
            //대리자를 호출
            this.onMoveComplete();
        }

        private void OnDrawGizmos()
        {
           // Gizmos.color = Color.red;
            // Gizmos.DrawWireSphere(this.transform.position, 1.2f);
            GizmosExtensions.DrawWireArc(this.transform.position,this.transform.forward,360,radius,40);
        }
    }
}
myoskin