유니티 기초/복습

SimpleRPG 복습

다모아 2023. 8. 8. 23:45
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class HeroController : MonoBehaviour
{
    private Animator anim;
    private Vector3 targetPosition;

    private float moveSpeed = 1f;
    private bool isMove = false;
    // Start is called before the first frame update
    void Start()
    {
        this.anim = GetComponent<Animator>();
    }

    // Update is called once per frame
    void Update()
    {
        if (isMove)
        {
            //이동하기
            this.anim.SetInteger("State", 1);
            this.transform.Translate(Vector3.forward * this.moveSpeed * Time.deltaTime);
            //위치 distance로 바꿔주기
            float distance = Vector3.Distance(this.transform.position, this.targetPosition);
            if(distance <= 0.1f)
            {
                this.isMove = false;
            }
        }
        else
        {
            this.anim.SetInteger("State", 0);
            this.transform.Translate(Vector3.zero);
        }
    }

    //목표 : targetPosition까지 가기
    public void Move(Vector3 targetPosition)
    {
        //위치 저장
        this.targetPosition = targetPosition;
        //목표 바라보기
        this.transform.LookAt(targetPosition);
        this.isMove = true;
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SimpleRpgSceneMain : MonoBehaviour
{
    [SerializeField]
    private HeroController heroController;
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        //마우스 왼쪽 클릭시
        if (Input.GetMouseButtonDown(0))
        {
            //Ray로 마우스 위치 전달받기
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            //마우스위치 찍어보기
            Debug.DrawRay(ray.origin, ray.direction * 100f, Color.red, 2f);
            //찍은 위치 저장하기
            RaycastHit hit;
            //충돌햇다면 true ,, 아니면 false
            if(Physics.Raycast(ray, out hit, 100f))
            {
                //이동하기.. 하지만 이 위치(hit.point)는 목표이고 이동해야함
                this.heroController.Move(hit.point);
            }
            
        }
    }
}

 

HeroController에서 목표위치와 Hero캐릭터 위치랑 거리를 어떻게 해주지.. 생각해보고 뭐로하더라 기억이 안나서 한번 봤다.

 


코루틴 + 대리자 사용

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

public class HeroController : MonoBehaviour
{
    private Animator anim;
    private Vector3 targetPosition;
    private Coroutine moveRoutine;
    public System.Action onMoveComplete;
    private float moveSpeed = 1f;
    // Start is called before the first frame update
    void Start()
    {
        this.anim = GetComponent<Animator>();
    }

    //코루틴 사용
    private IEnumerator CoMove()
    {
        while(true)
        {
            //목표 바라보기
            this.transform.LookAt(this.targetPosition);
            //이동하기
            this.anim.SetInteger("State", 1);
            this.transform.Translate(Vector3.forward * this.moveSpeed * Time.deltaTime);
            //위치 distance로 바꿔주기
            float distance = Vector3.Distance(this.transform.position, this.targetPosition);
            if (distance <= 0.1f)
            {
                break;
            }
            yield return null;
        }
        this.anim.SetInteger("State", 0);
        this.transform.Translate(Vector3.zero);
        this.onMoveComplete();

    }

    //목표 : targetPosition까지 가기
    public void Move(Vector3 targetPosition)
    {
        //위치 저장
        this.targetPosition = targetPosition;
        //코루틴이 실행중이라면
        if(this.moveRoutine != null)
        {
            this.StopCoroutine(this.CoMove());
        }
        this.moveRoutine = this.StartCoroutine(this.CoMove());
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SimpleRpgSceneMain : MonoBehaviour
{
    [SerializeField]
    private HeroController heroController;

    // Start is called before the first frame update
    void Start()
    {
        this.heroController.onMoveComplete = (() => {
            Debug.Log("이동완료");
        });
    }

    // Update is called once per frame
    void Update()
    {
        //마우스 왼쪽 클릭시
        if (Input.GetMouseButtonDown(0))
        {
            //Ray로 마우스 위치 전달받기
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            //마우스위치 찍어보기
            Debug.DrawRay(ray.origin, ray.direction * 100f, Color.red, 2f);
            //찍은 위치 저장하기
            RaycastHit hit;
            //충돌햇다면 true ,, 아니면 false
            if(Physics.Raycast(ray, out hit, 100f))
            {
                //이동하기.. 하지만 이 위치(hit.point)는 목표이고 이동해야함
                this.heroController.Move(hit.point);
            }
            
        }
    }
}

코루틴 .. yield return null이 처음에 이해가 안됐는데 distance가 <= 0.1f에 도달해서 while문을 빠져나가기 전까지 계속 실행해주는 것 같다. distance가 거리에 도달하면 멈추고 State 상태를 0으로 만들어서 Idle 상태로 만들어준다.

 

대리자 .. 학교에서할 때는 뭔지도 잘 모르는 상태로 사용했었는데 State 0 상태일 때 this.onMoveComplete();를 호출해서 대리자로 사용하는것을 이해했다.


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

public class HeroController : MonoBehaviour
{
    private Animator anim;
    private Vector3 targetPosition;
    private MonsterController target;
    private Coroutine moveRoutine;
    public System.Action<MonsterController> onMoveComplete;

    private float moveSpeed = 1f;
    public float radius = 1f;
    // Start is called before the first frame update
    void Start()
    {
        this.anim = GetComponent<Animator>();
    }

    //코루틴 사용
    private IEnumerator CoMove()
    {
        while(true)
        {
            //목표 바라보기
            this.transform.LookAt(this.targetPosition);
            //이동하기
            this.anim.SetInteger("State", 1);
            this.transform.Translate(Vector3.forward * this.moveSpeed * Time.deltaTime);
            //위치 distance로 바꿔주기
            float distance = Vector3.Distance(this.transform.position, this.targetPosition);
            //타겟이 있을 경우
            if (this.target != null)
            {
                if (distance <= 2f)
                {
                    break;
                }
            }
            else
            {
                if(distance <= 0.1f)
                {
                    break;
                }
            }
            yield return null;
        }
        this.anim.SetInteger("State", 0);
        this.onMoveComplete(this.target);

    }

    //Method Overload
    public void Move(MonsterController target)
    {
        this.target = target;
        this.targetPosition = this.target.gameObject.transform.position;
        //코루틴이 실행중이라면
        if (this.moveRoutine != null)
        {
            this.StopCoroutine(this.CoMove());
        }
        this.moveRoutine = this.StartCoroutine(this.CoMove());
    }

    //목표 : targetPosition까지 가기
    public void Move(Vector3 targetPosition)
    {
        //타겟 지움
        this.target = null;
        //위치 저장
        this.targetPosition = targetPosition;
        //코루틴이 실행중이라면
        if(this.moveRoutine != null)
        {
            this.StopCoroutine(this.CoMove());
        }
        this.moveRoutine = this.StartCoroutine(this.CoMove());
    }

    public void Attack()
    {

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

 

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

public class SimpleRpgSceneMain : MonoBehaviour
{
    [SerializeField]
    private HeroController heroController;
    // Start is called before the first frame update
    void Start()
    {
        this.heroController.onMoveComplete = ((target) => {
            Debug.Log("이동완료");
        });
    }

    // Update is called once per frame
    void Update()
    {
        //마우스 왼쪽 클릭시
        if (Input.GetMouseButtonDown(0))
        {
            //Ray로 마우스 위치 전달받기
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            //마우스위치 찍어보기
            Debug.DrawRay(ray.origin, ray.direction * 100f, Color.red, 2f);
            //찍은 위치 저장하기
            RaycastHit hit;
            //충돌햇다면 true ,, 아니면 false
            if(Physics.Raycast(ray, out hit, 100f))
            {
                //충돌 tag 정보 확인
                Debug.Log(hit.collider.tag);
                if(hit.collider.tag == "Monster")
                {
                    MonsterController monsterController = hit.collider.gameObject.GetComponent<MonsterController>();
                    //거리 합 구하기
                    float distance = Vector3.Distance(this.transform.position, hit.collider.gameObject.transform.position);
                    //반지름의 합 구하기
                    float sumRadius = this.heroController.radius + monsterController.radius;
                    //사거리 안에 들어오면
                    if(distance <= sumRadius)
                    {
                        //공격
                    }
                    else
                    {
                        //이동
                        this.heroController.Move(monsterController);
                    }
                }
                else if(hit.collider.tag == "Ground")
                {
                    this.heroController.Move(hit.point);
                }

            }
            
        }
    }
}

 

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

public class MonsterController : MonoBehaviour
{
    public float radius = 1f;

    // Start is called before the first frame update
    void Start()
    {
        
    }

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

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

 

뭔가.. 오류가 난다 실질적 오류는 아니지만 땅을 계속 연속 클릭하면 속도가 계속 빨라진다 왜 그런걸까?

 

계속 빨라진다..

 

빨라지는 오류를 찾았다.

이 부분이 this.StopCoroutine이 계속 this.CoMove())로 받고있어서 다른 곳을 지정할 때마다 StopCoroutine이 실행되서 this.CoMove())가 실행되었던 것 같다. 다시 this.moveRoutine으로 받아주었다.


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

public class HeroController : MonoBehaviour
{
    private Animator anim;
    private Vector3 targetPosition;
    private MonsterController target;
    private Coroutine moveRoutine;
    public System.Action<MonsterController> onMoveComplete;

    private float moveSpeed = 1f;
    public float radius = 1f;
    // Start is called before the first frame update
    void Start()
    {
        this.anim = GetComponent<Animator>();
    }

    //코루틴 사용
    private IEnumerator CoMove()
    {
        while(true)
        {
            //목표 바라보기
            this.transform.LookAt(this.targetPosition);
            //이동하기
            this.anim.SetInteger("State", 1);
            this.transform.Translate(Vector3.forward * this.moveSpeed * Time.deltaTime);
            //위치 distance로 바꿔주기
            float distance = Vector3.Distance(this.transform.position, this.targetPosition);
            //타겟이 있을 경우
            if (this.target != null)
            {
                if (distance <= 2f)
                {
                    break;
                }
            }
            else
            {
                if(distance <= 0.1f)
                {
                    break;
                }
            }
            yield return null;
        }
        this.anim.SetInteger("State", 0);
        this.onMoveComplete(this.target);

    }

    //Method Overload
    public void Move(MonsterController target)
    {
        this.target = target;
        this.targetPosition = this.target.gameObject.transform.position;
        //코루틴이 실행중이라면
        if (this.moveRoutine != null)
        {
            this.StopCoroutine(this.CoMove());
        }
        this.moveRoutine = this.StartCoroutine(this.CoMove());
    }

    //목표 : targetPosition까지 가기
    public void Move(Vector3 targetPosition)
    {
        //타겟 지움
        this.target = null;
        //위치 저장
        this.targetPosition = targetPosition;
        //코루틴이 실행중이라면
        if(this.moveRoutine != null)
        {
            this.StopCoroutine(this.CoMove());
        }
        this.moveRoutine = this.StartCoroutine(this.CoMove());
    }

    public void Attack(MonsterController target)
    {
        this.anim.SetInteger("State", 2);
        this.StartCoroutine(this.CoAttack());
    }

    private IEnumerator CoAttack()
    {
        yield return new WaitForSeconds(0.1f);

        yield return new WaitForSeconds(0.73f);

        this.anim.SetInteger("State", 0);
    }
    private void OnDrawGizmos()
    {
        Gizmos.DrawWireSphere(this.transform.position, this.radius);
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SimpleRpgSceneMain : MonoBehaviour
{
    [SerializeField]
    private HeroController heroController;
    // Start is called before the first frame update
    void Start()
    {
        this.heroController.onMoveComplete = ((target) => {
            Debug.Log("이동완료");
            if(target != null)
            {
                this.heroController.Attack(target);
            }
        });
    }

    // Update is called once per frame
    void Update()
    {
        //마우스 왼쪽 클릭시
        if (Input.GetMouseButtonDown(0))
        {
            //Ray로 마우스 위치 전달받기
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            //마우스위치 찍어보기
            Debug.DrawRay(ray.origin, ray.direction * 100f, Color.red, 2f);
            //찍은 위치 저장하기
            RaycastHit hit;
            //충돌햇다면 true ,, 아니면 false
            if(Physics.Raycast(ray, out hit, 100f))
            {
                //충돌 tag 정보 확인
                Debug.Log(hit.collider.tag);
                if(hit.collider.tag == "Monster")
                {
                    MonsterController monsterController = hit.collider.gameObject.GetComponent<MonsterController>();
                    //거리 합 구하기
                    float distance = Vector3.Distance(this.transform.position, hit.collider.gameObject.transform.position);
                    //반지름의 합 구하기
                    float sumRadius = this.heroController.radius + monsterController.radius;
                    //사거리 안에 들어오면
                    if(distance <= sumRadius)
                    {
                        //공격
                    }
                    else
                    {
                        //이동
                        this.heroController.Move(monsterController);
                    }
                }
                else if(hit.collider.tag == "Ground")
                {
                    this.heroController.Move(hit.point);
                }

            }
            
        }
    }
}

 

공격하기