Unity チュートリアルのタワーディフェンステンプレートを触ってみる(12)ではエージェントに使用したスクリプトのうち、AttackingAgent と FlyingAgent の継承元となる Agent 解説を行った。今回は AttackingAgent と FlyingAgent について解説を行っていく。
1.エージェント編 – AttackingAgent.cs
AttackingAgent.cs について稚拙ながら解説
「AttackingAgent.cs」は「Asset/Scripts/TowerDefense/Agents/AttackingAgent.cs」の指しておりスクリプトについては以下の通り。内容としてはタワーを攻撃する Agent の挙動制御を行っている。
[cce_csharp]using Core.Health;
using TowerDefense.Affectors;
using TowerDefense.Towers;
using UnityEngine;
namespace TowerDefense.Agents
{
/// <summary>
/// An implementation of Agent that will attack
/// any Towers that block its path
/// </summary>
public class AttackingAgent : Agent
{
/// <summary>
/// Tower to target
/// </summary>
protected Tower m_TargetTower;
/// <summary>
/// The attached attack affector
/// </summary>
protected AttackAffector m_AttackAffector;
/// <summary>
/// Is this agent currently engaging a tower?
/// </summary>
protected bool m_IsAttacking;
public override void Initialize()
{
base.Initialize();
// Attack affector
m_AttackAffector.Initialize(configuration.alignmentProvider);
// We don't want agents to attack towers until their path is blocked,
// so disable m_AttackAffector until it is needed
m_AttackAffector.enabled = false;
}
/// <summary>
/// Unsubscribes from tracked towers removed event
/// and disables the attached attack affector
/// </summary>
public override void Remove()
{
base.Remove();
if (m_TargetTower != null)
{
m_TargetTower.removed -= OnTargetTowerDestroyed;
}
m_AttackAffector.enabled = false;
m_TargetTower = null;
}
/// <summary>
/// Gets the closest tower to the agent
/// </summary>
/// <returns>The closest tower</returns>
protected Tower GetClosestTower()
{
var towerController = m_AttackAffector.towerTargetter.GetTarget() as Tower;
return towerController;
}
/// <summary>
/// Caches the Attack Affector if necessary
/// </summary>
protected override void LazyLoad()
{
base.LazyLoad();
if (m_AttackAffector == null)
{
m_AttackAffector = GetComponent<AttackAffector>();
}
}
/// <summary>
/// If the tower is destroyed while other agents attack it, ensure it becomes null
/// </summary>
/// <param name="tower">The tower that has been destroyed</param>
protected virtual void OnTargetTowerDestroyed(DamageableBehaviour tower)
{
if (m_TargetTower == tower)
{
m_TargetTower.removed -= OnTargetTowerDestroyed;
m_TargetTower = null;
}
}
/// <summary>
/// Peforms the relevant path update for the states <see cref="Agent.State.OnCompletePath"/>,
/// <see cref="Agent.State.OnPartialPath"/> and <see cref="Agent.State.Attacking"/>
/// </summary>
protected override void PathUpdate()
{
switch (state)
{
case State.OnCompletePath:
OnCompletePathUpdate();
break;
case State.OnPartialPath:
OnPartialPathUpdate();
break;
case State.Attacking:
AttackingUpdate();
break;
}
}
/// <summary>
/// Change to <see cref="Agent.State.OnCompletePath" /> when path is no longer blocked or to
/// <see cref="Agent.State.Attacking" /> when the agent reaches <see cref="AttackingAgent.m_TargetTower" />
/// </summary>
protected override void OnPartialPathUpdate()
{
if (!isPathBlocked)
{
state = State.OnCompletePath;
return;
}
// Check for closest tower at the end of the partial path
m_AttackAffector.towerTargetter.transform.position = m_NavMeshAgent.pathEndPosition;
Tower tower = GetClosestTower();
if (tower != m_TargetTower)
{
// if the current target is to be replaced, unsubscribe from removed event
if (m_TargetTower != null)
{
m_TargetTower.removed -= OnTargetTowerDestroyed;
}
// assign target, can be null
m_TargetTower = tower;
// if new target found subscribe to removed event
if (m_TargetTower != null)
{
m_TargetTower.removed += OnTargetTowerDestroyed;
}
}
if (m_TargetTower == null)
{
return;
}
float distanceToTower = Vector3.Distance(transform.position, m_TargetTower.transform.position);
if (!(distanceToTower < m_AttackAffector.towerTargetter.effectRadius))
{
return;
}
if (!m_AttackAffector.enabled)
{
m_AttackAffector.towerTargetter.transform.position = transform.position;
m_AttackAffector.enabled = true;
}
state = State.Attacking;
m_NavMeshAgent.isStopped = true;
}
/// <summary>
/// The agent attacks until the path is available again or it has killed the target tower
/// </summary>
protected void AttackingUpdate()
{
if (m_TargetTower != null)
{
return;
}
MoveToNode();
// Resume path once blocking has been cleared
m_IsAttacking = false;
m_NavMeshAgent.isStopped = false;
m_AttackAffector.enabled = false;
state = isPathBlocked ? State.OnPartialPath : State.OnCompletePath;
// Move the Targetter back to the agent's position
m_AttackAffector.towerTargetter.transform.position = transform.position;
}
}
}[/cce_csharp]
12行目 : 「public class AttackingAgent : Agent」は前回解説を行った Agentクラスを継承した抽象クラスであることを指している。Agentクラスについてはこちらを参照してほしい。
| Unity チュートリアルのタワーディフェンステンプレートを触ってみる(11)ではエージェント(敵ユニット)の作成を行った。今回はエージェントに使用したスクリプトのうち、AttackingAgent と FlyingAgent の継承元となる Agent 解説を行っていきたいと思う。1.エージェント編 – Agent.csAgent.cs について稚拙ながら解説「Agent.cs」は「Asset/Scripts/TowerDefense/Agents/Agent.cs」の指しておりスクリプトについては以下の通り。内容としては Agent の挙動制御を行っている。using System;using ActionGameFramework.Health;u... Unity チュートリアルのタワーディフェンステンプレートを触ってみる(12) - StudioFun |
29行目 : 「public override void Initialize()」は継承元となっている Targetable および Agent の初期化と AttackingAgent のグローバル変数である AttackAffector の初期化を行っている。
45行目 : 「public override void Remove()」は継承元となっている Targetable および Agent の削除処理と削除されたときの OnTargetTowerDestroyed 処理を m_TargetTower.removed のイベントから解除している。
60行目 : 「protected Tower GetClosestTower()」は AttackAffector から最も近いタワーの取得を行っている。
69行目 : 「protected override void LazyLoad()」は継承元となっている Targetable および Agent と同様に遅延読み込みを行っている。
82行目 : 「protected virtual void OnTargetTowerDestroyed(DamageableBehaviour tower)」はこの Agent が攻撃しているタワーが他の Agent によって破壊されたときにタワーオブジェクトを null として扱うように変更している。
95行目 : 「protected override void PathUpdate()」は Agent の状態によって行う処理を場合分けしている。ここでは「Agent が進む経路がブロックされていないとき」、「Agent が進む経路がブロックされているとき」、「Agent が攻撃しているとき」で分岐が行われている。
115行目 : 「protected override void OnPartialPathUpdate()」は Agent の進む経路がブロックされているときの処理を行っている。近くにあるタワーを判定し、攻撃範囲内にタワーがあれば攻撃を開始に移行している。
164行目 : 「protected void AttackingUpdate()」は Agent が攻撃しているときの処理を行っている。攻撃しているタワーがなくなったときに経路の状態を確認し、次のノードへ移動する処理に移行している。
2.エージェント編 – FlyingAgent.cs
FlyingAgent.cs について稚拙ながら解説
「FlyingAgent.cs」は「Asset/Scripts/TowerDefense/Agents/FlyingAgent.cs」の指しておりスクリプトについては以下の通り。内容としては 障害物を無視する Agent の挙動制御を行っている。
[cce_csharp]using UnityEngine;
using UnityEngine.AI;
namespace TowerDefense.Agents
{
/// <summary>
/// Agent that can pass "over" towers that block the path
/// </summary>
public class FlyingAgent : Agent
{
/// <summary>
/// Time to wait to clear the navmesh obstacles
/// </summary>
protected float m_WaitTime = 0.5f;
/// <summary>
/// The current time to wait until we can resume agent movement as normal
/// </summary>
protected float m_CurrentWaitTime;
/// <summary>
/// If flying agents are blocked, they should still move through obstacles
/// </summary>
protected override void OnPartialPathUpdate()
{
if (!isPathBlocked)
{
state = State.OnCompletePath;
return;
}
if (!isAtDestination)
{
return;
}
m_NavMeshAgent.enabled = false;
m_CurrentWaitTime = m_WaitTime;
state = State.PushingThrough;
}
/// <summary>
/// Controls behaviour based on the states <see cref="Agent.State.OnCompletePath"/>, <see cref="Agent.State.OnPartialPath"/>
/// and <see cref="Agent.State.PushingThrough"/>
/// </summary>
protected override void PathUpdate()
{
switch (state)
{
case State.OnCompletePath:
OnCompletePathUpdate();
break;
case State.OnPartialPath:
OnPartialPathUpdate();
break;
case State.PushingThrough:
PushingThrough();
break;
}
}
/// <summary>
/// When flying agents are pushing through, give them a small amount of time to clear the gap and turn on their agent
/// once time elapses
/// </summary>
protected void PushingThrough()
{
m_CurrentWaitTime -= Time.deltaTime;
// Move the agent, overriding its NavMeshAgent until it reaches its destination
transform.LookAt(m_Destination, Vector3.up);
transform.position += transform.forward * m_NavMeshAgent.speed * Time.deltaTime;
if (m_CurrentWaitTime > 0)
{
return;
}
// Check if there is a navmesh under the agent, if not, then reset the timer
NavMeshHit hit;
if (!NavMesh.Raycast(transform.position + Vector3.up, Vector3.down, out hit, navMeshMask))
{
m_CurrentWaitTime = m_WaitTime;
}
else
{
// If the time elapses, and there is a NavMesh under it, resume agent movement as normal
m_NavMeshAgent.enabled = true;
NavigateTo(m_Destination);
state = isPathBlocked ? State.OnPartialPath : State.OnCompletePath;
}
}
}
}[/cce_csharp]
9行目 : 「public class AttackingAgent : Agent」は前回解説を行った Agentクラスを継承した抽象クラスであることを指している。Agentクラスについてはこちらを参照してほしい。
| Unity チュートリアルのタワーディフェンステンプレートを触ってみる(11)ではエージェント(敵ユニット)の作成を行った。今回はエージェントに使用したスクリプトのうち、AttackingAgent と FlyingAgent の継承元となる Agent 解説を行っていきたいと思う。1.エージェント編 – Agent.csAgent.cs について稚拙ながら解説「Agent.cs」は「Asset/Scripts/TowerDefense/Agents/Agent.cs」の指しておりスクリプトについては以下の通り。内容としては Agent の挙動制御を行っている。using System;using ActionGameFramework.Health;u... Unity チュートリアルのタワーディフェンステンプレートを触ってみる(12) - StudioFun |
24行目 : 「protected override void OnPartialPathUpdate()」は Agent の進む経路がブロックされているときの処理を行っている。障害物があれば停止時間を設定し通過状態に移行している。
44行目 : 「protected override void PathUpdate()」は Agent の状態によって行う処理を場合分けしている。ここでは「Agent が進む経路がブロックされていないとき」、「Agent が進む経路がブロックされているとき」、「Agent が障害物を無視して通過しているとき」で分岐が行われている。
64行目 : 「protected void PushingThrough()」は指定時間待機後、障害物を通過する処理を行っている。
