Unity チュートリアルのタワーディフェンステンプレートを触ってみる(19)
Unity チュートリアルのタワーディフェンステンプレートを触ってみる(18)では LaserTower_0 に追加した AttackAffector コンポーネントのインタフェースにある ITowerRadiusProvider の解説を行った。今回は LaserTower_0 に追加した AttackAffector の解説を行っていく。
1.標的設定と砲撃編 – AttackAffector.cs
AttackAffector.cs について稚拙ながら解説
「AttackAffector.cs」は「Assets/Scripts/TowerDefense/Affectors/AttackAffector.cs」の指しておりスクリプトについては以下の通り。内容としては砲撃に関する処理を行っている。
[cce_csharp]using System.Collections.Generic; using ActionGameFramework.Audio; using ActionGameFramework.Health; using Core.Health; using TowerDefense.Targetting; using TowerDefense.Towers; using TowerDefense.Towers.Projectiles; using UnityEngine; namespace TowerDefense.Affectors { /// <summary> /// The common effect for handling firing projectiles to attack /// /// Requires an ILauncher but it is not automatically added /// Add an ILauncher implementation to this GameObject before you add this script /// </summary> [RequireComponent(typeof(ILauncher))] public class AttackAffector : Affector, ITowerRadiusProvider { /// <summary> /// The projectile used to attack /// </summary> public GameObject projectile; /// <summary> /// The list of points to launch the projectiles from /// </summary> public Transform[] projectilePoints; /// <summary> /// The reference to the center point where the tower will search from /// </summary> public Transform epicenter; /// <summary> /// Configuration for when the tower does splash damage /// </summary> public bool isMultiAttack; /// <summary> /// The fire rate in fires-per-second /// </summary> public float fireRate; /// <summary> /// The audio source to play when firing /// </summary> public RandomAudioSource randomAudioSource; /// <summary> /// Gets the targetter /// </summary> public Targetter towerTargetter; /// <summary> /// Color of effect radius visualization /// </summary> public Color radiusEffectColor; /// <summary> /// Search condition /// </summary> public Filter searchCondition; /// <summary> /// Fire condition /// </summary> public Filter fireCondition; /// <summary> /// The reference to the attached launcher /// </summary> protected ILauncher m_Launcher; /// <summary> /// The time before firing is possible /// </summary> protected float m_FireTimer; /// <summary> /// Reference to the current tracked enemy /// </summary> protected Targetable m_TrackingEnemy; /// <summary> /// Gets the search rate from the targetter /// </summary> public float searchRate { get { return towerTargetter.searchRate; } set { towerTargetter.searchRate = value; } } /// <summary> /// Gets the targetable /// </summary> public Targetable trackingEnemy { get { return m_TrackingEnemy; } } /// <summary> /// Gets or sets the attack radius /// </summary> public float effectRadius { get { return towerTargetter.effectRadius; } } public Color effectColor { get { return radiusEffectColor; } } public Targetter targetter { get { return towerTargetter; } } /// <summary> /// Initializes the attack affector /// </summary> public override void Initialize(IAlignmentProvider affectorAlignment) { Initialize(affectorAlignment, -1); } /// <summary> /// Initialises the attack affector with a layer mask /// </summary> public override void Initialize(IAlignmentProvider affectorAlignment, LayerMask mask) { base.Initialize(affectorAlignment, mask); SetUpTimers(); towerTargetter.ResetTargetter(); towerTargetter.alignment = affectorAlignment; towerTargetter.acquiredTarget += OnAcquiredTarget; towerTargetter.lostTarget += OnLostTarget; } void OnDestroy() { towerTargetter.acquiredTarget -= OnAcquiredTarget; towerTargetter.lostTarget -= OnLostTarget; } void OnLostTarget() { m_TrackingEnemy = null; } void OnAcquiredTarget(Targetable acquiredTarget) { m_TrackingEnemy = acquiredTarget; } public Damager damagerProjectile { get { return projectile == null ? null : projectile.GetComponent<Damager>(); } } /// <summary> /// Returns the total projectile damage /// </summary> public float GetProjectileDamage() { var splash = projectile.GetComponent<SplashDamager>(); float splashDamage = splash != null ? splash.damage : 0; return damagerProjectile.damage + splashDamage; } /// <summary> /// Initialise the RepeatingTimer /// </summary> protected virtual void SetUpTimers() { m_FireTimer = 1 / fireRate; m_Launcher = GetComponent<ILauncher>(); } /// <summary> /// Update the timers /// </summary> protected virtual void Update() { m_FireTimer -= Time.deltaTime; if (trackingEnemy != null && m_FireTimer <= 0.0f) { OnFireTimer(); m_FireTimer = 1 / fireRate; } } /// <summary> /// Fired at every poll of the fire rate timer /// </summary> protected virtual void OnFireTimer() { if (fireCondition != null) { if (!fireCondition()) { return; } } FireProjectile(); } /// <summary> /// Common logic when attacking /// </summary> protected virtual void FireProjectile() { if (m_TrackingEnemy == null) { return; } if (isMultiAttack) { List<Targetable> enemies = towerTargetter.GetAllTargets(); m_Launcher.Launch(enemies, projectile, projectilePoints); } else { m_Launcher.Launch(m_TrackingEnemy, damagerProjectile.gameObject, projectilePoints); } if (randomAudioSource != null) { randomAudioSource.PlayRandomClip(); } } /// <summary> /// A delegate to compare distances of components /// </summary> /// <param name="first"></param> /// <param name="second"></param> protected virtual int ByDistance(Targetable first, Targetable second) { float firstSqrMagnitude = Vector3.SqrMagnitude(first.position - epicenter.position); float secondSqrMagnitude = Vector3.SqrMagnitude(second.position - epicenter.position); return firstSqrMagnitude.CompareTo(secondSqrMagnitude); } #if UNITY_EDITOR /// <summary> /// Draws the search area /// </summary> void OnDrawGizmosSelected() { Gizmos.DrawWireSphere(epicenter.position, towerTargetter.effectRadius); } #endif } /// <summary> /// A delegate for boolean calculation logic /// </summary> public delegate bool Filter(); }[/cce_csharp]
18行目 : 「[RequireComponent(typeof(ILauncher))]」は ILauncher が必須であることを指している。ILaucher については次回以降に解説を行うが ILaucher はインタフェースであるため、クラスとして追加することはできない。ここでの ILauncher が必須であるというのは ILauncher をインタフェースとして持つクラスが必須であるという意味である。
19行目 : 「public class AttackAffector : Affector, ITowerRadiusProvider」は前回および前々回解説を行った Affector を継承し、ITowerRadiusProvider をインタフェースとしていることを指している。Affector についてはこちらを参照してほしい。
Affector
また、ITowerRadiusProvider についてはこちらを参照してほしい。
ITowerRadiusProvider
125行目 : 「public override void Initialize(IAlignmentProvider affectorAlignment)」は Affector の際に解説を行った初期化処理を上書きしている。内容としては自身のオーバーロードの Initialize を呼び出している。
133行目 : 「public override void Initialize(IAlignmentProvider affectorAlignment, LayerMask mask)」は Affector の際に解説を行った初期化処理を上書きしている。内容としては Affector の初期化処理を行った後、自身の SetUpTimers の呼び出しと Tagetter の初期化を行っている。
144行目 : 「void OnDestroy()」は Unity 固有の OnDestory 時の処理を記載している。内容としては Targetetter に定義した acquiredTarget と lostTarget 関数の解除を行っている。
150 行目 : 「void OnLostTarget()」はターゲットをロストした際の処理を行っている。
155 行目 : 「void OnAcquiredTarget(Targetable acquiredTarget)」はターゲットを見つけた時の処理を行ってる。
168行目 : 「public float GetProjectileDamage()」は砲弾の合計ダメージを計算しそれを返却している。
178行目 : 「protected virtual void SetUpTimers()」は砲弾の発射タイマーの時間設定とランチャーの取得を行っている。
187行目 : 「protected virtual void Update()」は Unity 固有の更新処理を行っている。内容としては砲弾の発射タイマーを確認し指定時間に達していた際に OnFireTimer を呼び出している。
200行目 : 「protected virtual void OnFireTimer()」は発射の状態になっていれば FireProjectile を呼び出している。
215行目 : 「protected virtual void FireProjectile()」はターゲットしている敵がいるか確認し、複数アタックが可能であれば複数の的に対して砲撃する。単体アタックの場合はターゲットしている的に対して攻撃している。
242行目 : 「protected virtual int ByDistance(Targetable first, Targetable second)」については2個それぞれのターゲットから中央への距離を計算し、大小の判定を行っている。