[Unity][3D-Game] Tower Defense Game (6)

suhan0304·2023년 11월 30일
0
post-thumbnail

강의영상 (8)


개발

개발을 시작하기에 앞서 UI 캔버스의 Pixel Perfect를 체크해주자. 픽셀과 정렬하도록 캔버스 요소를 강제할 수 있다.(renderMode가 Screen Space인 경우에만 작동) 이를 사용할 경우, 요소가 선명하게 표시되고 블러를 방지할 수 있다.

많은 요소가 스케일링, 회전, 미세한 위치 및 스케일을 변경하는 애니메이션을 사용하는 경우에는 PixelPerfect를 해제하는 편이 움직임이 더 매끄러워진다.

캔버스의 자세한 파라미터 설명은 공식 문서를 참고하자, Pixel Perfect에 대한 설명 또한 공식 문서를 참고하자.

상점 UI

캔버스에 Panel 오브젝트를 하나 생성한 후 하단의 상점이 보일정도로 적절한 크기로 조정해준다. 크기를 조정해준다음에 위치는 앵커 설정으로 설정해주자. (해상도가 바뀌어도 상점이 하단에 붙어있도록 해주기 위함)

  • 패널의 이름은 Shop으로 설정
  • 패널의 이미지는 비활성화 (투명한 배경을 위해서)
  • 버튼이 가로로 정렬되로고 Horizontal LayOutGroup을 추가
    - spacing을 25로 설정해서 버튼 간 간격이 25 픽셀로 설정되도록 한다.
    - 버튼이 가운데 정렬되도록 Child Alignment를 Middle Center로 설정한다.
    - 버튼의 위치만 조정되도록 사이즈 확장을 막기 위해 Child Force Expand를 체크 해제해준다.

이제 ShopTurretItem이라는 이름의 버튼을 패널의 자식 오브젝트로 생성해준다. 버튼 오브젝트에 Layout Element라는 컴포넌트를 추가해준 후 Preferred width, Preferred Height를 각각 200, 200으로 설정해준다.

  • 버튼의 경우 Navgation을 None으로 설정한다.

Preferred With와 Height 사이즈로 버튼의 크기가 조절되기 위해서는 Horizontal Layout Group의 Control Child Size 속성을 체크해야한다.

만약 레이아웃 그룹이 레이아웃 원소들의 크기를 제어하지 못하도록 하려면 체크를 해제하고 자체적으로 크기를 설정하면 된다.

버튼 아이콘 이미지 설정

포탑 이미지를 아이콘에 사용하기 위해 인스펙터 하단에서 이미지를 캡처해서 배경 이미지를 제거후에 사용해도 된다.

위 와같이 배경을 제거한 사진을 아이콘을 사용해보자. 배경을 지운 사진(PNG) 파일이나 포토샵 파일(PSD)를 유니티 프로젝트 창에 드래그해서 임포트시킬 수 있다.

임포트 시킨 파일의 Texture Type을 Sprite (2D and UI)로 변경해주어야 이미지 파일을 2D 스프라이트로 읽어와 정상적인 사용이 가능하다.

이제 버튼의 이미지의 Source Image를 터렛 이미지로 변경해주자.

이제 다 만들어진 버튼을 프리팹화 시킨다.

상점 로직 구현

Shop 스크립트, BuildManager 스크립트, Node 스크립트를 수정해서 버튼을 눌러서 타워를 선택해야만 노드의 하이라이트, 건물 건설이 작동하도록 로직을 수정한다.

Shop.cs

using UnityEngine;

public class Shop : MonoBehaviour
{
    BuildManager buildManager;

    private void Start() //빌드 매니저 참조를 위해 instance 초기화
    {
        buildManager = BuildManager.instance;
    }
    public void PurchaseStandardTurret() //기본 터렛 건설
    {
        Debug.Log("Standard Turret Selected"); //테스트용 출력
        buildManager.SetTurretToBuild(buildManager.standardTurretPrefab); //기본 터렛을 건설하도록 설정
    }
    public void PurchaseAnotherTurret() //다른 터렛 건설
    {
        Debug.Log("Another Turret Selected"); //테스트용 출력
        buildManager.SetTurretToBuild(buildManager.anotherTurretPrefab); //다른 터렛을 건설하도록 설정
    }
}

BuildManager.cs

using UnityEngine;

public class BuildManager : MonoBehaviour
{
    public static BuildManager instance; //싱글톤 패턴으로 빌드 매니저를 선언

    private void Awake()
    {
        if (instance != null)  //선언된 적 있으면 더 이상 선언 X
        {
            // 이미 빌드 매니저 인스턴스가 존재
            Debug.LogError("More than one BuildManager in scene!");
            return;
        }
        //게임이 시작하면 새로운 빌드 매니저를 instance에 저장.
        //빌드 매니저는 하나의 인스턴스로만 유지됨 (싱글톤 패턴의 특징 : 하나의 인스턴스만 유지)
        instance = this;
    }

    public GameObject standardTurretPrefab; //기본 터렛 프리팹
    public GameObject anotherTurretPrefab; //다른 터렛 프리팹

    private GameObject turretToBuild; //노드 선택 시 건설할 터렛

    public GameObject GetTurretToBuild() //건설할 터렛을 가져오는 함수
    {
        return turretToBuild;
    }

    public void SetTurretToBuild(GameObject turret)//건설할 터렛을 선택
    {
        turretToBuild = turret;
    }
}

Node.cs

using UnityEngine;

public class Node : MonoBehaviour
{
    public Color hoverColor; //색
    public Vector3 positionOffset;

    private GameObject turret;

    private Renderer rend;
    private Color startColor;

    BuildManager buildManager;

    private void Start()
    {
        rend = GetComponent<Renderer>();//게임이 시작될 때 렌더러를 미리 저장
        startColor = rend.material.color; //시작 색을 저장 후 기억

        buildManager = BuildManager.instance;
    }

    private void OnMouseDown()
    {
        if (buildManager.GetTurretToBuild() == null) //건설할 터렛이 null (건설할 터렛을 선택하지 않으면)
            return;                                  //노드를 클릭해도 터렛을 건설하지 않고 그냥 return 시킴

        if(turret != null) //터렛 오브젝트가 null이 아니면 이미 터렛이 있다는 모습
        {
            Debug.Log("Can't build there! - TODO : Display on screen.");
            return;
        }

        //Build a turret
        GameObject turretBuild = BuildManager.instance.GetTurretToBuild(); //빌드 매니저를 바로 호출 가능(싱그톤)
        
        //노드 위치에 건설 시 노드와 동일한 Position에 겹쳐서 생성됨 ( 노드 내부에 터렛이 위치함 )
        //높이 오프셋을 선언해 준 후 위치 벡터에 더해서 초기 생성 위치를 조정해준다. -> Offset은 노드 프리팹의 인스펙터에서 조정 가능 
        turret =  (GameObject)Instantiate(turretBuild, transform.position + positionOffset, transform.rotation); //건설할 터렛을 복사 한 후 turret 변수에 초기화
    }

    private void OnMouseEnter() //마우스가 오브젝트 충돌체에 지나가거나 들어갈 때
    {
        if (buildManager.GetTurretToBuild() == null) //건설할 터렛이 null (건설할 터렛을 선택하지 않으면)
            return;                                  //노드에 마우스가 올라와도 하이라이트 시키지 않고 그냥 return 시킴

        //렌더러를 매번 마우스가 들어갈 때마다 아래와 같이 찾는 것은 성능 낭비 -> 게임 시작에서 한 번만 찾고 저장
        //GetComponent<Renderer>().material.color = hoverColor;

        //Start에서 저장된 렌더러를 호출해서 색을 변경
        rend.material.color = hoverColor;
    }

    private void OnMouseExit() //마우스가 오브젝트에서 나갈 때
    {
        rend.material.color = startColor; //startColor로 되돌리기
    }
}

노드-버튼 중첩 오류 해결

버튼을 누른 상태에서 버튼 위에 마우스가 있으면 버튼과 노드가 같이 하이라이트 표시되는 현상을 아래와 같이 작성해 해결할 수 있다.

Node.cs

using UnityEngine.EventSystems;

private void OnMouseEnter() //마우스가 오브젝트 충돌체에 지나가거나 들어갈 때
{
    if (EventSystem.current.IsPointerOverGameObject())
        return;
	~~(생략)~~
}

IsPointerOverGameObject
: pointer(마우스)가 UI에 있는 경우 True를 아닌 경우에는 false를 반환한다.


결과물

profile
Be Honest, Be Harder, Be Stronger

0개의 댓글