Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Tags more
Archives
Today
Total
관리 메뉴

코딩inf

#5 적 이동 스크립트 구현 / 타워설치(돈 감소) (6/24) 본문

Unity/TowerDiffence

#5 적 이동 스크립트 구현 / 타워설치(돈 감소) (6/24)

D-coding 2021. 6. 24. 23:09

일단 스크립트 이다.

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

public class Enemy : MonoBehaviour
{
    public Transform[] waypoints;
    public Transform targetWaypoint;
    public int targetWaypointIndex = 0;
    public float minDistance = 0.001f;

    public float movementSpeed = 3.0f;
    public float rotationSpeed = 1.0f;
    // Start is called before the first frame update
    void Start()
    {
        targetWaypoint = waypoints[targetWaypointIndex];
    }

    // Update is called once per frame
    void Update()
    {
        float movementStep = movementSpeed * Time.deltaTime;
        float rotationStep = rotationSpeed * Time.deltaTime;

        Vector3 directionToTarget = targetWaypoint.position - transform.position;
        Quaternion rotationToTarget = Quaternion.LookRotation(directionToTarget);

        transform.rotation = rotationToTarget;

        float distance = Vector3.Distance(transform.position, targetWaypoint.position);
        CheckDistanceToWayPoint(distance);


        transform.position = Vector3.MoveTowards(transform.position, targetWaypoint.position, movementStep);
    }

    void CheckDistanceToWayPoint(float currentDistance) 
    {
        if(currentDistance <= minDistance && waypoints.Length < targetWaypointIndex)
        {
            targetWaypointIndex++;
            UpdateTargetWaypoint();
        }
    }
    void UpdateTargetWaypoint()
    {
        targetWaypoint = waypoints[targetWaypointIndex];
    }
}

 

 

 

 

 

public Transform[] waypoints;

적이 이동할 다음 위치를 정해두는 transform 배열 입니다.

public Transform targetWaypoint;

현재 적이 목표로 두고 있는 위치의 정보를 저장하는 변수 입니다.

public int targetWaypointIndex = 0;

목표 위치의 순서를 컨트롤 하는 정수형 변수 입니다.

public float minDistance = 0.001f;

현재 목표 위치와의 거리가 최소 거리(0.001)보다 작으면 도착했다라고 판별하는 변수 입니다.

public float movementSpeed = 3.0f;

적의 속도를 저장하는 변수 입니다.

public float rotationSpeed = 1.0f;

 적의 회전 속도를 저장하는 변수 입니다.

                  void Start() 
   
                                                                                targetWaypoint = waypoints[targetWaypointIndex]; 
    

 

 

여기부터는 update함수이다.

 

목표 좌표를 다음 위치를 정하는 배열[targetWaypoint(0)]으로 지정

float movementStep = movementSpeed * Time.deltaTime;

 float rotationStep = rotationSpeed * Time.deltaTime; 

이동속도와 회전속도에 델타 타임을 곱해서 프레임당이 아닌

초당으로 계산해 공정하게 플레이 할 수 있도록 설정했습니다.

Vector3 directionToTarget = targetWaypoint.position - transform.position; 
Quaternion rotationToTarget = Quaternion.LookRotation(directionToTarget); 

transform.rotation = rotationToTarget; 

Vector3 directionToTarget을 만들고 그 값을 목표좌표에서 현재 좌표를 뺀 만큼으로 해 줍니다.

directionToTarget을 rotationToTarget의 회전값으로 한다.

rotationToTarget을 적 오브젝트의 회전값으로 한다.

float distance = Vector3.Distance(transform.position, targetWaypoint.position);

distance변수를 현재 좌표부터 목표좌표까지의 거리로 정한다. 즉 distance변수는 목표 좌표까지의 거리이다.

CheckDistanceToWayPoint(distance);

구한 distance를 CheckDistanceToWayPoint로 호출한다.

transform.position = Vector3.MoveTowards(transform.position, targetWaypoint.position, movementStep);

위치를 현재 좌표(transform.position)부터

목표 좌표(targetWaypoint.position)까지 deltaTime을 곱한 변수(movementStep)만끔씩 이동한다.

 

 

여기부터는 CheckDistanceToWayPoint 함수이다.

 

void CheckDistanceToWayPoint(float currentDistance)  

일단 목표좌표까지의 거리(currentDistance)를 반환받는다.

if(currentDistance <= minDistance && waypoints.Length < targetWaypointIndex)

만일 목표좌표까지의 거리가(currentDistance) 최소거리(minDistance 현재 값은 0.001이다)보다 작고

배열의 크기(.Length를 하면 배열의 크기를 알 수 있다.)가 목표위치 순서보다 작으면 실행

배열의 크기는 적이이동할 좌표의 개수를 의미한다. 그리고 목표위치 순서(targetWaypointIndex)가 현재 3이면

적이 이동한 좌표의 개수는 총 4개가 되는것이다.

그러니 목표위치 순서(targetWaypointIndex)가 무한 증가하는것을 방지하려고 구현한 코드이다.

 

그러므로 이 if문은 적이 도착하였는가를 체크해주는 알고리즘이다.

targetWaypointIndex++;

자 그럼 위에 조건을 충족시키면

목표위치 순서(targetWaypointIndex)를 1만큼 증가시킨다.

UpdateTargetWaypoint();

그리고 UpdateTargetWaypoint함수를 실행 시킨다.

 

여기부터는 UpdateTargetWaypoint함수이다.

targetWaypoint = waypoints[targetWaypointIndex];

적이 도착했을때 다음 목표좌표로 변경해주기 위해 구현했다.

현재 목표좌표는 여러 목표좌표가 있는 배열(waypoints[])중 현재 있는 목표위치 순서(targetWaypointIndex)

로 바꿔주는거다.

그런데 여기서 근데 지금있는 순서로 바꾸면 그대로 아니냐 할 수 있다.

근데 CheckDistanceToWayPoint 함수에서 targetWaypointIndex에 1을 더했으니 다음 순서로 변수가 지정이 된것이다.

 

 

 

 

 

 

 

 

변수 세팅은 적이 이동할 좌표를 순서대로 waypoints[]배열에 넣고

targetWaypointIndex는 0으로 지정한뒤 나머지는 개개인에 따라 잘 설정하면 된다.

 

-----------------------------------------------------------------------------------------------------------------------------------

 

이제 타워 설치 구현 코드이다.

코드는 전에 작성한 Ray 파일이고 수정부분은 색처리함.

 

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

public class Ray : MonoBehaviour
{
    private RaycastHit hit;
    public TextMeshProUGUI look;
    public GameObject[] before; 
    public Material[] MA;
    public bool[] isTower;
    public GameObject EnemyPrefab;
    public GameObject TowerPrefab;
    public TextMeshProUGUI money;
    public TextMeshProUGUI Error;
    public int have_money = 1000;
    public GameObject[] TowerPointPosition;

    void Start()
    {
        money.text = ("Money : " + have_money);
    }
    void Update()
    {
        Raycast(); // ray를 쏘아주고 text(look)에 맞은 위치, 거리, 이름을 출력해주는 함수
        TowerPointMeshRenderer(); //맞은 오브젝트의 색을 바꿔주는 함수이다.
    }
    void Raycast()
    {
        if (Physics.Raycast(this.transform.position, transform.forward, out hit)) //ray를 쏘아서 맞았을때
        {
            //Debug.Log("hit point : " + hit.point + ", distance : " + hit.distance + ", name : " + hit.collider.name+ ", tag : " +hit.collider.tag);
            look.text = ("hit point : " + hit.point + ", distance: " + hit.distance + ", name: " + hit.collider.name + ", hit object position : " + hit.collider.transform.position ); // look.text에 맞은 좌표와, 현재 지점부터의 거리, 맞은 오브젝트, 맞은 오브젝트의 position를 출력

            Debug.DrawRay(this.transform.position, transform.forward * 100000f, Color.red); //scene창에 ray를 출력해주어서 시각적으로 볼 수 있게 해준다. game 창에는 표시 안됨
            //hit.collider.gameObject.GetComponent<Renderer>().material.color = Color.red;
        }
        else { 
            Debug.DrawRay(this.transform.position, transform.forward * 100000f, Color.red); //scene창에 ray를 출력해주어서 시각적으로 볼 수 있게 해준다. game 창에는 표시 안됨
        }
    }
    void TowerPointMeshRenderer()
    {
        int i = 0; //tower point의 색을 일일히 변경하려면 30줄 가량 써야해서 for문으로 구성하기위해 만든 변수
        for(i = 0; i < 32; i++) //총 32번 반복
        {
            if (hit.collider.name == before[i].name)
            {

                hit.collider.GetComponent<Renderer>().material = MA[1]; 
                if(isTower[i] == false && Input.GetMouseButtonDown(0) && have_money >= 100)
                {
                    Instantiate(TowerPrefab, TowerPointPosition[i].transform.position, TowerPrefab.transform.rotation);
                    have_money -= 100;
                    money.text = ("Money : " + have_money);
                    isTower[i] = true;
                }
                if (isTower[i] == false && Input.GetMouseButtonDown(0) && have_money < 100)
                {
                    StartCoroutine("Error_Coroutine_0001");
                }

            } //그 맞은 오브젝트의 색 맞은 상태의 색(MA[1])로 바꾸기
            else { before[i].GetComponent<Renderer>().material = MA[0]; } //아니면 안맞은 상태의 색(MA[0])로 바꾸기
        }
    }
    IEnumerator Error_Coroutine_0001()
    {
        Error.text = ("Not Enough Money (Code 0001)");
        yield return new WaitForSeconds(3f);
        Error.text = ("");
    }
}

public bool[] isTower;

현재 보는 위치에 tower가 있으면 재설치가 되지않도록 구현하기 위해 만든bool형 배열이다.

               public GameObject TowerPrefab; 

설치할 타워의 프리펩이다.
               public TextMeshProUGUI money; 

현재 가지고 있는 돈을 표시하기위해 만든 변수이다.
            public TextMeshProUGUI Error; 

여러 경고, 에러등을 표시하기위해 만든 변수이다.
            public int have_money = 1000; 

현재 가지고 있는 돈을 저장하는 변수이다.
                        public GameObject[] TowerPointPosition; 

타워가 설치될 위치를 모두 저장하는 GameObject배열이다.

 

if(isTower[i] == false && Input.GetMouseButtonDown(0) && have_money >= 100) 

만일 현재 타워를 설치할 자리가 비어있고 마우스 좌클을 했고 돈이 100보다 많거나 같으면

Instantiate(TowerPrefab, TowerPointPosition[i].transform.position, TowerPrefab.transform.rotation);

Instantiate는 복제하는 변수이다. 여기서는 생성하는데 쓸거다.

첫번째 인수는 복제할 오브젝트 두번째는 위치 세번째는 회전값이다.

여기서는 타워오브젝트(TowerPrefab)를 복사하고 타워생성위치(TowerPosintPosition)의

위치(position)와 회전값(rotation)을 사용할 것이다.

have_money -= 100;

설치를 했으니 돈을 깎는다.

money.text = ("Money : " + have_money);

현재 남은 돈을 출력

isTower[i] = true;

현재 자리에 이미 타워가 있다고 bool값을 True로 바꾸기

if (isTower[i] == false && Input.GetMouseButtonDown(0) && have_money < 100)

만일 타워가 현재 시선이 향하는 위치에 없고 좌클을 했고 돈이 100보자 작으면

StartCoroutine("Error_Coroutine_0001");

[Error_Coroutine_0001]코루틴을 시작한다.

 

 

여기부터는 [Error_Coroutine_0001] 코루틴 속의 내용이다.

Error.text = ("Not Enough Money (Code 0001)");

돈이 부족하다고 출력하기

yield return new WaitForSeconds(3f);

3초 기다리기

Error.text = ("");

3초후에 지우기

 

 

 

 

 

 

영상