Unityゲームにおける状態管理とキャラクター制御スクリプトの詳細解説資料
本資料では、Unityを利用して実装されたキャラクター(プレイヤーおよび敵)の状態管理、移動、攻撃、衝突判定などのスクリプト群について、各クラスやオブジェクトの役割、内部処理、そして相互連携のポイントを詳細に解説します。モジュールごとに機能が明確に分離され、再利用性・拡張性を考慮した設計となっています。
敵を倒せるようにする前まで
「Mob」とは、もともと「mobile object」または「mobile entity」の略で、ゲーム内で自律的に動くキャラクターや敵、NPC(Non-Player Character)など、動的な挙動を持つオブジェクト全般を指す用語です。
具体的には、以下のような特徴があります:
- 状態管理:
多くの場合、Mobは「通常状態(Normal)」「攻撃状態(Attack)」「死亡状態(Die)」など、状況に応じた複数の状態を持ち、それぞれの状態に応じた動作や挙動を管理する仕組みが組み込まれています。 - 行動制御:
移動、攻撃、ダメージ処理など、ゲーム内での一連の行動がプログラム上で定義され、MobStatusやMobAttackといったスクリプトでその制御が行われます。 - 再利用性:
同じ基盤(例:MobStatusクラス)を利用することで、プレイヤーキャラクターや敵キャラクターなど、動的なオブジェクトの基本的な挙動や状態管理を共通化し、再利用や拡張が容易になる設計になっています。
つまり、ゲーム開発において「Mob」は、動きや行動、状態の変化を持つあらゆる対象を包括する概念として利用され、効率的なプログラム設計や統一的な動作管理に寄与しています。
1. 基本クラスと状態管理
1.1 MobStatus クラス
目的と設計思想
MobStatusは、移動・攻撃が可能なオブジェクト(キャラクター、敵など)の基本状態を統一的に管理するための抽象クラスです。
- 状態定義:
- Normal: 通常の移動状態。
- Attack: 攻撃中。攻撃モーション実行中および攻撃判定が有効なタイミングを示す。
- Die: 死亡状態。ライフが0になった際に遷移し、死亡アニメーション実行後のオブジェクト破棄処理をサポート。
プロパティと初期化
- IsMovable / IsAttackable:
キャラクターがその時点で移動や攻撃を行えるかを状態に応じて判断します。 - LifeMax と Life:
ライフの最大値と現在値を管理し、Damageメソッドによりライフ減少や死亡判定を実施します。 - 初期化処理:
Start() メソッドでライフを満タンにセットし、子オブジェクトや自身のAnimatorコンポーネントを取得。これにより、アニメーションと状態遷移の連携が容易になります。
ダメージ処理と状態遷移
- Damage(int damage) メソッドにより、受けたダメージ分だけライフを減少。
- ライフが0以下になった場合、状態をDieに切り替え、死亡アニメーションの実行と後続処理(オブジェクト破棄など)を開始。
- また、攻撃可能な状態への切り替え(GoToAttackStateIfPossible)や通常状態への復帰(GoToNormalStateIfPossible)といった状態遷移ロジックも内包しています。
1.2 EnemyStatus クラス
MobStatus の継承と拡張
EnemyStatusはMobStatusを継承し、基本状態管理機能を利用しながら敵キャラクター固有の処理を実装しています。
NavMeshAgent との連携
- Start() でNavMeshAgentコンポーネントを取得し、敵の経路探索を可能にします。
- Update() 内でNavMeshAgentの速度を計算し、AnimatorのMoveSpeedパラメータに反映させることで、移動とアニメーションの連動を実現します。
死亡処理
- OnDie() メソッドにより、敵キャラクターが死亡状態になった後、一定時間経過後にオブジェクトを破棄するコルーチンを起動。
- これにより、不要なオブジェクトの残存を防ぎ、ゲーム全体のパフォーマンスを維持します。
2. 攻撃制御と関連オブジェクト
2.1 MobAttack クラス
目的
MobAttackは、キャラクターの攻撃処理を管理するクラスです。攻撃可能状態の判定、攻撃開始・終了の制御、攻撃ヒット時のダメージ処理を実装しています。
主な機能
- 攻撃可能判定:
AttackIfPossible() により、現在のMobStatusの状態を確認し、攻撃可能な場合は状態をAttackに遷移。 - 攻撃範囲と衝突判定:
- OnAttackRangeEnter(Collider collider) により、攻撃範囲内への対象物の侵入を検知し、攻撃処理のトリガーを発動。
- OnHitAttack(Collider collider) で、相手のMobStatusコンポーネントを取得し、Damage() メソッドを呼び出してダメージ付与を実施。
- 攻撃の開始と終了:
- OnAttackStart() により、攻撃用コライダー(MobAttackHitCollider等)を有効化。
- 攻撃終了後、OnAttackFinished() によってコライダーを無効化し、クールダウン後に通常状態へ復帰させるコルーチンを開始。
2.2 AttackRangeDetector オブジェクト
役割
敵がプレイヤーなどの対象物が攻撃可能範囲に入ったかどうかを検知するためのトリガーオブジェクト。
構成と設定
- BoxCollider:
Is Trigger を有効にして、物理的な衝突ではなく侵入判定専用とします。範囲・位置は敵の前方や周囲に適したサイズに調整。 - CollisionDetector:
OnTriggerEnter/OnTriggerStayイベントで設定されたUnityEvent(TriggerEvent)をInvoke。
例として、EnemyMoveのOnDetectObjectメソッドがアサインされ、プレイヤーの侵入を検知すると追尾処理が開始されます。
2.3 MobAttackHitCollider オブジェクト
役割
攻撃モーション中に、実際に「攻撃が当たった」ことを検知するためのトリガー用コライダー。
構成と設定
- Collider (SphereCollider / CapsuleCollider / BoxCollider):
Is Trigger を有効にし、物理的な衝突を無視して、攻撃ヒットの判定だけを行う。 - CollisionDetector:
攻撃判定時、OnTriggerEnter/OnTriggerStayを通じてMobAttack.OnHitAttackが呼び出され、対象にダメージを与える。
通常はオフ状態で、攻撃アニメーションやスクリプトフローに合わせて一時的に有効化されます。
3. キャラクターの移動および入力制御
3.1 EnemyMove クラス
目的
敵キャラクターの移動および追尾ロジックを実装し、プレイヤー検知後に適切な経路探索を行う役割を担います。
主な機能
- NavMeshAgent の利用:
コンポーネントとしてNavMeshAgentを取得し、Unityのナビメッシュシステムを利用して障害物を避けながら目的地に向かいます。 - アニメーションとの連動:
NavMeshAgentの速度情報をAnimatorに反映し、視覚的な移動アニメーションと実際の移動を同期させます。 - プレイヤー検知と障害物判定:
AttackRangeDetectorからのコライダー情報をもとに、OnDetectObjectでプレイヤーの存在を確認。
RaycastNonAllocを利用し、敵とプレイヤー間に障害物が存在するかを高速に判定。障害物がなければ追尾、存在すれば移動を停止するロジックを実装しています。
3.2 PlayerController クラス
目的
プレイヤーキャラクターの操作を直感的に制御するため、ユーザー入力に応じた移動、回転、ジャンプ、重力処理を管理します。
主な機能
- 入力管理:
Unityの新しいInput Systemを利用して、PlayerInputコンポーネントから「Move」や「Jump」などの入力を取得。 - 移動と回転:
CharacterControllerを使用し、物理的な衝突判定と連動した移動処理を実現。
入力方向に基づいて移動ベクトルを計算し、AnimatorのMoveSpeedパラメータを更新。
Quaternion.Slerpを用いたスムーズな回転補間で、自然な向きの変更を行います。 - ジャンプと重力:
地面との接触判定を行い、ジャンプ入力時に上方向へ初速度を与える。
空中では重力を継続的に適用し、自然な落下動作を再現します。
4. 衝突判定の共通処理:CollisionDetector クラス
目的
複数オブジェクト間の衝突(Trigger)判定を一元管理し、Inspector上で簡単に各種イベント(UnityEvent)を設定できるように設計されたコンポーネントです。
主な機能
- 衝突イベントの検知:
OnTriggerEnterおよびOnTriggerStayが実装され、接触したCollider情報を元に設定されたTriggerEvent(UnityEvent)をInvoke。 - 柔軟なイベント連携:
内部クラスTriggerEventは[Serializable]属性が付与されており、Inspector上で任意のメソッド(例:EnemyMove.OnDetectObject、MobAttack.OnHitAttack)をドラッグ&ドロップで設定可能です。
5. シーン上のオブジェクト配置と Inspector 設定
5.1 オブジェクトの配置イメージ
- AttackRangeDetector:
- 敵キャラクターの子オブジェクトとして配置。
- 敵の前方や周囲に広い範囲で設定し、プレイヤーが一定距離まで接近した場合に追尾や攻撃のトリガーを発動。
- MobAttackHitCollider:
- 武器(剣、拳など)やキャラクターの手足の先端に配置。
- 攻撃モーション中の特定タイミングで有効化され、攻撃ヒット判定を実施。
5.2 Inspector 設定例
- Collider の設定:
各トリガーオブジェクトのColliderは「Is Trigger」を有効に設定。
対象のオブジェクト(敵の視認範囲、攻撃範囲)に合わせたサイズや中心位置の調整が必要です。 - CollisionDetector の設定:
CollisionDetectorコンポーネントのTriggerEventに、呼び出したいメソッド(例:EnemyMove.OnDetectObject、MobAttack.OnHitAttack)をInspector上でアサインすることで、各種イベントが自動的に連携されます。
6. 全体の設計概要と連携のポイント
モジュール設計と再利用性
- 各クラスは、状態管理、攻撃制御、移動入力、衝突判定といった単一の機能に特化。
- このため、必要に応じて各機能を他のシーンやプロジェクトへ再利用・拡張が可能です。
統一された状態管理
- MobStatusを中心とする状態管理により、全キャラクターで「Normal」「Attack」「Die」といった状態が統一的に扱われ、状態遷移の整合性が保たれます。
処理の連携フロー
- 認知:
- AttackRangeDetectorがプレイヤーの侵入を検知し、CollisionDetectorを介してEnemyMoveのOnDetectObjectが呼び出される。
- 追跡:
- EnemyMoveはNavMeshAgentを利用して、障害物の有無を確認しながらプレイヤーへ追尾を開始。
- 攻撃:
- 攻撃可能な範囲に入ると、MobAttackのAttackIfPossible()が状態をAttackに遷移させ、攻撃モーションとともにMobAttackHitColliderが有効化される。
- ダメージ付与とクールダウン:
- 攻撃ヒット判定が行われ、OnHitAttack()により相手キャラクターのDamage()が呼ばれる。
- 攻撃終了後、クールダウン処理で一定時間後に通常状態へ戻る。

視覚的な設計図
- クラス図やシーケンス図(例:攻撃シーケンス図、プレイヤー移動シーケンス図)を用いることで、各コンポーネント間の関係性と処理フローが視覚的に把握でき、チーム間のコミュニケーションやデバッグ作業が円滑になります。
クラス図

プレイヤー移動シーケンス図

攻撃シーケンス図

7. まとめ
本資料で解説したUnityにおける状態管理とキャラクター制御スクリプトは、以下のポイントにより、効率的かつ拡張性の高いゲームシステムの構築を実現しています。
- 状態管理:
MobStatusを基盤とした統一的な状態管理により、移動、攻撃、死亡などの状態遷移が明確に制御されます。 - 攻撃処理:
MobAttackと関連オブジェクト(AttackRangeDetector、MobAttackHitCollider)の連携により、タイミングに沿った攻撃処理とダメージ判定が実装されています。 - 移動・入力制御:
EnemyMoveとPlayerControllerにより、敵の自動追尾やプレイヤーの直感的な操作が可能になり、シーン全体の動作が滑らかに連動します。 - 衝突判定:
CollisionDetectorにより、各オブジェクトの衝突判定が一元管理され、Inspector上で簡単にイベントの紐付けが可能となっています。 - シーン配置と連携:
各オブジェクトの適切な配置とInspectorでの設定により、認知から追尾、攻撃までの一連の流れがスムーズに実現されています。
この設計は、Unityでのキャラクター制御やゲームロジック実装において、メンテナンス性と拡張性を両立した理想的なサンプルとして、今後のプロジェクトにも応用可能です。
以上が、Unityゲームにおける状態管理とキャラクター制御スクリプトの詳細な解説資料となります。各項目を参考に、シーン上でのオブジェクト配置やInspector設定も合わせて確認することで、より深い理解と効率的な開発が実現できるでしょう。
ディスカッション
コメント一覧
まだ、コメントがありません