0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Learn how to create a 2D Tower Defense Game in Unity Section3 学習メモ

Last updated at Posted at 2023-03-12

概要

Udemyの講座「Learn how to create a 2D Tower Defense Game in Unity」Section3の学習メモです。
動きを把握するため、クラス図を書いたり実際に動かした際の試行錯誤を記録に残しています。

この講座を選択した理由としては
本講座は英語ですが、Section毎にUnityプロジェクトフォルダが貼られています。なので実際に動かしやすいというメリットがあります。実際に動かせば大抵のことはわかります。

開発環境

IDE:Rider
Unity:2020.3.42(LTS)
OS:Windows10

実装する動き

タワーデフェンスでは敵が特定の経路に沿って移動します。
今回はその経路と敵スポーン機能を実装します。
image.png

1.敵の経路を作成する→Gizmoクラス
2.Editorのみ見れる自作UI→Editorクラスを作る(赤丸部分と黄色数字の部分)
3.敵がスポーンする機能

クラス図

image.png

クラス名 説明
Waypoint Enemyの移動経路を定義しているクラス。Gizmosクラスを使っている。
WaypointEditor WaypointクラスのUnityEditor上で表示できるuiを追加。(開発しやすくするために作っている)
Spawner 敵が発生することを定義するクラス。ランダムのタイミングでスポーンするメソッドがある。
ObjectPooler 敵の発生を管理するクラス

実装

UnityEditor上の設定

image.png

オブジェクト名 説明
Spawner Spawner、Waypointスクリプトがアタッチされている。
Pool-Quad 敵のスポーン状態を管理するオブジェクト。敵の数がスポーンするたびにQuad(Clone)オブジェクトをonにする。

コード部分

Waypoint

Waypoint.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Waypoint : MonoBehaviour
{
    [SerializeField] private Vector3[] points;

    public Vector3[] Points => points;
    public Vector3 CurrentPosition => _currentPosition;
    
    private Vector3 _currentPosition;
    private bool _gameStarted;
    
    // Start is called before the first frame update
    private void Start()
    {
        _gameStarted = true;
        _currentPosition = transform.position;
    }

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

    private void OnDrawGizmos()
    {
        Debug.Log("<color=blue>WaypointクラスのOnDrawGizmosが実行されました。</color>");
        if (!_gameStarted && transform.hasChanged)
        {
            _currentPosition = transform.position;
        }
        
        for (int i = 0; i < points.Length; i++)
        {
            Gizmos.color = Color.green;
            Gizmos.DrawWireSphere(points[i] + _currentPosition, 0.5f);

            if (i < points.Length - 1)
            {
                Gizmos.color = Color.gray;
                Gizmos.DrawLine(points[i] + _currentPosition, points[i + 1] + _currentPosition);
            }
        }
    }
}

WaypointEditor

WaypointEditor.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(Waypoint))]
public class WaypointEditor : Editor
{
    Waypoint Waypoint => target as Waypoint;
    private void OnSceneGUI()
    {
        Debug.Log("<color=purple>WaypointEditorクラスのWaypointEditorが実行されました。</color>");
        Handles.color = Color.red;
        for (int i = 0; i < Waypoint.Points.Length; i++)
        {
            EditorGUI.BeginChangeCheck();
            
            // Create Handles
            Vector3 currentWaypointPoint = Waypoint.CurrentPosition + Waypoint.Points[i];
            Vector3 newWaypointPoint = Handles.FreeMoveHandle(currentWaypointPoint,
                Quaternion.identity, 0.7f, 
                new Vector3(0.3f, 0.3f, 0.3f), Handles.SphereHandleCap);
            
            // Create text
            GUIStyle textStyle = new GUIStyle();
            textStyle.fontStyle = FontStyle.Bold;
            textStyle.fontSize = 16;
            textStyle.normal.textColor = Color.yellow;
            Vector3 textAlligment = Vector3.down * 0.35f + Vector3.right * 0.35f;
            Handles.Label(Waypoint.CurrentPosition + Waypoint.Points[i] + textAlligment, 
                $"{i + 1}", textStyle);
            EditorGUI.EndChangeCheck();

            if (EditorGUI.EndChangeCheck())
            {
                Undo.RecordObject(target, "Free Move Handle");
                Waypoint.Points[i] = newWaypointPoint - Waypoint.CurrentPosition;
            }
        }
        
    }
}

Spawner

Spawner.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;

public enum SpawnModes
{
    Fixed,
    Random
}

public class Spawner : MonoBehaviour
{
    [Header("Settings")]
    [SerializeField] private SpawnModes spawnMode = SpawnModes.Fixed;
    [SerializeField] private int enemyCount = 10;
    [SerializeField] private GameObject testGO;
    
    [Header("Fixed Delay")]
    [SerializeField] private float delayBtwSpawns;
    
    [Header("Random Delay")]
    [SerializeField] private float minRandomDelay;
    [SerializeField] private float maxRandomDelay;

    private float _spawnTimer;
    private int _enemiesSpawned;

    private ObjectPooler _pooler;

    private void Start()
    {
        _pooler = GetComponent<ObjectPooler>();
    }

    private void Update()
    {
        _spawnTimer -= Time.deltaTime;
        if (_spawnTimer < 0)
        {
            _spawnTimer = GetSpawnDelay();
            if (_enemiesSpawned < enemyCount)
            {
                _enemiesSpawned++;
                SpawnEnemy();
            }
        }
    }

    private void SpawnEnemy()
    {
        Debug.Log("<color=yellow>SpawnerクラスのSpawnEnemyが実行されました。</color>");
        GameObject newInstance = _pooler.GetInstanceFromPool();
        newInstance.SetActive(true);
    }

    private float GetSpawnDelay()
    {
        Debug.Log("<color=yellow>SpawnerクラスのGetSpawnDelayが実行されました。</color>");
        float delay = 0f;
        if (spawnMode == SpawnModes.Fixed)
        {
            delay = delayBtwSpawns;
        }
        else
        {
            delay = GetRandomDelay();
        }

        return delay;
    }
    
    private float GetRandomDelay()
    {
        Debug.Log("<color=yellow>SpawnerクラスのGetRandomDelayが実行されました。</color>");
        float randomTimer = Random.Range(minRandomDelay, maxRandomDelay);
        return randomTimer;
    }
}

ObjectPooler

ObjectPooler.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ObjectPooler : MonoBehaviour
{
    [SerializeField] private GameObject prefab;
    [SerializeField] private int poolSize;

    private List<GameObject> _pool;
    private GameObject _poolContainer;

    private void Awake()
    {
        _pool = new List<GameObject>();
        _poolContainer = new GameObject($"Pool - {prefab.name}");
        
        CreatePooler();
    }

    private void CreatePooler()
    {
        Debug.Log("<color=green>ObjectPoolerクラスのCreatePoolerが実行されました。</color>");
        for (int i = 0; i < poolSize; i++)
        {
            _pool.Add(CreateInstance());
        }
    }
    
    private GameObject CreateInstance()
    {
        Debug.Log("<color=green>ObjectPoolerクラスのCreateInstanceが実行されました。</color>");
        GameObject newInstance = Instantiate(prefab);
        newInstance.transform.SetParent(_poolContainer.transform);
        newInstance.SetActive(false);
        return newInstance;
    }

    public GameObject GetInstanceFromPool()
    {
        Debug.Log("<color=green>ObjectPoolerクラスのGetInstanceFromPoolが実行されました。</color>");
        for (int i = 0; i < _pool.Count; i++)
        {
            if (!_pool[i].activeInHierarchy)
            {
                return _pool[i];
            }
        }
        
        return CreateInstance();
    }
}

動かして確認

gifアニメ

ゲームウィンドウのときはOnDrawGizmosメソッドは実行されませんが、
シーンに切り替えると実行されます。
towerSection345.gif

ログ

image.png

上のログから以下のことがわかります。
ObjectPooloer.CreatePoolerとObjectPooler.CreateInstance :ゲーム開始時に実行Poolオブジェクトを作成

Spawner.GetSpawnDelay:Poolerがオンオフ関わらず実行される
Spawner.SpawnEnemy、ObjectPooler.GetInstanceFromPool:Poolerをオンにする。上限値が10回なので呼び出しも10回
Waypoint.OnDrawGizmos:Gizmosを描画するメソッド

参考

Section3
https://www.udemy.com/course/learn-how-to-create-a-2d-tower-defense-game-in-unity-2020/

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?