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
| Unity チュートリアルのタワーディフェンステンプレートを触ってみる(16)では砲弾に対して追加した HitscanAttack コンポーネントについて解説を行った。今回は LaserTower_0 に追加した AttackAffector コンポーネントの継承関係にある Affector を行っていく。1.標的設定と砲撃編 – Affector.csAffector.cs について稚拙ながら解説「Affector.cs」は「Assets/Scripts/TowerDefense/Affectors/Affector.cs」の指しておりスクリプトについては以下の通り。内容としては Affector の初期化のみを行っている。using Core.Health;us... Unity チュートリアルのタワーディフェンステンプレートを触ってみる(17) - StudioFun |
また、ITowerRadiusProvider についてはこちらを参照してほしい。
ITowerRadiusProvider
| Unity チュートリアルのタワーディフェンステンプレートを触ってみる(17)では LaserTower_0 に追加した AttackAffector コンポーネントの継承関係にある Affector を行った。今回は LaserTower_0 に追加した AttackAffector コンポーネントのインタフェースにある ITowerRadiusProvider の解説を行っていく。1.標的設定と砲撃編 – ITowerRadiusProvider.csITowerRadiusProvider.cs について稚拙ながら解説「ITowerRadiusProvider.cs」は「Assets/Scripts/TowerDefense/Towers/ITowerRadiusProvider.cs」の指しておりスクリプトに... Unity チュートリアルのタワーディフェンステンプレートを触ってみる(18) - StudioFun |
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個それぞれのターゲットから中央への距離を計算し、大小の判定を行っている。
