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

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

강의영상 - (1)(2)


개발

Node 준비

타워가 설치될 발판, 노드들을 만들어준다. 하이어라키 창에서 3D Object > Cube를 하나 생성한 후 이름을 Node, 크기를 (4, 1, 4)로 설정한다.

Nodes 오브젝트를 새로 만들어 Node를 자식 오브젝트로 넣어준뒤 Node들을 계속해서 Ctrl+D로 맵을 크게 만들어준다

  • 16 * 16 사이즈로 만들어준다.
  • 모바일 게임이나, 맵의 사이즈가 더 커진다면 플레이 성능에 영향을 줄 수도 있다.
  • 데스크탑 게임이므로 성능적인 면에서는 크게 영향이 없을 것이므로 진행한다.

Increment Snapping을 5로 설정한다음에 Ctrl를 누르고 오브젝트를 옮기면 5 단위로 오브젝트를 옮길 수 있어서 편리하다.

이처럼 16 * 16 노드들의 배치가 끝났으면 해당 씬을 Main Scene이라는 이름으로 저장하자.

경로 제작

적이 생성될 위치와 목표 위치 노드를 제거한 후 적들이 지나갈 경로도 동일하게 제거해준다.

이제 적들이 걸어다닐 땅을 만들어준다. 동일하게 3D Object > Cube를 (4, 1, 4)로 만들어준 후 생성지점에 위치시킨다.

이후 Ground라는 이름으로 새 재질을 만들어 Ground Object에 적용시킨다. Ground의 색과 재질을 조절해 원하는 바닥 재질을 완성한다.

  • 색은 진한 회색으로 설정
  • Smoothness를 0으로 설정

Smoothness : 금속 재질의 광택 정도를 설정, 낮을수록 무광택에 가깝게 설정된다.

자세한 라이팅 이론과 물리기반 랜더에 대해서는 이 문서를 참고하자

이제 Ground Object의 크기를 조절해 길을 채워보자. 위에서 사용했던 Increment Snapping의 scale을 잘 조정해 길이를 쉽게 설정할 수 있다.

이제 Environment라는 빈 오브젝트를 만들어 Ground Object들을 모두 자식으로 넣어준다.

항상 빈 오브젝트에 그룹화 하기 위해 자식 오브젝트들을 넣을때 새로 생성한 빈 오브젝트의 transform을 Reset 시켜주어야 한다.

지금까지 했던 과정을 토대로 Start, End 오브젝트와 재질을 각각 만들어 적용 시킨 후 시작 지점과 끝지점에 위치시킨다. 오브젝트의 scale은 (4, 4, 4)로 설정한다.

이후에 Start, End, Ground Object의 Box Collider을 제거해준 후 저장한다.

메인카메라 조정

카메라가 맵의 상단에서 모든 노드들이 한눈에 들어오도록 Position과 Rotation을 적절히 조절한다.

GroundPlane이라는 Cube오브젝트를 (1000, -1, 1000)의 사이즈로 설정한 후 GroundPlane 재질을 만들어 해당 재질로 설정한다.

y = -1 이어야 플랫폼이 평면 위에 부착되듯이 위치된다.

GroundPlane 재질의 색은 짙은 검은색(Blue 수치를 좀 높게 설정), Metallic을 0.4, Smoothness를 0으로 설정한다.

Metallic : 표면이 얼마나 "금속 같은지"를 결정한다. 표면이 더 금속 같을 수록 환경을 더 많이 반사하고 표면의 알베도 컬러가 덜 보인다.

단순히 Main Camera에서 BackGround 컬러를 수정할 수도 있겠지만 위와 같이 평면 오브젝트를 하나 밑에 색을 입혀 추가하면 위의 노드들의 그림자가 평면 위에 생기면서 시각적인 효과를 줄 수 있다.

Directional Light의 방향을 조절해서 그림자의 크기 및 방향을 조절할 수 있다.

Enemy 오브젝트 생성

3D Object > Sphere를 Enemy 오브젝트로 생성한 후 Enemy 재질을 추가해서 적용시킨다. 설정이 모두 끝났으면 이제 오브젝트를 프리팹화시킨다.

  • 크기는 2 * 2 * 2로 설정한다.
  • 밝은 파랑색으로 색을 설정한다.

WayPoint 제작

이제 적이 경로를 따라가도록 WayPoint를 찍어보자.
Way Point라는 빈 오브젝트를 생성한 후에 아이콘을 붉은 색 루비로 설정해서 Scene 창에서 웨이 포인트가 잘 보이도록 설정한다.

3D 아이콘은 Scene에서만 보이고 실제 인게임에서는 보이지 않는다. 빈 오브젝트의 위치를 씬 창에서 움직이고 확인하고 싶을때 사용하면 좋다.

만약 아이콘이 안보인다면 Toggle Visibility 에서 3D icon의 크기를 확인해보자.

이제 이 WayPoint를 프리팹화 한 다음에 씬을 상단뷰로 바꾸고 코너마다 해당 웨이 포인트를 추가해준다.

움직일 때 Increment Snapping의 Move를 5로 설정하면 한칸씩 정확하게 움직일 수 있다.

스크립트

에너미 스크립트는 현재 자신이 가지고 있는 웨이포인트 index가 가리키고 있는 points를 향해 이동하며 웨이 포인트와의 거리가 0.4f보다 작아지면 인덱스를 증가시키고 목표 오브젝트를 해당 인덱스가 가리키는 웨이포인트로 업데이트한다.
웨이포인트 스크립트는 웨이포인트를 스태틱으로 선언한 후 현재 자신의 자식 오브젝트들을 가져와 선언해둔 배열에 하나씩 할당 시켜준다. 이렇게 컴파일 중에 메모리에 공간이 직접 정적으로 할당되기 때문에 다른 스크립트에서도 해당 데이터를 쉽게 접근해서 사용할 수 있다는 장점이 있다.

WayPoints.cs

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

public class WayPoints : MonoBehaviour
{
    public static Transform[] points;

    void Awake()
    {
        points = new Transform[transform.childCount]; //wayPoint 갯수만큼 배열 초기화
        for (int i = 0; i < points.Length; i++)
        {
            points[i] = transform.GetChild(i); //transform의 갯수만큼 자녀를 가져와 points 배열에 할당시켜준다.
        }
    }
}

Enemy.cs

using UnityEngine;

public class Enemy : MonoBehaviour
{
    public float speed = 10f; //속도

    private Transform target; //목표 방향
    private int wavepointIndex = 0;//현재 목표로하는 웨이포인트 인덱스

    private void Start()
    {
        //WayPoints의 points를 static으로 선언해놨기 때문에 바로 불러올 수 있다.
        //points를 싱글톤 디자인 패턴으로 사용하는 모습
        target = WayPoints.points[0]; 
    }

    void Update()
    {
        //이동해야 할 방향 ( 목표 위치 - 내 위치  )
        Vector3 dir = target.position - transform.position;

        //방향 벡터로 스피드 만큼 이동
        //방향을 단위벡터로 바꾸기 위해 normalized 진행 후 speed를 곱한만큼 진행 (프레임-시간 보정으로 deltaTime을 사용)
        transform.Translate(dir.normalized * speed * Time.deltaTime, Space.World); //World Space에서 이동


        //웨이포인트 도착 시 다음 웨이포인트로 변경
        if (Vector3.Distance(transform.position, target.position) <= 0.4f) // 웨이 포인트와 에너미의 거리가 0.4 이하면 다음 웨이 포인트로
        {
            GetNextWayPoint(); //다음 웨이포인트를 타겟으로 변경
        } 
    }

    void GetNextWayPoint()
    {
        if (wavepointIndex >= WayPoints.points.Length - 1) //만약 가지고 있는 모든 웨이포인트를 방문 > 도착 지점 도달
        {
            Destroy(gameObject); //도착지점 도착 시 오브젝트 파괴
            return; //아래의 다음 웨이포인트 가져오는 것을 하지 않고 바로 종료
        }

        wavepointIndex++; //다음 웨이포인트 인덱스
        target = WayPoints.points[wavepointIndex]; //다음 인덱스의 웨이포인트 오브젝트를 받아온다.
    }
}

결과물

profile
Be Honest, Be Harder, Be Stronger

0개의 댓글