Unityを触り始めた頃、「とりあえず全部Updateに書いちゃえ!」となりがちですよね。移動処理、ジャンプ、ダッシュ、アニメーション、カメラ制御……全部ひとつのスクリプトに詰め込むと、最初は動いていても、あとから仕様変更が入った瞬間に地獄が始まります。
特に「ダッシュ機能」は、
- 移動速度の一時的な変更
- クールダウン管理
- スタミナやエフェクトとの連携
など、他のシステムと絡みやすく、巨大なプレイヤースクリプトの中に埋もれがちな代表例です。
そこでこの記事では、「ダッシュ機能」を単独のコンポーネントとして切り出した DashMechanic を用意して、親オブジェクトの移動速度を一時的に倍増させ、クールダウンもまとめて管理する仕組みを作っていきます。プレイヤーや敵、動く床などにポン付けできるようにしておくと、レベルデザインがかなり楽になりますよ。
【Unity】一時的に爆速!クールダウン付きダッシュ「DashMechanic」コンポーネント
コンポーネントの概要
この DashMechanic コンポーネントは、
- 親オブジェクトの「通常移動コンポーネント」を参照
- 入力が来たら、一定時間だけ移動速度を倍率でブースト
- その後はクールダウンが終わるまで再使用不可
という単機能に絞ったコンポーネントです。
ここでは「親の移動速度」を扱うために、シンプルな移動コンポーネント SimpleMover を一緒に用意し、その moveSpeed をダッシュ中だけ書き換える構成にします。
プロジェクトで既に独自の移動コンポーネントがある場合は、同じような形でプロパティを書き換えるだけで応用できます。
フルコード:SimpleMover + DashMechanic
まずはベースとなる移動コンポーネント SimpleMover と、ダッシュ機能を担当する DashMechanic の2つをまとめて掲載します。どちらも同じゲームオブジェクトにアタッチして使う想定です。
using UnityEngine;
using UnityEngine.InputSystem; // 新Input System用
/// <summary>
/// シンプルな2D/3D共通の移動コンポーネント。
/// - 移動方向(Normalized)と速度をもとに、Transformを移動させる。
/// - 物理挙動が不要なキャラやオブジェクト向け。
/// - DashMechanic から moveSpeed を書き換えられるようにしている。
/// </summary>
public class SimpleMover : MonoBehaviour
{
[Header("基本移動設定")]
[SerializeField] private float moveSpeed = 5f; // 通常移動速度
[SerializeField] private bool useLocalSpace = false; // ローカル座標で移動するか
// 現在の移動方向(外部から設定)
private Vector3 moveDirection = Vector3.zero;
/// <summary>
/// 外部(Inputなど)から移動方向を設定する。
/// 正規化はこの中で行う。
/// </summary>
public void SetMoveDirection(Vector3 direction)
{
// 方向ベクトルを正規化(0ベクトルの場合はそのまま)
moveDirection = direction.sqrMagnitude > 0.0001f
? direction.normalized
: Vector3.zero;
}
/// <summary>
/// 現在の速度を取得(DashMechanic などから参照する用)。
/// </summary>
public float GetMoveSpeed()
{
return moveSpeed;
}
/// <summary>
/// 現在の速度を設定(DashMechanic などから一時的に変更する用)。
/// </summary>
public void SetMoveSpeed(float newSpeed)
{
moveSpeed = Mathf.Max(0f, newSpeed); // マイナス速度は防止
}
private void Update()
{
if (moveDirection == Vector3.zero) return;
// 時間依存の移動
float distance = moveSpeed * Time.deltaTime;
if (useLocalSpace)
{
transform.Translate(moveDirection * distance, Space.Self);
}
else
{
transform.Translate(moveDirection * distance, Space.World);
}
}
}
/// <summary>
/// ダッシュ機能コンポーネント。
/// - 指定された入力アクションが押されたとき、
/// 親オブジェクトの SimpleMover の移動速度を一時的に増加させる。
/// - ダッシュ時間とクールダウンを管理する。
/// </summary>
[RequireComponent(typeof(SimpleMover))]
public class DashMechanic : MonoBehaviour
{
[Header("ダッシュ設定")]
[SerializeField] private float dashMultiplier = 2f; // 速度倍率(例: 2倍)
[SerializeField] private float dashDuration = 0.2f; // ダッシュ継続時間(秒)
[SerializeField] private float dashCooldown = 1.0f; // クールダウン時間(秒)
[Header("入力設定 (Input System)")]
[Tooltip("ダッシュ用のInput Action。type=Button 推奨")]
[SerializeField] private InputActionReference dashActionReference;
// 内部状態管理
private SimpleMover simpleMover;
private float baseMoveSpeed; // 通常時の速度を保持
private bool isDashing = false; // 現在ダッシュ中か
private bool isOnCooldown = false; // 現在クールダウン中か
private float dashTimer = 0f; // ダッシュ残り時間
private float cooldownTimer = 0f; // クールダウン残り時間
private void Awake()
{
// 同じオブジェクトに付いている SimpleMover を取得
simpleMover = GetComponent<SimpleMover>();
baseMoveSpeed = simpleMover.GetMoveSpeed();
}
private void OnEnable()
{
// Input Action の有効化
if (dashActionReference != null && dashActionReference.action != null)
{
dashActionReference.action.performed += OnDashPerformed;
dashActionReference.action.Enable();
}
}
private void OnDisable()
{
// Input Action の無効化
if (dashActionReference != null && dashActionReference.action != null)
{
dashActionReference.action.performed -= OnDashPerformed;
dashActionReference.action.Disable();
}
}
/// <summary>
/// ダッシュボタンが押されたときに呼ばれるコールバック。
/// </summary>
private void OnDashPerformed(InputAction.CallbackContext context)
{
// 既にダッシュ中 or クールダウン中なら無視
if (isDashing || isOnCooldown) return;
StartDash();
}
/// <summary>
/// ダッシュ開始処理。
/// </summary>
private void StartDash()
{
isDashing = true;
isOnCooldown = true;
dashTimer = dashDuration;
cooldownTimer = dashCooldown;
// 現在の通常速度を記録
baseMoveSpeed = simpleMover.GetMoveSpeed();
// 速度を倍率分だけ増加
float dashedSpeed = baseMoveSpeed * dashMultiplier;
simpleMover.SetMoveSpeed(dashedSpeed);
// ここでエフェクトやSEを鳴らしても良い
// 例: PlayDashEffect();
}
/// <summary>
/// ダッシュ終了処理。
/// </summary>
private void EndDash()
{
isDashing = false;
// 速度を元に戻す
simpleMover.SetMoveSpeed(baseMoveSpeed);
}
private void Update()
{
// ダッシュ時間のカウントダウン
if (isDashing)
{
dashTimer -= Time.deltaTime;
if (dashTimer <= 0f)
{
EndDash();
}
}
// クールダウン時間のカウントダウン
if (isOnCooldown)
{
cooldownTimer -= Time.deltaTime;
if (cooldownTimer <= 0f)
{
isOnCooldown = false;
}
}
}
/// <summary>
/// 現在ダッシュ中かどうかを外部から参照するためのプロパティ。
/// </summary>
public bool IsDashing => isDashing;
/// <summary>
/// 現在クールダウン中かどうかを外部から参照するためのプロパティ。
/// </summary>
public bool IsOnCooldown => isOnCooldown;
}
使い方の手順
① 移動用コンポーネントをセットアップする
まずはプレイヤーや敵など、ダッシュさせたいオブジェクトに SimpleMover をアタッチします。
- Hierarchy で「Player」オブジェクトを選択
- Add Component から
SimpleMoverを追加 Move Speedに通常移動速度(例: 5)を設定- 2Dの場合でも3Dの場合でも、
SetMoveDirectionに渡すベクトル次第で動きます
入力からの移動方向設定は、例えばこんな感じの別コンポーネントに任せるとスッキリします(プレイヤー用の例):
using UnityEngine;
using UnityEngine.InputSystem;
/// <summary>
/// プレイヤー入力を SimpleMover に流し込むだけのコンポーネント。
/// 移動とダッシュを分離することで、責務を小さくしている。
/// </summary>
[RequireComponent(typeof(SimpleMover))]
public class PlayerInputMover : MonoBehaviour
{
[SerializeField] private InputActionReference moveActionReference;
private SimpleMover simpleMover;
private void Awake()
{
simpleMover = GetComponent<SimpleMover>();
}
private void OnEnable()
{
if (moveActionReference != null && moveActionReference.action != null)
{
moveActionReference.action.Enable();
}
}
private void OnDisable()
{
if (moveActionReference != null && moveActionReference.action != null)
{
moveActionReference.action.Disable();
}
}
private void Update()
{
if (moveActionReference == null || moveActionReference.action == null) return;
Vector2 input = moveActionReference.action.ReadValue<Vector2>();
// 2D横スクロールなら x のみ、3Dなら x,z を使うなど用途に応じて変換
Vector3 moveDir = new Vector3(input.x, 0f, input.y);
simpleMover.SetMoveDirection(moveDir);
}
}
② DashMechanic を追加する
- 同じ「Player」オブジェクトに DashMechanic を追加
- インスペクターで以下を設定
- Dash Multiplier: 2(2倍速ダッシュ)
- Dash Duration: 0.2(0.2秒だけブースト)
- Dash Cooldown: 1(1秒ごとにダッシュ可)
③ Input System の設定(ダッシュボタン)
Unity の新 Input System を使って、ダッシュ用のアクションを用意します。
- Project ウィンドウで Input Actions アセット(例:
PlayerInputActions.inputactions)を開く - 「Player」アクションマップに Dash というアクションを追加
- Action Type を Button に設定
- Binding にキーボードの LeftShift やゲームパッドの Bボタン などを割り当て
- アセットを保存
- Hierarchy で適当なオブジェクト(または Player)に PlayerInput を追加し、先ほどのアセットを割り当て
- DashMechanic の Dash Action Reference に、Input Actions アセット内の Dash アクションをドラッグ&ドロップ
④ 実際の使用例
- プレイヤーキャラクター
横スクロールアクションで、プレイヤーにSimpleMover+PlayerInputMover+DashMechanicをアタッチ。
左右キーで移動し、Shift で一瞬だけスピードアップさせることで、敵の攻撃を避けるアクションが簡単に実装できます。 - 敵キャラクター
敵AIがプレイヤーとの距離を詰めるときだけDashMechanicのStartDash()をスクリプトから呼び出すことで、突進攻撃のような挙動を実装可能です(AI側スクリプトからpublic bool IsOnCooldownを見て使用タイミングを制御すると良いです)。 - 動く床(ギミック)
通常はゆっくり動く床にSimpleMoverを付けておき、プレイヤーが乗った瞬間にDashMechanicをトリガーすることで、「一瞬だけ加速するベルトコンベア」などのギミックを作ることもできます。
メリットと応用
コンポーネント分割のメリット
DashMechanic を独立コンポーネントにしておくと、次のようなメリットがあります。
- プレイヤースクリプトが肥大化しない
移動、ダッシュ、ジャンプ、攻撃をそれぞれ別コンポーネントに分けておけば、どれか1つを改造しても他に影響しづらくなります。ダッシュの仕様変更(倍率・時間・クールダウンなど)もDashMechanicだけを見れば良いので、デバッグが楽です。 - プレハブの再利用性が高い
「この敵にもダッシュさせたい」となったとき、DashMechanicをアタッチして、AIスクリプトからトリガーするだけでOK。プレイヤーと敵で同じダッシュロジックを共有できます。 - レベルデザイン時にパラメータ調整が簡単
シーン上のオブジェクトごとにDash MultiplierやDash Cooldownをバラバラに設定できるので、「このステージだけダッシュが強い」「この敵だけクールダウンが短い」といった調整がインスペクターから直感的にできます。
改造案:スタミナ制限付きダッシュ
もう一歩踏み込むと、「スタミナが一定以上ないとダッシュできない」という制限を入れることもできます。
下記は DashMechanic に追加するイメージのメソッド(スタミナ値は外部から渡される想定)です。
/// <summary>
/// 外部からスタミナ値を受け取り、
/// 足りない場合はダッシュを拒否する例。
/// (DashMechanic 内に追加して使う)
/// </summary>
public bool TryStartDashWithStamina(float currentStamina, float requiredStamina)
{
// すでにダッシュ中 or クールダウン中なら失敗
if (isDashing || isOnCooldown) return false;
// スタミナが足りなければ失敗
if (currentStamina < requiredStamina) return false;
// ここで外部のスタミナ管理コンポーネントに消費を依頼しても良い
// 例: staminaComponent.Consume(requiredStamina);
StartDash();
return true;
}
このように、ダッシュ自体のロジックは DashMechanic に閉じ込めつつ、スタミナ管理やAI制御などは別コンポーネントに分けて連携させると、責務が明確でメンテナンスしやすいプロジェクトになります。
Update に全部書きがちな処理こそ、どんどんコンポーネントに分割していきましょう。
