Unityゲームにおける状態管理とキャラクター制御スクリプトの概要解説資料(プレイヤー攻撃まで)
プレイヤーが攻撃できる機能を追加まで
1. 基本的な状態管理とキャラクターの基本クラス
1.1 MobStatus クラス
目的:
Mob(移動・攻撃可能なオブジェクト)の共通状態(Normal
、Attack
、Die
など)を管理する抽象クラス。
主な機能:
- 移動や攻撃が可能かどうかの判定 (
IsMovable
,IsAttackable
) - ライフ管理 (
LifeMax
とLife
) - 初期化処理: ライフのセットや
Animator
の取得 - ダメージ処理:
Life
が 0 以下になると死亡状態への遷移を行う
1.2 EnemyStatus クラス
目的:
敵キャラクター固有の状態管理を実装するクラス(MobStatus
の派生)。
主な機能:
- NavMeshAgent を利用した移動制御(
EnemyMove
クラスなどから呼び出し) - 移動速度を
Animator
に反映 - 死亡時の後処理: 一定時間後にオブジェクトを破棄
1.3 PlayerStatus クラス
目的:
プレイヤーキャラクター固有の状態管理を実装するクラス(MobStatus
の派生)。
主な機能(現状):
MobStatus
と同様に、ライフや状態(Normal
,Attack
,Die
)の管理- TODO コメントにあるように、将来的にプレイヤー特有の追加状態やパラメータを拡張可能
using UnityEngine;
public class PlayerStatus : MobStatus
{
// TODO あとでプレイヤー向けにカスタムする
}
2. 攻撃制御と関連オブジェクト
2.1 MobAttack クラス
目的:
Mob の攻撃処理を制御するクラス。
主な機能:
- 攻撃可能かどうかの判定 (
AttackIfPossible
) - 攻撃範囲内に侵入した対象への処理 (
OnAttackRangeEnter
) - 実際の攻撃時に対象へダメージを与える処理 (
OnHitAttack
) - 攻撃開始(
OnAttackStart
)および終了後のクールダウン処理
2.2 AttackRangeDetector オブジェクト
役割:
敵がプレイヤーを追跡・攻撃する際に、プレイヤーが「攻撃可能範囲」に入ったかを検知するトリガー用オブジェクト。
主な設定:
BoxCollider
(Is Trigger
設定)で範囲を定義CollisionDetector
スクリプトをアタッチし、OnTriggerEnter
/OnTriggerStay
でEnemyMove
のOnDetectObject
などを呼び出す
2.3 MobAttackHitCollider オブジェクト
役割:
攻撃アニメーションのタイミングで有効化され、実際に「攻撃が当たった」ことを判定するトリガー用コライダー。
主な設定:
Collider
(Is Trigger
設定)で攻撃判定用の範囲を定義CollisionDetector
スクリプトを用いてOnTriggerEnter
/OnTriggerStay
時にMobAttack.OnHitAttack
を実行し、相手にダメージを与える
3. キャラクターの移動および入力制御
3.1 EnemyMove クラス
目的:
敵キャラクターの移動制御およびプレイヤー追跡ロジックを実装するクラス。
主な機能:
- NavMeshAgent を用いた経路探索
CollisionDetector
からの通知(OnDetectObject
)により、プレイヤー検知と障害物判定(RaycastNonAlloc
など)- 障害物がなければ追跡、障害物があれば移動停止
3.2 PlayerController クラス
目的:
プレイヤーキャラクターの入力受付と動作(移動、回転、ジャンプ、攻撃)の管理。
主な機能:
- Unityの
PlayerInput
コンポーネントを利用した入力管理(Move
,Jump
,Attack
) CharacterController
を使った移動処理とAnimator
のMoveSpeed
更新- 入力方向に基づいた
Quaternion.LookAt
を用いた回転 - 地上判定、ジャンプ処理、重力適用による落下処理
- 攻撃入力があった際に、
MobAttack.AttackIfPossible()
を呼び出して攻撃状態へ移行
サンプルコード:
using UnityEngine;
using UnityEngine.InputSystem;
[RequireComponent(typeof(CharacterController))]
[RequireComponent(typeof(PlayerInput))]
[RequireComponent(typeof(PlayerStatus))]
[RequireComponent(typeof(MobAttack))]
public class PlayerController : MonoBehaviour
{
[SerializeField] private Animator animator;
[SerializeField] private float moveSpeed = 3; // 移動速度
[SerializeField] private float jumpPower = 3; // ジャンプ力
private CharacterController _characterController; // CharacterControllerのキャッシュ
private Transform _transform; // Transformのキャッシュ
private InputAction _jump; // Jumpアクションのキャッシュ
private InputAction _move; // Moveアクションのキャッシュ
private InputAction _attack; // Attackアクションのキャッシュ
private Vector3 _moveVelocity; // キャラの移動速度情報
private PlayerStatus _status;
private MobAttack _mobAttack;
private void Start()
{
_characterController = GetComponent<CharacterController>();
_transform = transform;
_status = GetComponent<PlayerStatus>();
_mobAttack = GetComponent<MobAttack>();
var input = GetComponent<PlayerInput>();
// PlayerInputの「Default Map」で指定されているアクションマップを有効化
input.currentActionMap.Enable();
// アクションマップからアクションを取得
_move = input.currentActionMap.FindAction("Move");
_jump = input.currentActionMap.FindAction("Jump");
_attack = input.currentActionMap.FindAction("Attack");
}
private void Update()
{
// 地上判定のデバッグ表示
Debug.Log(_characterController.isGrounded ? "地上にいます" : "空中です");
// Attackアクション(例: マウス左クリック)で攻撃
if (_attack.WasPressedThisFrame())
{
_mobAttack.AttackIfPossible();
}
// プレイヤーが移動可能な状態であれば移動入力を処理
if (_status.IsMovable)
{
var moveValue = _move.ReadValue<Vector2>();
_moveVelocity.x = moveValue.x * moveSpeed;
_moveVelocity.z = moveValue.y * moveSpeed;
// 移動方向にキャラクターを向ける
_transform.LookAt(_transform.position + new Vector3(_moveVelocity.x, 0, _moveVelocity.z));
}
else
{
// 移動不可時は速度をゼロに
_moveVelocity.x = 0;
_moveVelocity.z = 0;
}
// ジャンプや落下処理
if (_characterController.isGrounded)
{
if (_jump.WasPressedThisFrame())
{
Debug.Log("ジャンプ!");
_moveVelocity.y = jumpPower;
}
}
else
{
// 重力による加速
_moveVelocity.y += Physics.gravity.y * Time.deltaTime;
}
// 実際にキャラクターを動かす
_characterController.Move(_moveVelocity * Time.deltaTime);
// 移動スピードを Animator に反映
animator.SetFloat("MoveSpeed", new Vector3(_moveVelocity.x, 0, _moveVelocity.z).magnitude);
}
}
ポイント:
RequireComponent
属性により、CharacterController
,PlayerInput
,PlayerStatus
,MobAttack
が必須コンポーネントとして設定されているPlayerInput
コンポーネントで定義されたアクション(Move
,Jump
,Attack
)をスクリプトで取得して、プレイヤー操作を実装MobAttack
を呼び出すことで、共通の攻撃ロジックを再利用
4. 衝突判定の共通処理
4.1 CollisionDetector クラス
目的:
衝突(Trigger)判定を行い、Inspector で設定した UnityEvent(TriggerEvent
)を発火させる。
主な機能:
OnTriggerEnter
とOnTriggerStay
により、指定イベントをInvoke
- 内部クラス
TriggerEvent
は[Serializable]
属性を持ち、Inspector 上でメソッドを設定可能にしている
5. シーン上のオブジェクト配置とスクリプト間連携
5.1 シーン上の配置イメージ
- AttackRangeDetector: 敵キャラクターの子オブジェクトとして配置。プレイヤーを認識できる範囲を示す。
- MobAttackHitCollider: 攻撃時のみ有効化されるトリガー。武器や手・足の先端などに配置して、攻撃ヒットを判定。
5.2 Inspector 設定例
- Collider の
Is Trigger
設定:AttackRangeDetector
とMobAttackHitCollider
の各Collider
で有効化
- CollisionDetector の設定:
TriggerEvent
に各クラス(EnemyMove
,MobAttack
など)の呼び出したいメソッドをアサイン
5.3 各クラス・オブジェクト間の連携
- EnemyMove:
AttackRangeDetector
のTriggerEvent
により、プレイヤー検知後に追跡開始 - MobAttack: 攻撃開始時に
MobAttackHitCollider
を有効化し、攻撃ヒット判定を実行 - CollisionDetector: 共通の衝突判定ロジックとして各オブジェクトで利用
- PlayerController:
PlayerInput
からの入力に基づき移動やジャンプ、攻撃を制御。内部でMobAttack
を呼び出すことで、敵と同様の攻撃システムを利用
6. 全体のまとめと設計図
6.1 システム全体の特徴
- モジュール設計:
- 各クラスが役割ごとに分離され、再利用性・拡張性が高い
- 状態管理:
MobStatus
を基盤とし、状態遷移(移動、攻撃、死亡)を統一的に管理EnemyStatus
,PlayerStatus
がそれぞれを継承して固有の実装を拡張
- 連携:
- 敵はプレイヤーの接近を検知して追跡→攻撃
- プレイヤーは
PlayerInput
による入力操作で移動や攻撃 - 同じ攻撃ロジック(
MobAttack
)を共有することで、一貫性のあるダメージ処理が可能
6.2 設計図・シーケンス図
- クラス図:
MobStatus
→EnemyStatus
/PlayerStatus
MobAttack
はEnemy
/Player
両方にアタッチ可能EnemyMove
/PlayerController
で各自の移動・入力処理

- 攻撃シーケンス図:
AttackIfPossible
呼び出し- 攻撃状態遷移 → 攻撃アニメーション再生
- 攻撃ヒット判定(
MobAttackHitCollider
)→ ダメージ付与 - クールダウン → 通常状態に戻る

- プレイヤー移動シーケンス図:
- 入力(Move, Jump, Attack)を受け取る
- 移動方向を算出 →
CharacterController.Move
- アニメーションへ速度を反映 (
MoveSpeed
) - 攻撃ボタン押下で
MobAttack.AttackIfPossible
を実行

まとめ
- 本資料では、状態管理(MobStatus, EnemyStatus, PlayerStatus) を中心に、攻撃制御(MobAttack, AttackRangeDetector, MobAttackHitCollider) と 移動・入力処理(EnemyMove, PlayerController) を連携させる設計について解説しました。
- 新たに追加された PlayerStatus と PlayerController により、プレイヤーも同じフレームワーク(Mobの概念)上で動作・攻撃が可能になります。
- 今後、プレイヤー特有の状態や演出(例: 無敵時間、被ダメージ演出、UI 連携など)を拡張する際は、
PlayerStatus
やPlayerController
に追加実装していくことで対応できます
ディスカッション
コメント一覧
まだ、コメントがありません