【Cocos Creator】アタッチするだけ!DashMechanic (ダッシュ機能)の実装方法【TypeScript】

Godot 4ゲーム制作 実践ドリル 100本ノック

新品価格
¥1,250から
(2025/12/13 21:27時点)

脱・初心者!Godot 4 ゲーム開発の「2歩目」

新品価格
¥831から
(2025/12/13 21:39時点)

Godot4ローグライク入門 ~ダンジョン自動生成~

新品価格
¥831から
(2025/12/13 21:44時点)

Godot4& GDScriptではじめる 2Dゲーム開発レシピ

新品価格
¥590から
(2025/12/13 21:46時点)

【Cocos Creator 3.8】DashMechanic の実装:アタッチするだけで「一時的な速度倍増&クールダウン付きダッシュ」を実現する汎用スクリプト

プレイヤーキャラクターやエネミーに「一瞬だけ素早く動くダッシュ」を持たせたい場面は多いですが、毎回ロジックを書くのは面倒です。この記事では、任意のノードにアタッチするだけで「入力に応じて速度を一時的に倍増し、クールダウンを自動管理してくれる」汎用ダッシュコンポーネント DashMechanic を実装します。

このコンポーネントは、Rigidbody2D を使った物理移動にも、スクリプトで直接 position を動かすタイプの移動にも対応できるように設計します。外部の GameManager や InputManager には一切依存せず、インスペクタの設定だけで完結します。


コンポーネントの設計方針

機能要件の整理

  • 特定の入力(キーボードやゲームパッドのボタン)でダッシュを開始する。
  • ダッシュ中は、通常速度に倍率をかけた「ダッシュ速度」を返す/適用できるようにする。
  • ダッシュ時間が経過したら自動的に終了し、通常速度に戻る。
  • ダッシュ終了後は、クールダウン時間が経過するまで再ダッシュ不可。
  • 外部スクリプトに依存せず、このコンポーネント単体で完結する。
  • 移動ロジックの種類に依らず使えるように、「現在の速度倍率」や「ダッシュ中かどうか」などを公開する。

今回は「親の移動速度を一時的に倍増」とあるため、このコンポーネント自体が直接移動処理を行うのではなく、「速度倍率(speedMultiplier)」を提供する設計にします。

  • 普段は speedMultiplier = 1.0
  • ダッシュ中は speedMultiplier = dashMultiplier(例: 2.0)

移動を行う別のスクリプト側では、

// 例:他スクリプト(依存はしないが、こう使える)
const baseSpeed = 300;
const dash = this.node.getComponent(DashMechanic);
const currentSpeed = dash ? baseSpeed * dash.speedMultiplier : baseSpeed;

のように参照することで、汎用的に利用できます。また、Rigidbody2D が付いている場合は、オプションで「速度を直接ブーストするモード」も用意します。

インスペクタで設定可能なプロパティ

以下のようなプロパティを用意します。

  • enableInput (boolean)
    • 入力監視を有効にするかどうか。
    • true の場合、指定キー/ボタンでダッシュを発動。
    • false の場合、triggerDash() メソッドからのみダッシュ開始。
  • dashKey (KeyCode)
    • キーボードでダッシュを開始するキー。
    • デフォルトは KeyCode.SHIFT_LEFT
  • gamepadButtonIndex (number)
    • ゲームパッドのボタンインデックス(0〜)。
    • -1 の場合はゲームパッド入力を無効化。
  • dashMultiplier (number)
    • ダッシュ時の速度倍率。
    • 例: 2.0 なら通常の 2 倍の速度。
  • dashDuration (number)
    • ダッシュが継続する時間(秒)。
  • cooldownDuration (number)
    • ダッシュ終了後、再度ダッシュ可能になるまでの時間(秒)。
  • useRigidbody2D (boolean)
    • true の場合、Rigidbody2D の linearVelocity を直接ブーストする。
    • false の場合は速度倍率の提供のみ。
  • rigidbodyBoostScale (number)
    • Rigidbody2D をブーストする際の倍率。
    • 通常は dashMultiplier と同じ値にするのが自然。
  • allowAirDash (boolean)
    • 空中でもダッシュを許可するか(2Dアクション向け)。
    • 今回は汎用的なフラグとして用意し、実際の「地上判定」は外部スクリプトが判断して triggerDash を呼ぶ運用も可能。
  • debugLog (boolean)
    • ダッシュ開始/終了/クールダウンなどの状態変化をログ出力するか。

公開用の読み取り専用プロパティ

インスペクタには表示しないが、他スクリプトから参照できるように以下を用意します。

  • isDashing: 現在ダッシュ中かどうか。
  • isInCooldown: クールダウン中かどうか。
  • speedMultiplier: 現在の速度倍率(1.0 or dashMultiplier)。
  • remainingDashTime: 残りダッシュ時間(秒)。
  • remainingCooldownTime: 残りクールダウン時間(秒)。
  • triggerDash(): 外部スクリプトからダッシュを強制開始するためのメソッド。

TypeScriptコードの実装


import { _decorator, Component, KeyCode, input, Input, EventKeyboard, systemEvent, SystemEventType, EventGamepad, game, RigidBody2D, Vec2 } from 'cc';
const { ccclass, property } = _decorator;

/**
 * DashMechanic
 * 任意のノードにアタッチして使える、汎用ダッシュ機能コンポーネント。
 * - 入力(キーボード / ゲームパッド)または triggerDash() 呼び出しでダッシュ開始
 * - 一定時間だけ速度倍率を上げ、その後クールダウン
 * - Rigidbody2D を使う場合は速度ベクトルを直接ブーストすることも可能
 */
@ccclass('DashMechanic')
export class DashMechanic extends Component {

    // ===== インスペクタ設定項目 =====

    @property({
        tooltip: 'インプット(キーボード / ゲームパッド)からダッシュを開始できるようにするか。\nfalse の場合は triggerDash() からのみダッシュ開始できます。'
    })
    public enableInput: boolean = true;

    @property({
        tooltip: 'ダッシュを発動するキーボードキー。\nデフォルトは左シフトキーです。'
    })
    public dashKey: KeyCode = KeyCode.SHIFT_LEFT;

    @property({
        tooltip: 'ダッシュを発動するゲームパッドボタンのインデックス。\n0 以上で有効。-1 の場合はゲームパッド入力を無効化します。'
    })
    public gamepadButtonIndex: number = -1;

    @property({
        tooltip: 'ダッシュ時の速度倍率。\n例: 2.0 なら通常の 2 倍の速度になります。'
    })
    public dashMultiplier: number = 2.0;

    @property({
        tooltip: 'ダッシュが継続する時間(秒)。'
    })
    public dashDuration: number = 0.2;

    @property({
        tooltip: 'ダッシュ終了後、再度ダッシュ可能になるまでのクールダウン時間(秒)。'
    })
    public cooldownDuration: number = 1.0;

    @property({
        tooltip: 'Rigidbody2D を使っている場合に、ダッシュ中に速度ベクトルを直接ブーストするかどうか。\ntrue の場合、現在の速度に rigidbodyBoostScale を掛けて一時的に加速させます。'
    })
    public useRigidbody2D: boolean = false;

    @property({
        tooltip: 'Rigidbody2D の速度をブーストする倍率。\nuseRigidbody2D が true のときに使用されます。',
        visible() {
            return this.useRigidbody2D;
        }
    })
    public rigidbodyBoostScale: number = 2.0;

    @property({
        tooltip: '空中ダッシュを許可するかどうかを示すフラグです。\n実際の地上判定はこのコンポーネントでは行いませんが、\n外部スクリプトから triggerDash() を呼ぶ際の条件分岐などに利用できます。'
    })
    public allowAirDash: boolean = true;

    @property({
        tooltip: 'ダッシュ開始 / 終了 / クールダウンの状態をログ出力するかどうか。'
    })
    public debugLog: boolean = false;

    // ===== 実行時状態(インスペクタ非表示) =====

    private _isDashing: boolean = false;
    private _isInCooldown: boolean = false;
    private _dashTimer: number = 0;
    private _cooldownTimer: number = 0;

    private _speedMultiplier: number = 1.0;

    private _rigidbody2D: RigidBody2D | null = null;

    // Rigidbody2D ブースト前の速度を保存しておくための変数
    private _originalVelocity: Vec2 | null = null;

    // ===== 外部公開用の getter =====

    /**
     * 現在ダッシュ中かどうか。
     */
    public get isDashing(): boolean {
        return this._isDashing;
    }

    /**
     * 現在クールダウン中かどうか。
     */
    public get isInCooldown(): boolean {
        return this._isInCooldown;
    }

    /**
     * 現在の速度倍率。通常時は 1.0、ダッシュ中は dashMultiplier。
     * 移動スクリプト側で baseSpeed * speedMultiplier のように利用してください。
     */
    public get speedMultiplier(): number {
        return this._speedMultiplier;
    }

    /**
     * 残りダッシュ時間(秒)。ダッシュしていないときは 0。
     */
    public get remainingDashTime(): number {
        return this._isDashing ? this._dashTimer : 0;
    }

    /**
     * 残りクールダウン時間(秒)。クールダウン中でないときは 0。
     */
    public get remainingCooldownTime(): number {
        return this._isInCooldown ? this._cooldownTimer : 0;
    }

    // ===== ライフサイクル =====

    onLoad() {
        // Rigidbody2D がある場合は取得
        this._rigidbody2D = this.getComponent(RigidBody2D);
        if (this.useRigidbody2D && !this._rigidbody2D) {
            console.error('[DashMechanic] useRigidbody2D が true ですが、Rigidbody2D コンポーネントが見つかりません。ノードに Rigidbody2D を追加するか、useRigidbody2D を false にしてください。');
        }

        if (this.enableInput) {
            // キーボード入力の監視を開始
            input.on(Input.EventType.KEY_DOWN, this.onKeyDown, this);
            // ゲームパッド入力は update 内でポーリングする方式を採用
        }
    }

    start() {
        // 初期状態を明示
        this._isDashing = false;
        this._isInCooldown = false;
        this._dashTimer = 0;
        this._cooldownTimer = 0;
        this._speedMultiplier = 1.0;
    }

    update(deltaTime: number) {
        // ダッシュ中のタイマー更新
        if (this._isDashing) {
            this._dashTimer -= deltaTime;
            if (this._dashTimer <= 0) {
                this.endDash();
            }
        }

        // クールダウン中のタイマー更新
        if (this._isInCooldown) {
            this._cooldownTimer -= deltaTime;
            if (this._cooldownTimer <= 0) {
                this._cooldownTimer = 0;
                this._isInCooldown = false;
                if (this.debugLog) {
                    console.log('[DashMechanic] クールダウン終了。再度ダッシュ可能になりました。');
                }
            }
        }

        // ゲームパッド入力のチェック(enableInput が true のときのみ)
        if (this.enableInput && this.gamepadButtonIndex >= 0) {
            this.checkGamepadInput();
        }
    }

    onDestroy() {
        if (this.enableInput) {
            input.off(Input.EventType.KEY_DOWN, this.onKeyDown, this);
        }
    }

    // ===== 入力処理 =====

    private onKeyDown(event: EventKeyboard) {
        if (!this.enableInput) {
            return;
        }

        if (event.keyCode === this.dashKey) {
            this.tryStartDash();
        }
    }

    /**
     * ゲームパッドのボタン入力をチェックします。
     * Cocos Creator 3.8 では game.input.gamepads から状態を取得できます。
     */
    private checkGamepadInput() {
        const gamepads = game.input.gamepads;
        if (!gamepads || gamepads.length === 0) {
            return;
        }

        for (let i = 0; i < gamepads.length; i++) {
            const pad = gamepads[i];
            if (!pad) continue;

            const buttons = pad.buttons;
            if (!buttons || this.gamepadButtonIndex < 0 || this.gamepadButtonIndex >= buttons.length) {
                continue;
            }

            const button = buttons[this.gamepadButtonIndex];
            // pressed が true の瞬間だけ拾いたいが、
            // ここでは簡易的に「押されている間に一度だけ発動」するようにする
            if (button.pressed) {
                // ダッシュ開始を試みる(連打防止は tryStartDash 内で管理)
                this.tryStartDash();
                // 1 フレームで複数回発動しないように break
                break;
            }
        }
    }

    // ===== ダッシュ制御ロジック =====

    /**
     * 入力や外部から呼ばれたときに、ダッシュ開始可能か判定し、可能なら開始する。
     */
    private tryStartDash() {
        if (this._isDashing) {
            if (this.debugLog) {
                console.log('[DashMechanic] すでにダッシュ中のため、再ダッシュはできません。');
            }
            return;
        }

        if (this._isInCooldown) {
            if (this.debugLog) {
                console.log('[DashMechanic] クールダウン中のため、ダッシュできません。残りクールダウン時間: ', this._cooldownTimer.toFixed(2));
            }
            return;
        }

        this.startDash();
    }

    /**
     * 外部スクリプトから強制的にダッシュを開始したい場合に使用します。
     * クールダウンや連続ダッシュの制限は tryStartDash() と同じく適用されます。
     */
    public triggerDash(): void {
        this.tryStartDash();
    }

    /**
     * ダッシュ開始処理。
     */
    private startDash() {
        this._isDashing = true;
        this._dashTimer = this.dashDuration;
        this._speedMultiplier = this.dashMultiplier;

        if (this.debugLog) {
            console.log('[DashMechanic] ダッシュ開始。継続時間:', this.dashDuration, '秒 / 速度倍率:', this.dashMultiplier);
        }

        // Rigidbody2D を使う場合は速度ベクトルをブースト
        if (this.useRigidbody2D && this._rigidbody2D) {
            const currentVel = this._rigidbody2D.linearVelocity;
            this._originalVelocity = currentVel.clone();
            const boosted = new Vec2(currentVel.x * this.rigidbodyBoostScale, currentVel.y * this.rigidbodyBoostScale);
            this._rigidbody2D.linearVelocity = boosted;

            if (this.debugLog) {
                console.log('[DashMechanic] Rigidbody2D 速度ブースト:', currentVel.toString(), '→', boosted.toString());
            }
        }
    }

    /**
     * ダッシュ終了処理。
     */
    private endDash() {
        if (!this._isDashing) {
            return;
        }

        this._isDashing = false;
        this._dashTimer = 0;
        this._speedMultiplier = 1.0;

        if (this.debugLog) {
            console.log('[DashMechanic] ダッシュ終了。クールダウン開始。クールダウン時間:', this.cooldownDuration, '秒');
        }

        // Rigidbody2D の速度を元に戻す(ブースト前の速度が記録されている場合)
        if (this.useRigidbody2D && this._rigidbody2D && this._originalVelocity) {
            this._rigidbody2D.linearVelocity = this._originalVelocity;
            if (this.debugLog) {
                console.log('[DashMechanic] Rigidbody2D 速度を元に戻しました:', this._originalVelocity.toString());
            }
            this._originalVelocity = null;
        }

        // クールダウンを開始
        if (this.cooldownDuration > 0) {
            this._isInCooldown = true;
            this._cooldownTimer = this.cooldownDuration;
        } else {
            this._isInCooldown = false;
            this._cooldownTimer = 0;
        }
    }
}

コードのポイント解説

  • onLoad
    • getComponent(RigidBody2D) で Rigidbody2D を取得し、useRigidbody2D が true なのに見つからない場合は console.error で警告。
    • enableInput が true のときだけ input.on(KEY_DOWN) を登録。
  • update
    • ダッシュ中は _dashTimer を減算し、0 以下になったら endDash()
    • クールダウン中は _cooldownTimer を減算し、0 以下でクールダウン解除。
    • ゲームパッド入力は毎フレーム checkGamepadInput() でポーリング。
  • ダッシュ制御
    • tryStartDash() で「すでにダッシュ中」「クールダウン中」をチェック。
    • 問題なければ startDash() を呼び、_isDashing_speedMultiplier を更新。
    • endDash() で速度倍率を 1.0 に戻し、クールダウン開始。
  • Rigidbody2D 連携
    • useRigidbody2D が true かつ Rigidbody2D がある場合のみ、linearVelocityrigidbodyBoostScale を掛けてブースト。
    • 元の速度は _originalVelocity に保存し、ダッシュ終了時に復元。
  • 外部からの利用
    • speedMultiplier を参照することで、移動スクリプトは「ダッシュ中かどうか」を意識せずに速度を調整可能。
    • triggerDash() を呼べば、独自の入力システムや地上判定と組み合わせてダッシュ開始を制御できる。

使用手順と動作確認

1. スクリプトファイルの作成

  1. エディタの Assets パネルで、任意のフォルダ(例: assets/scripts)を右クリックします。
  2. Create → TypeScript を選択し、ファイル名を DashMechanic.ts にします。
  3. 自動生成されたコードをすべて削除し、本記事の 「TypeScriptコードの実装」 セクションのコードを丸ごと貼り付けて保存します。

2. テスト用ノードの作成

ダッシュの効果を確認するために、簡単なプレイヤーノードを作成します。

  1. Hierarchy パネルで右クリック → Create → 2D Object → Sprite を選択し、名前を Player に変更します。
  2. (任意)Inspector で Sprite の画像(Texture)を設定して、見やすくしておきます。
  3. 物理挙動で試したい場合は、Player ノードを選択し、Inspector の Add Component → Physics 2D → RigidBody2D を追加します。
    • Rigidbody2D を使う場合は、Body Type を Dynamic に設定しておきます。

3. DashMechanic コンポーネントのアタッチ

  1. HierarchyPlayer ノードを選択します。
  2. Inspector の下部にある Add Component ボタンをクリックします。
  3. Custom Component(または Custom)の中から DashMechanic を選択して追加します。

4. インスペクタでプロパティを設定

DashMechanic のプロパティを、まずは以下のように設定してみましょう。

  • enableInput: チェック(オン)
  • dashKey: SHIFT_LEFT(デフォルトのまま)
  • gamepadButtonIndex: -1(ゲームパッドは今回は無効)
  • dashMultiplier: 2.0
  • dashDuration: 0.3
  • cooldownDuration: 1.0
  • useRigidbody2D:
    • Rigidbody2D を追加した場合: チェック(オン)
    • Rigidbody2D を使わない場合: チェックを外す(オフ)
  • rigidbodyBoostScale: 2.0(useRigidbody2D をオンにした場合のみ表示されます)
  • allowAirDash: 任意(今回は挙動には直接影響しません)
  • debugLog: チェック(オンにすると状態変化が Console に表示されて分かりやすいです)

5. 簡易的な移動スクリプトと連携してテスト

DashMechanic は「速度倍率」を提供するコンポーネントなので、実際に動かして確認するために、簡易移動スクリプトを作ってみます。(あくまで例であり、本記事のコンポーネント自体はこれに依存しません)

  1. Assets パネルで右クリック → Create → TypeScript を選択し、SimpleMover.ts という名前のスクリプトを作成します。
  2. 以下のコードを貼り付けます。

import { _decorator, Component, Vec3, input, Input, EventKeyboard, KeyCode } from 'cc';
import { DashMechanic } from './DashMechanic';
const { ccclass, property } = _decorator;

/**
 * 矢印キーで左右に移動するだけの簡易プレイヤー移動。
 * DashMechanic の speedMultiplier を利用してダッシュ速度を反映します。
 */
@ccclass('SimpleMover')
export class SimpleMover extends Component {

    @property({
        tooltip: '通常時の移動速度(単位: 単位/秒)。'
    })
    public moveSpeed: number = 300;

    private _moveDir: number = 0;
    private _dash: DashMechanic | null = null;

    onLoad() {
        // DashMechanic を取得(なくても動作するように防御的に扱う)
        this._dash = this.getComponent(DashMechanic);
        if (!this._dash) {
            console.warn('[SimpleMover] DashMechanic が同じノードに見つかりません。ダッシュなしで通常移動のみ行います。');
        }

        input.on(Input.EventType.KEY_DOWN, this.onKeyDown, this);
        input.on(Input.EventType.KEY_UP, this.onKeyUp, this);
    }

    onDestroy() {
        input.off(Input.EventType.KEY_DOWN, this.onKeyDown, this);
        input.off(Input.EventType.KEY_UP, this.onKeyUp, this);
    }

    private onKeyDown(event: EventKeyboard) {
        if (event.keyCode === KeyCode.ARROW_LEFT) {
            this._moveDir = -1;
        } else if (event.keyCode === KeyCode.ARROW_RIGHT) {
            this._moveDir = 1;
        }
    }

    private onKeyUp(event: EventKeyboard) {
        if ((event.keyCode === KeyCode.ARROW_LEFT && this._moveDir === -1) ||
            (event.keyCode === KeyCode.ARROW_RIGHT && this._moveDir === 1)) {
            this._moveDir = 0;
        }
    }

    update(deltaTime: number) {
        if (this._moveDir === 0) {
            return;
        }

        const baseSpeed = this.moveSpeed;
        const dashMultiplier = this._dash ? this._dash.speedMultiplier : 1.0;
        const currentSpeed = baseSpeed * dashMultiplier;

        const move = currentSpeed * this._moveDir * deltaTime;
        const pos = this.node.position;
        this.node.setPosition(new Vec3(pos.x + move, pos.y, pos.z));
    }
}

このスクリプトを Player ノードにアタッチします。

  1. Hierarchy で Player を選択。
  2. Inspector → Add Component → Custom → SimpleMover を選択。
  3. moveSpeed を 300〜500 くらいに設定。

6. ゲームプレイで動作確認

  1. エディタ上部の Play ボタンを押してゲームを実行します。
  2. 実行中に 左右矢印キーで Player を左右に動かします。
  3. 移動中に 左Shiftキー を押すと、一定時間だけ移動速度が上がり、その後元の速度に戻ることを確認します。
  4. 連続で Shift を押しても、クールダウン中はダッシュできないことを確認します。(Console にログが出ていれば状態が分かりやすいです)

Rigidbody2D を使う場合は、物理挙動を確認しやすいように、ステージに床の Collider を置き、重力で落下するようにしてから DashMechanic の useRigidbody2D をオンにしてテストすると、「速度ベクトルが一瞬だけブーストされる」挙動を確認できます。


まとめ

この記事では、Cocos Creator 3.8 向けに、

  • 単体で完結し、他のスクリプトに依存しない
  • インスペクタから入力・倍率・時間を柔軟に調整できる
  • Rigidbody2D の有無にかかわらず利用可能

という条件を満たす、汎用ダッシュコンポーネント DashMechanic を実装しました。

このコンポーネントを使えば、

  • プレイヤーキャラクターのダッシュ
  • エネミーの突進攻撃(一定時間だけ高速で移動させる)
  • ギミックの一時的な速度変化(移動床など)

といった要素を、ノードにアタッチしてパラメータを調整するだけで素早く実装できます。移動ロジック側は speedMultiplier を掛け算するだけで良いため、プロジェクト全体の再利用性や保守性も向上します。

必要に応じて、

  • ダッシュ開始・終了時にエフェクトやサウンドを再生するフック
  • 方向付きダッシュ(入力方向に応じてベクトルを変える)
  • スタミナ消費と連動したダッシュ制限

などを追加していけば、よりリッチなアクションゲームのベースとしても活用できます。まずは本記事の DashMechanic をそのまま導入し、自分のプロジェクトに合わせてカスタマイズしてみてください。

Godot 4ゲーム制作 実践ドリル 100本ノック

新品価格
¥1,250から
(2025/12/13 21:27時点)

脱・初心者!Godot 4 ゲーム開発の「2歩目」

新品価格
¥831から
(2025/12/13 21:39時点)

Godot4ローグライク入門 ~ダンジョン自動生成~

新品価格
¥831から
(2025/12/13 21:44時点)

Godot4& GDScriptではじめる 2Dゲーム開発レシピ

新品価格
¥590から
(2025/12/13 21:46時点)

URLをコピーしました!