Unity チュートリアルのタワーディフェンステンプレートを触ってみる(7)

Unity チュートリアルのタワーディフェンステンプレートを触ってみる(6) では Tower.cs の継承元となっている Targetable.cs スクリプトの解説を行った。今回は Tower.cs の解説を行っていく。
「Tower.cs」 は 「Targetable.cs」 を継承し、「Targetable.cs」 は 「DamageableBehaviour.cs」 を継承している。
前回は「Targetable.cs」の行った。「Tower.cs」 を行っていく。Targetable.cs の解説についてはこちらを参照してほしい。
1.タワー編 – Tower.cs
Tower.cs について稚拙ながら解説
「Tower.cs」は「Asset/Scripts/TowerDefense/Towers/Tower.cs」の指しておりスクリプトについては以下の通り。内容としてはタワーのアップグレード・ダウングレード・売却の処理を主に行っている。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299 using System;
using ActionGameFramework.Health;
using Core.Utilities;
using TowerDefense.Level;
using TowerDefense.Towers.Placement;
using TowerDefense.UI.HUD;
using UnityEngine;
namespace TowerDefense.Towers
{
/// <summary>
/// Common functionality for all types of towers
/// </summary>
public class Tower : Targetable
{
/// <summary>
/// The tower levels associated with this tower
/// </summary>
public TowerLevel[] levels;
/// <summary>
/// A generalised name common to a levels
/// </summary>
public string towerName;
/// <summary>
/// The size of the tower's footprint
/// </summary>
public IntVector2 dimensions;
/// <summary>
/// The physics mask the tower searches on
/// </summary>
public LayerMask enemyLayerMask;
/// <summary>
/// The current level of the tower
/// </summary>
public int currentLevel { get; protected set; }
/// <summary>
/// Reference to the data of the current level
/// </summary>
public TowerLevel currentTowerLevel { get; protected set; }
/// <summary>
/// Gets whether the tower can level up anymore
/// </summary>
public bool isAtMaxLevel
{
get { return currentLevel == levels.Length - 1; }
}
/// <summary>
/// Gets the first level tower ghost prefab
/// </summary>
public TowerPlacementGhost towerGhostPrefab
{
get { return levels[currentLevel].towerGhostPrefab; }
}
/// <summary>
/// Gets the grid position for this tower on the <see cref="placementArea"/>
/// </summary>
public IntVector2 gridPosition { get; private set; }
/// <summary>
/// The placement area we've been built on
/// </summary>
public IPlacementArea placementArea { get; private set; }
/// <summary>
/// The purchase cost of the tower
/// </summary>
public int purchaseCost
{
get { return levels[0].cost; }
}
/// <summary>
/// The event that fires off when a player deletes a tower
/// </summary>
public Action towerDeleted;
/// <summary>
/// The event that fires off when a tower has been destroyed
/// </summary>
public Action towerDestroyed;
/// <summary>
/// Provide the tower with data to initialize with
/// </summary>
/// <param name="targetArea">The placement area configuration</param>
/// <param name="destination">The destination position</param>
public virtual void Initialize(IPlacementArea targetArea, IntVector2 destination)
{
placementArea = targetArea;
gridPosition = destination;
if (targetArea != null)
{
transform.position = placementArea.GridToWorld(destination, dimensions);
transform.rotation = placementArea.transform.rotation;
targetArea.Occupy(destination, dimensions);
}
SetLevel(0);
if (LevelManager.instanceExists)
{
LevelManager.instance.levelStateChanged += OnLevelStateChanged;
}
}
/// <summary>
/// Provides information on the cost to upgrade
/// </summary>
/// <returns>Returns -1 if the towers is already at max level, other returns the cost to upgrade</returns>
public int GetCostForNextLevel()
{
if (isAtMaxLevel)
{
return -1;
}
return levels[currentLevel + 1].cost;
}
/// <summary>
/// Kills this tower
/// </summary>
public void KillTower()
{
// Invoke base kill method
Kill();
}
/// <summary>
/// Provides the value recived for selling this tower
/// </summary>
/// <returns>A sell value of the tower</returns>
public int GetSellLevel()
{
return GetSellLevel(currentLevel);
}
/// <summary>
/// Provides the value recived for selling this tower of a particular level
/// </summary>
/// <param name="level">Level of tower</param>
/// <returns>A sell value of the tower</returns>
public int GetSellLevel(int level)
{
// sell for full price if waves haven't started yet
if (LevelManager.instance.levelState == LevelState.Building)
{
int cost = 0;
for (int i = 0; i <= level; i++)
{
cost += levels[i].cost;
}
return cost;
}
return levels[currentLevel].sell;
}
/// <summary>
/// Used to (try to) upgrade the tower data
/// </summary>
public virtual bool UpgradeTower()
{
if (isAtMaxLevel)
{
return false;
}
SetLevel(currentLevel + 1);
return true;
}
/// <summary>
/// A method for downgrading tower
/// </summary>
/// <returns>
/// <value>false</value> if tower is at lowest level
/// </returns>
public virtual bool DowngradeTower()
{
if (currentLevel == 0)
{
return false;
}
SetLevel(currentLevel - 1);
return true;
}
/// <summary>
/// Used to set the tower to any valid level
/// </summary>
/// <param name="level">
/// The level to upgrade the tower to
/// </param>
/// <returns>
/// True if successful
/// </returns>
public virtual bool UpgradeTowerToLevel(int level)
{
if (level < 0 || isAtMaxLevel || level >= levels.Length)
{
return false;
}
SetLevel(level);
return true;
}
public void Sell()
{
Remove();
}
/// <summary>
/// Removes tower from placement area and destroys it
/// </summary>
public override void Remove()
{
base.Remove();
placementArea.Clear(gridPosition, dimensions);
Destroy(gameObject);
}
/// <summary>
/// unsubsribe when necessary
/// </summary>
protected virtual void OnDestroy()
{
if (LevelManager.instanceExists)
{
LevelManager.instance.levelStateChanged += OnLevelStateChanged;
}
}
/// <summary>
/// Cache and update oftenly used data
/// </summary>
protected void SetLevel(int level)
{
if (level < 0 || level >= levels.Length)
{
return;
}
currentLevel = level;
if (currentTowerLevel != null)
{
Destroy(currentTowerLevel.gameObject);
}
// instantiate the visual representation
currentTowerLevel = Instantiate(levels[currentLevel], transform);
// initialize TowerLevel
currentTowerLevel.Initialize(this, enemyLayerMask, configuration.alignmentProvider);
// health data
ScaleHealth();
// disable affectors
LevelState levelState = LevelManager.instance.levelState;
bool initialise = levelState == LevelState.AllEnemiesSpawned || levelState == LevelState.SpawningEnemies;
currentTowerLevel.SetAffectorState(initialise);
}
/// <summary>
/// Scales the health based on the previous health
/// Requires override when the rules for scaling health on upgrade changes
/// </summary>
protected virtual void ScaleHealth()
{
configuration.SetMaxHealth(currentTowerLevel.maxHealth);
if (currentLevel == 0)
{
configuration.SetHealth(currentTowerLevel.maxHealth);
}
else
{
int currentHealth = Mathf.FloorToInt(configuration.normalisedHealth * currentTowerLevel.maxHealth);
configuration.SetHealth(currentHealth);
}
}
/// <summary>
/// Intiailises affectors based on the level state
/// </summary>
protected virtual void OnLevelStateChanged(LevelState previous, LevelState current)
{
bool initialise = current == LevelState.AllEnemiesSpawned || current == LevelState.SpawningEnemies;
currentTowerLevel.SetAffectorState(initialise);
}
}
}
14行目 : 「public class Tower : Targetable」これは前回解説した「Targetable」を継承していることを指している。「Targetable」についてはこちらを参照してほしい。
95行目 : 「public virtual void Initialize(IPlacementArea targetArea, IntVector2 destination)」では「Tower」の初期化を行っている。引数としては配置可能エリアと配置先を引数としている。
118行目 : 「public int GetCostForNextLevel()」では次のレベルアップにかかるコストの取得を行っている。
130行目 : 「public void KillTower()」では継承元である「DamageableBehaviour」の Kill 関数を用いて自身のタワーオブジェクトの削除を行っている。
140行目 : 「public int GetSellLevel()」ではタワーの販売価格を取得している。また、この関数は以下の「public int GetSellLevel(int level)」に現在のレベルを与えることで販売価格の計算を行っている。
150行目 : 「public int GetSellLevel(int level)」ではレベルに応じたタワーの販売価格を取得している。
169行目 : 「public virtual bool UpgradeTower()」ではタワーのアップグレードを行っている。この時すでに最大レベルとなっている場合はアップグレードは行われない。
185行目 : 「public virtual bool DowngradeTower()」ではタワーのダウングレードを行っている。この時、最小レベルの場合はダウングレードは行われない。
204行目 : 「public virtual bool UpgradeTowerToLevel(int level)」では任意のレベルまでアップグレードを行っている。この時すでにタワーが任意のレべルまで達していた場合はアップグレードをは行われない。
214行目 : 「public void Sell()」ではタワーの削除を行っている。内容については以下の「public override void Remove()」と同様
222行目 : 「public override void Remove()」では削除処理を行っている。「DamageableBehaviour」の Remove 関数を実行後、配置しているエリアのクリアとゲームオブジェクトの削除を行っている。
233行目 : 「protected virtual void OnDestroy()」ではタワーのゲームオブジェクトが Destroy された時の処理を行っている。LevelManager クラスに状態を設定しているのだが、こちらについては「LevelManager 」の設定が出てきた時にまとめて解説したいと思う。
244行目 : 「protected void SetLevel(int level)」では既存のデータから引き続きプレイするときに用いる。保存しているレベルを与えることで続きから行えるようになる。
275行目 : 「protected virtual void ScaleHealth()」でが体力ゲージの設定を行っている。
293行目 : 「protected virtual void OnLevelStateChanged(LevelState previous, LevelState current)」ではレベルが変更となった時の処理を定義している。引数として前のレベルと現在のレベルを与え、「TowerLevel」の設定を行っている。「TowerLevel」についてはこちらを参照してほしい。