FPS서바이벌 디펜스

26.건축불가

ruripanda 2025. 6. 2. 16:09

본 강좌는 케이디님의 유튜브강좌 영상을 보고 유니티6로 구현한 것 입니다 이렇게 멋진 강좌를 만드신 케이디님을 존경합니다

using System;
using UnityEngine;

[System.Serializable]
public class Craft
{
    public string craftName;            //이름
    public GameObject go_Prefab;        //실제 설치 프리팹
    public GameObject go_PreviewPrefab; //미리보기 프리팹
}
public class Craft_Manual : MonoBehaviour
{
    //상태변수
    private bool isActivated = false;
    private bool isPreviewActivated = false;

    [SerializeField]
    private GameObject go_BaseUI;//기본 베이스 UI

    [SerializeField]
    private Craft[] craft_fire;//모닥불용 탭

    private GameObject go_Preview;//미리보기 프리팹을 담을 변수
    private GameObject go_Prefab;//실제 생성될 모닥불을 담을 변수

    [SerializeField]
    private Transform tf_Player;//플레이어 위치

    //Raycast 필요변수 선언
    private RaycastHit hitInfo;
    [SerializeField]
    private LayerMask layerMask;
    [SerializeField]
    private float range;

    public void SlotClick(int _slotNumber)
    {
        go_Preview = Instantiate(craft_fire[_slotNumber].go_PreviewPrefab, tf_Player.position + tf_Player.forward, Quaternion.identity);
        go_Prefab = craft_fire[_slotNumber].go_Prefab;
        isPreviewActivated = true;
        go_BaseUI.SetActive(false);
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Tab) && !isPreviewActivated)
            Window();
        if (isPreviewActivated)
            PreviewPositionUpdate();
        if (Input.GetButtonDown("Fire1"))
            Build();
        if (Input.GetKeyDown(KeyCode.Escape))
            Cancel();
    }

    private void Build()
    {
        if (isPreviewActivated && go_Preview.GetComponent<PreviewObject>().IsBuildable())
        {
            Instantiate(go_Prefab, hitInfo.point, Quaternion.identity);
            Destroy(go_Preview);
            isActivated = false;
            isPreviewActivated = false;
            go_Preview = null;
            go_Prefab = null;
        }
    }

    private void PreviewPositionUpdate()
    {
        if (Physics.Raycast(tf_Player.position, tf_Player.forward, out hitInfo, range, layerMask))
        {
            if(hitInfo.transform != null)
            {
                Vector3 _location = hitInfo.point;
                go_Preview.transform.position = _location;
            }
        }
    }
    private void Cancel()
    {
        if(isPreviewActivated)
            Destroy(go_Preview);

        isActivated = false;
        isPreviewActivated= false;
        go_Preview = null;
        go_Prefab = null;

        go_BaseUI.SetActive(false);
    }

    private void Window()
    {
        if (!isActivated)
            OpenWindow();
        else
            CloseWindow();
    }

    private void OpenWindow()
    {
        isActivated = true;
        go_BaseUI.SetActive(true);
    }

    private void CloseWindow()
    {
        isActivated = false;
        go_BaseUI.SetActive(false);
    }
}
using System.Collections.Generic;
using UnityEngine;

public class PreviewObject : MonoBehaviour
{
    //충돌한 오브젝트의 컬라이더
    private List<Collider> colliderList = new List<Collider>();

    [SerializeField] private int layerGround; //지상레이어
    private const int IGNORE_RAYCAST_LAYER = 2;

    [SerializeField] private Material green;
    [SerializeField] private Material red;

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

    private void ChangeColor()
    {
        if (colliderList.Count > 0)
            SetColor(red);
        else
            SetColor(green);
    }

    private void SetColor(Material mat)
    {
        foreach(Transform tf_Child in this.transform)
        {
            var newMaterials = new Material[tf_Child.GetComponent<Renderer>().materials.Length];

            for(int i =0; i < newMaterials.Length; i++)
            {
                newMaterials[i] = mat;
            }

            tf_Child.GetComponent<Renderer>().materials = newMaterials;
        }
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.layer != layerGround && other.gameObject.layer != IGNORE_RAYCAST_LAYER)
            colliderList.Add(other);
    }

    private void OnTriggerExit(Collider other)
    {
        if (other.gameObject.layer != layerGround && other.gameObject.layer != IGNORE_RAYCAST_LAYER)
            colliderList.Remove(other);
    }

    public bool IsBuildable()
    {
        return colliderList.Count == 0;
    }
}


이번시간에는 스타크래프트 처럼 건설이 불가능한 곳에 건설을 할 경우 건설할 이미지가 빨간색으로 바뀌는 것을 구현할 것이다

스크립트가 복잡하니 차근차근 따라하자

 

일단 C# 스크립트를 만들고 PreviewObject 라고 명명하자

 

기본 변수목록이다

7번은 우리가 건할 할 지역에 만약 오브젝트가 있을 경우 이걸 받아올 변수를 작성한 것이다

 

9번은 인스펙터에서 레이어를 받아올 변수를 작성한 것이다

 

12,13번은 인스펙터에서 빨간색과 초록색 머터리얼을 받아올 것을 작성한 것이다

 

Update함수이다

18번에 ChangeColor() 함수를 호출할 것이다

 

ChangeColor()함수이다

23번에 if문으로 colliderList가 0보다 높을 경우 SetColor(red)를 호출 할 것이다

else는 SetColor(green)를 호출할 것이다

 

SetColor 함수이다 인수로 Material mat를 받아온다

foreach문으로 Transform tf_Child가 있을때 in this.tranform으로 반복할 것이다

이게 좀 복잡한 반복문인데

쉽게 말해서 tf_Child는 Fire_Preview의 Wall이 4개가 있는데 this.transform은 자신만큼 반복해준다는 의미이다

쉽게 말해 Wall이 4개 있으니 4개만큼 반복한다는 의미

33번줄은 var로 임시로 자동으로 변수를 잡고 new Material[tf_Child.GetComponent<Renderer>().materials.Length]라고 되어 있는데

풀어 쓰자면 새로운 머테리얼[차일드된 wall.Renderer의 컴포넌트를 받아오고.머테리얼들을 센다는 의미이다

35번 줄은 for문으로 i =0이고 i는 newMaterials보다 크고 i를 반복한다

37번에는 newMaterials[i] 만큼 반복하여 mat에 넣어준다

40번 줄은 tf_Child객체에 Renderer을 받아와서 머테리얼을 newMaterials와 같게해준다

 

이 스크립트에서는 OntriggerEnter 함수가 나온다 인수로 Collider other이 있다

이 함수는 무언가가 닿는 동안 호출되는 함수이다

if문으로 layerGround가 아닐때 그리고 이그노어 레이어가 아닐때

colliderList.add(other) 해준다

 

OnTriggerExit함수이다

무언가가 닿았다가 떨어질때 호출되는 함수이다

인수하고 if문의 조건은 같으나

colliderList.Remove를 해준다

 

IsBuilable함수이다

콜라이더 리스트. == 0을 리턴해준다

 

위 함수들은 프리뷰 오브젝트에 넣을 스크립트의 내용들이다

 

이제 Craft_Manual 스크립트를 열어주자

밑줄친 부분만 추가하자

위에 작성한 스크립트가 작동하게 해주는 것이다

이제 PreviewObject 스크립트를 우리가 지난 글에 만들어 놓은 Fire_Preview에 넣어준다

그 뒤 Layer Ground를 12로 세팅하고 Green과 Red를 검색ㅎ해서 넣어준다

그뒤 Box콜라이더를 추가해서 isTrigger을 체크해준다 스크립트와 연동되게 해주는 것이다

 

여기서 질문이 있을 것이다 Layer Ground를 케이디님은 14로 설정했어요 라고 할 것이다

 

필자는 버전이 틀려서 12번에 Terrain을 넣어놨다

 

우리가 만들은 터레인맵에 Layer을 Terrain으로 세팅해두자

 

그리고 장애물로 표시할 부분에 Rigidbody를 넣어두자 이제 이렇게 한다음 테스트를 하면...

 

 

이렇게 장애물에 부딪히면 빨간색으로 표시가 된다

그리고 제작도 안된다