Unity

[타워디펜스] 타워 철거기능 구현

wny0320 2023. 2. 3. 17:39

타워 디펜스에서 타워를 철거하는 기능과 한 종류의 타워만 사용을 방지하여 게임의 재미를 올리기 위해 타워 건설의 자원 값에 가중치를 두었음

 

#철거 기능 구현

#예전에 RepairMode였던 함수를 ManageMode로 수정하고 UI에서 먼저 수리인지 철거인지 정하고 실행
void ManageMode()
    {
        if (manageMode)
        {
            if(Input.GetMouseButton(0))
            {
                Vector3 mousePos = Input.mousePosition;
                Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
                RaycastHit hit;
                if(Physics.Raycast(ray, out hit))
                {
                    manageTarget = hit.transform.gameObject;
                }
                if(manageTarget != null && manageTarget.tag == "Tower")
                {
                    VisualizeStatus();
                }
                if (manageOn && manageTarget.tag == "Tower")
                {
                    if(manageSelectBuffer == (int)manageSelection.RepairMode)
                    {
                        TowerManager targetTowerManager = manageTarget.GetComponent<TowerManager>();
                        targetTowerManager.hp.healthCheck = targetTowerManager.hp.health;
                        //자원 감소 코드
                        //타워 매니저 데미지 타입으로 설정하면 될듯
                    }
                    if(manageSelectBuffer == (int)manageSelection.RemoveMode)
                    {
                        int targetAttackType = manageTarget.GetComponent<TowerManager>().damageType;
                        switch(targetAttackType)
                        {
                            case (int)towerAttackType.기본:
                                GameManager.resource += (int)((towerCostAdder[targetAttackType - 1] + 6) * 0.7);
                                break;

                            case (int)towerAttackType.밀기:
                                GameManager.resource += (int)((towerCostAdder[targetAttackType - 1] + 9) * 0.7);
                                break;

                            case (int)towerAttackType.광역:
                                GameManager.resource += (int)((towerCostAdder[targetAttackType - 1] + 18) * 0.7);
                                break;

                            case (int)towerAttackType.슬로우:
                                GameManager.resource += (int)((towerCostAdder[targetAttackType - 1] + 15) * 0.7);
                                break;
                        }
                        GameObject.Destroy(manageTarget);
                    }
                    manageOn = false;
                }
            }
        }
    }

수리와 철거 중 선택한 이후 철거라면 비용의 70% 반환 후 해당 게임 오브젝트 파괴, 수리라면 체력을 최대 체력으로 설정해주는 코드로 바뀌었다.

 

#건설 기능

void BuildMode()
    {
        if(buildMode)
        {
            switch(choice)
            {
                case (int)towerList.BasicTower:
                    buildMode = false;
                    if (GameManager.resource >= towerCostAdder[choice] + 6)
                    {
                        Instantiate(towerObjList[(int)towerList.BasicTower], transform.position, new Quaternion(0, 0, 0, 0));
                        GameManager.resource -= towerCostAdder[choice] + 6;
                        towerCostAdder[choice] += 3;
                    }
                    choice = -1;
                    break;

                case (int)towerList.PunchTower:
                    buildMode = false;
                    if(GameManager.resource >= towerCostAdder[choice] + 15)
                    {
                        Instantiate(towerObjList[(int)towerList.PunchTower], transform.position, new Quaternion(0, 0, 0, 0));
                        GameManager.resource -= towerCostAdder[choice] + 15;
                        towerCostAdder[choice] += 5;
                    }
                    choice = -1;
                    break;

                case (int)towerList.RangeTower:
                    buildMode = false;
                    if(GameManager.resource >= towerCostAdder[choice] + 9)
                    {
                        Instantiate(towerObjList[(int)towerList.RangeTower], transform.position, new Quaternion(0, 0, 0, 0));
                        GameManager.resource -= towerCostAdder[choice] + 9;
                        towerCostAdder[choice] += 4;
                    }
                    choice = -1;
                    break;

                case (int)towerList.SlowTower:
                    buildMode = false;
                    if (GameManager.resource >= towerCostAdder[choice] + 18)
                    {
                        Instantiate(towerObjList[(int)towerList.SlowTower], transform.position, new Quaternion(0, 0, 0, 0));
                        GameManager.resource -= towerCostAdder[choice] + 18;
                        towerCostAdder[choice] += 6;
                    }
                    choice = -1;
                    break;
            }
        }
    }

건설 기능은 towerCostAdder라는 배열을 하나 생성하여 해당 배열마다 추가된 값을 가지고 있어서 그 자원을 참고하여 설치가 가능한지 판단하도록 수정하였음

 

따로 Manage 버튼을 클릭하면 위와 같이 Repair와 Remove중 선택하게 수정하였음

 

아래는 Player의 전체 소스코드

 

더보기
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Player : MonoBehaviour
{
    //IMove 관련 필드
    float moveSpeed;
    Vector3 inputDir;
    Vector3 moveDir;
    CharacterController controller;

    //Health 관련 필드
    Health health;
    int healthBuffer;

    //Pick 관련 필드
    GameObject Hand = null;
    float standardDistance = 1.2f;

    //피격 관련 필드
    public bool canDamaged = true;
    float unBeatTime = 1.0f;
    Material material;

    //Menu 관련 필드
    GameObject menuUI;
    GameObject selectUI;
    GameObject buildUI;
    GameObject manageUI;
    GameObject manageSelectUI;

    bool isEnabledMenuUI;
    /*bool isEnabledSelectUI;
    bool isEnabledBuildUI;
    bool isEnabledRepairUI;*/

    public int modeSelect;
    public int manageSelect;
    int manageSelectBuffer;
    public bool buildMode;
    public bool manageMode;
    enum towerList
    {
        BasicTower = 0,
        PunchTower = 1,
        RangeTower = 2,
        SlowTower = 3,
    }
    enum modeSelection
    {
        NonSelect = -1,
        BuildMode = 0,
        ManageMode = 1,
    }
    enum manageSelection
    {
        NonSelect = -1,
        RepairMode = 0,
        RemoveMode = 1,
    }
    enum towerAttackType
    {
        기본 = 1,
        밀기 = 2,
        광역 = 3,
        슬로우 = 4,
    }
    [SerializeField]
    GameObject[] towerObjList;
    public int choice;
    public int[] towerCostAdder;

    //repair 관련 필드
    GameObject manageTarget;
    public bool manageOn;
    Text lv;
    Text attackType;
    Text maxHP;
    Text curHP;
    Text power;

    void Start()
    {
        //Move 필드 초기화
        controller = GetComponent<CharacterController>();
        moveSpeed = 0.07f;

        //Health 필드 초기화
        health = GetComponent<Health>();
        healthBuffer = health.healthCheck;

        //UnBeat에 쓰일 필드 초기화
        material = GetComponent<Renderer>().material;

        //repair 필드 초기화
        manageTarget = null;
        manageMode = false;
        manageOn = false;
        manageSelectBuffer = (int)manageSelection.NonSelect;
        lv = GameObject.Find("Lv").GetComponent<Text>();
        attackType = GameObject.Find("AttackType").GetComponent<Text>();
        maxHP = GameObject.Find("Max").GetComponent<Text>();
        curHP = GameObject.Find("Cur").GetComponent<Text>();
        power = GameObject.Find("Power").GetComponent<Text>();

        //Menu에 쓰일 필드 초기화
        menuUI = GameObject.Find("MenuUI");
        selectUI = GameObject.Find("SelectUI");
        buildUI = GameObject.Find("BuildUI");
        manageUI = GameObject.Find("ManageUI");
        manageSelectUI = GameObject.Find("ManageSelectUI");

        buildUI.SetActive(false);
        manageUI.SetActive(false);
        selectUI.SetActive(false);
        manageSelectUI.SetActive(false);
        menuUI.SetActive(false);

        isEnabledMenuUI = false;
        /*isEnabledSelectUI = true;
        isEnabledBuildUI = false;
        isEnabledRepairUI = false;*/

        modeSelect = -1;
        manageSelect = -1;
        buildMode = false;
        towerCostAdder = new int[4];
    }

    void Pick()
    {
        //플레이어 안에 게임 오브젝트를 하나 둠
        //주울 수 있는 오브젝트 List인 PickList 안에 있는 물체에서 제일 가까운 물체의 거리와 Target 오브젝트를 찾음
        float closeDistance = float.PositiveInfinity;
        GameObject Target = null;

        //손에 들고 있지 않은 경우
        if(Hand == null)
        {
            /*foreach (GameObject i in GameManager.PickList)
            //distance에 값을 두기
            {
                //PickList에 있는 물체들로 거리 측정 반복문 
                Vector3 targetPos = i.transform.position;
                Vector3 playerPos = this.transform.position;
                float nowDistacne = Vector3.Magnitude(playerPos - targetPos);
                if (closeDistance > nowDistacne)
                {
                    closeDistance = nowDistacne;
                    Target = i;
                }
            }*/

            //PickList 사용 안한 충돌 물체 구하기
            //기준 거리 내에 있는 Collider 추출해서 목록에 넣기
            Collider[] collider = Physics.OverlapSphere(this.transform.position, standardDistance);
            foreach(Collider i in collider)
            {
                Vector3 targetPos = i.gameObject.transform.position;
                Vector3 playerPos = this.transform.position;
                float nowDistacne = Vector3.Magnitude(playerPos - targetPos);
                if (closeDistance > nowDistacne && (i.gameObject.tag =="Tower"||i.gameObject.tag =="Resource"))
                {
                    closeDistance = nowDistacne;
                    Target = i.gameObject;
                }
            }
            //collider가 IsTrigger든 아니든 줍고 내려놓을 수 있음, 단 플레이어의 위치에 내려놓는다면
            //Trigger가 아닐시 플레이어가 움직일시 밀릴 수 있음

            //기준 거리 안에 있다면(주울 수 있는 거리라면)
            if(closeDistance <= standardDistance)
            {
                //Target의 이름별로 실행
                if (Target.tag == "Tower")
                {
                    Hand = Target;
                    Hand.transform.SetParent(this.transform);
                    Hand.SetActive(false);
                    //renderer가 자식한테도 있어서 타워가 포신이 안지워짐(그거까지 넣기)
                    /*TowerAttackType HandScript = Hand.GetComponentInChildren<TowerAttackType>();
                    TowerManager HandScript2 = Hand.GetComponentInChildren<TowerManager>();
                    HandScript.enabled = false;
                    HandScript2.enabled = false;
                    Renderer Handrenderer = Hand.GetComponent<Renderer>();
                    Handrenderer.enabled = false;*/
                    //오브젝트 콜라이더 제외 비활성화
                    //상속
                }
                else if (Target.tag == "Resource")
                {
                    GameManager.resource++;
                    //GameManager.PickList.Remove(Target);
                    Destroy(Target);
                    //자원 추가
                }
            }
            else
            {
                //기준 거리 밖에 있다면
                //거리가 멀다고 메세지 띄우기?
            }
        }
        else
        {
            //물체를 들고 있는 경우(들 수 있는 물체가 타워만 우선 구현)
            //타워 내려놓기, 설치방법에 따라 함수호출
            //임시로 플레이어 위치에 내려놓기로 설정
            Hand.transform.position = this.transform.position;
            Hand.SetActive(true);
            /*TowerAttackType HandScript = Hand.GetComponentInChildren<TowerAttackType>();
            TowerManager HandScript2 = Hand.GetComponentInChildren<TowerManager>();
            HandScript.enabled = true;
            HandScript2.enabled = true;
            Renderer Handrenderer = Hand.GetComponent<Renderer>();
            Handrenderer.enabled = true;*/
            Hand.transform.SetParent(null);
            Hand = null;
        }
    }

    void OnPick()
    {
        //space가 눌리면 Pick 함수 실행
        if (Input.GetKeyDown(KeyCode.Space))
        {
            Pick();
        }

    }

    public void ManageHP(int amount)
    {
        health.healthCheck += amount;
        // amount는 음수, 양수로 받아 계산
    }


    //무적 코루틴
    IEnumerator UnBeat()
    {
        int blinkTime = (int)(unBeatTime/0.25f);
        for(int i = 0; i < blinkTime; i++)
        {
            if(i%2 == 0)
            {
                material.color = new Color32(255, 100, 0, 255);
            }
            else
            {
                material.color = new Color32(255, 142, 0, 255);
            }
            yield return new WaitForSeconds(0.25f);
        }
        material.color = new Color32(255, 142, 0, 255);

        canDamaged = true;

        yield return null;
    }

    void Menu()
    {
        if (Input.GetKeyDown(KeyCode.B))
        {
            if (isEnabledMenuUI)
            {
                isEnabledMenuUI = false;
                buildUI.SetActive(false);
                manageUI.SetActive(false);
                selectUI.SetActive(false);
                manageSelectUI.SetActive(false);
                menuUI.SetActive(false);
                manageTarget = null;
                manageMode = false;
                modeSelect = (int)modeSelection.NonSelect;
                manageSelectBuffer = (int)manageSelection.NonSelect;
            }
            else
            {
                isEnabledMenuUI = true;
                menuUI.SetActive(true);
                selectUI.SetActive(true);
            }
        }
        if(modeSelect == (int)modeSelection.BuildMode)
        {
            buildUI.SetActive(true);
            selectUI.SetActive(false);
            modeSelect = (int)modeSelection.NonSelect;
        }
        if(modeSelect == (int)modeSelection.ManageMode)
        {
            selectUI.SetActive(false);
            manageSelectUI.SetActive(true);
            if(manageSelect != (int)manageSelection.NonSelect)
            {
                manageSelectUI.SetActive(false);
                manageUI.SetActive(true);
                manageMode = true;
                modeSelect = (int)modeSelection.NonSelect;
                manageSelectBuffer = manageSelect;
                manageSelect = (int)manageSelection.NonSelect;
            }

        }
    }

    void BuildMode()
    {
        if(buildMode)
        {
            switch(choice)
            {
                case (int)towerList.BasicTower:
                    buildMode = false;
                    if (GameManager.resource >= towerCostAdder[choice] + 6)
                    {
                        Instantiate(towerObjList[(int)towerList.BasicTower], transform.position, new Quaternion(0, 0, 0, 0));
                        GameManager.resource -= towerCostAdder[choice] + 6;
                        towerCostAdder[choice] += 3;
                    }
                    choice = -1;
                    break;

                case (int)towerList.PunchTower:
                    buildMode = false;
                    if(GameManager.resource >= towerCostAdder[choice] + 15)
                    {
                        Instantiate(towerObjList[(int)towerList.PunchTower], transform.position, new Quaternion(0, 0, 0, 0));
                        GameManager.resource -= towerCostAdder[choice] + 15;
                        towerCostAdder[choice] += 5;
                    }
                    choice = -1;
                    break;

                case (int)towerList.RangeTower:
                    buildMode = false;
                    if(GameManager.resource >= towerCostAdder[choice] + 9)
                    {
                        Instantiate(towerObjList[(int)towerList.RangeTower], transform.position, new Quaternion(0, 0, 0, 0));
                        GameManager.resource -= towerCostAdder[choice] + 9;
                        towerCostAdder[choice] += 4;
                    }
                    choice = -1;
                    break;

                case (int)towerList.SlowTower:
                    buildMode = false;
                    if (GameManager.resource >= towerCostAdder[choice] + 18)
                    {
                        Instantiate(towerObjList[(int)towerList.SlowTower], transform.position, new Quaternion(0, 0, 0, 0));
                        GameManager.resource -= towerCostAdder[choice] + 18;
                        towerCostAdder[choice] += 6;
                    }
                    choice = -1;
                    break;
            }
        }
    }

    void VisualizeStatus()
    {
        TowerManager targetTowerManager = manageTarget.GetComponent<TowerManager>();
        int targetCurHP = targetTowerManager.hp.health;
        int targetMaxHP = targetTowerManager.hp.healthCheck;
        int targetPower = targetTowerManager.damage;
        int targetAttackType = targetTowerManager.damageType;
        int targetLv = targetTowerManager.towerLevel;
        lv.text = targetLv.ToString();
        curHP.text = targetCurHP.ToString();
        maxHP.text = targetMaxHP.ToString();
        power.text = targetPower.ToString();
        switch(targetAttackType)
        {
            case (int)towerAttackType.기본:
                attackType.text = "기본";
                break;

            case (int)towerAttackType.밀기:
                attackType.text = "밀기";
                break;

            case (int)towerAttackType.광역:
                attackType.text = "광역";
                break;

            case (int)towerAttackType.슬로우:
                attackType.text = "슬로우";
                break;
        }
        if(manageOn)
        {
            
        }
    }
    void ManageMode()
    {
        if (manageMode)
        {
            if(Input.GetMouseButton(0))
            {
                Vector3 mousePos = Input.mousePosition;
                Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
                RaycastHit hit;
                if(Physics.Raycast(ray, out hit))
                {
                    manageTarget = hit.transform.gameObject;
                }
                if(manageTarget != null && manageTarget.tag == "Tower")
                {
                    VisualizeStatus();
                }
                if (manageOn && manageTarget.tag == "Tower")
                {
                    if(manageSelectBuffer == (int)manageSelection.RepairMode)
                    {
                        TowerManager targetTowerManager = manageTarget.GetComponent<TowerManager>();
                        targetTowerManager.hp.healthCheck = targetTowerManager.hp.health;
                        //자원 감소 코드
                        //타워 매니저 데미지 타입으로 설정하면 될듯
                    }
                    if(manageSelectBuffer == (int)manageSelection.RemoveMode)
                    {
                        int targetAttackType = manageTarget.GetComponent<TowerManager>().damageType;
                        switch(targetAttackType)
                        {
                            case (int)towerAttackType.기본:
                                GameManager.resource += (int)((towerCostAdder[targetAttackType - 1] + 6) * 0.7);
                                break;

                            case (int)towerAttackType.밀기:
                                GameManager.resource += (int)((towerCostAdder[targetAttackType - 1] + 9) * 0.7);
                                break;

                            case (int)towerAttackType.광역:
                                GameManager.resource += (int)((towerCostAdder[targetAttackType - 1] + 18) * 0.7);
                                break;

                            case (int)towerAttackType.슬로우:
                                GameManager.resource += (int)((towerCostAdder[targetAttackType - 1] + 15) * 0.7);
                                break;
                        }
                        GameObject.Destroy(manageTarget);
                    }
                    manageOn = false;
                }
            }
        }
    }

    void Move()
    {
        inputDir = Vector3.zero;
        if (Input.GetKey(KeyCode.A)) inputDir += Vector3.left;
        if (Input.GetKey(KeyCode.D)) inputDir += Vector3.right;
        if (Input.GetKey(KeyCode.S)) inputDir += Vector3.back;
        if (Input.GetKey(KeyCode.W)) inputDir += Vector3.forward;
        inputDir.Normalize();

        moveDir = Vector3.MoveTowards(moveDir, inputDir, Time.deltaTime * 5);
        controller.Move(moveDir * moveSpeed);
    }
    void Update()
    {
        Move();
        OnPick();
        Menu();
        BuildMode();
        ManageMode();
    }

    void LateUpdate()
    {
        //체력이 변했는지 체크
        if(healthBuffer != health.healthCheck)
        {
            //체력 변환 체크값을 현재 체력으로 바꿔줌
            healthBuffer = health.healthCheck;
            StartCoroutine("UnBeat");
        }
    }
}