【Cocos Creator】アタッチするだけ!MousePeek (マウス視点)の実装方法【TypeScript】

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

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

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

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

Unity 6 C#スクリプト 100本ノック

新品価格
¥1,230から
(2025/12/29 10:28時点)

Cocos Creator100本ノックTypeScriptで書く!

新品価格
¥1,250から
(2025/12/29 10:32時点)

【Cocos Creator 3.8】MousePeek(マウス視点)の実装:アタッチするだけで「キーを押している間だけマウス方向にカメラをずらす」を実現する汎用スクリプト

このコンポーネントは、カメラノードにアタッチしておくだけで、「特定のキーを押している間だけ、マウスカーソルの方向にカメラをスッとずらす」視点操作を実現します。
2Dアクションや見下ろし型ゲームで、プレイヤーの視界を少し先まで見せたいときに便利で、外部の GameManager などに一切依存せず、このスクリプト単体で完結します。


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

1. 機能要件の整理

  • このコンポーネントを カメラノード にアタッチすると、指定したキーを押している間だけ、カメラがマウス位置の方向へオフセット移動する。
  • キーを離すと、カメラは元の位置にスムーズに戻る。
  • マウスの絶対座標ではなく、画面中心からマウス方向への「相対ベクトル」 を使ってカメラをずらす。
  • ずらす距離や反応のスムーズさ、対象キーなどはすべてインスペクタから調整できる。
  • 他のカスタムスクリプトには一切依存せず、このコンポーネント単体で完結する。

2. 外部依存をなくすためのアプローチ

  • Camera コンポーネントの自動取得
    アタッチされたノードに Camera がある前提とし、onLoad()getComponent(Camera) を試みます。
    見つからなかった場合は error ログを出し、処理を無効化します(防御的実装)。
  • イベント登録の自己管理
    マウスとキーボードの入力は systemEvent(3.8 では systemEvent / input)を使い、onEnable() で登録、onDisable() で解除します。他スクリプトとは共有しません。
  • 元位置の保持
    カメラの「基準位置」を _basePosition として内部に保持し、その周りをオフセットさせます。
    他ノード(プレイヤーなど)に追従させたい場合は、そのロジックは別に書き、このコンポーネントは「基準位置からの視点ずらし」だけを担当する、という設計にします。

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

以下のプロパティを用意します:

  • enabledPeek: boolean
    – デフォルト: true
    – このフラグが false のときは、キーを押してもカメラをずらさない(デバッグ用の一時無効化などに便利)。
  • peekKey: KeyCode
    – デフォルト: KeyCode.SHIFT_LEFT
    – 「このキーを押している間だけ視点をずらす」ためのキー。
    – 例: KeyCode.SPACE, KeyCode.SHIFT_LEFT, KeyCode.MOUSE_BUTTON_RIGHT など。
  • maxOffset: number
    – デフォルト: 200
    – 画面中心からマウス方向へのオフセットの最大距離(ワールド座標ベース)。
    – 数値を大きくすると、より遠くまでカメラを動かせます。
  • lerpSpeed: number
    – デフォルト: 8
    – カメラが「現在位置 → 目標位置」へ追従する速度。
    – 値が大きいほど素早く追従し、小さいほどゆっくり滑らかに動きます。
  • useWorldSpace: boolean
    – デフォルト: true
    true: カメラの ワールド座標 を直接動かす(多くのケースでおすすめ)。
    false: カメラの ローカル座標 を動かす(カメラが他ノードの子になっている特殊な構造で使いたい場合)。
  • lockX: boolean
    – デフォルト: false
    true の場合、X方向のオフセットを無効化し、Y方向だけずらす。
  • lockY: boolean
    – デフォルト: false
    true の場合、Y方向のオフセットを無効化し、X方向だけずらす。
  • debugDrawGizmo: boolean
    – デフォルト: false
    true にすると、editor 上でカメラの基準位置と現在位置、オフセット方向を簡易的にログで確認できる(実行時の挙動確認用)。
    – 実際のゲームビルドには影響しません。

TypeScriptコードの実装

以下が完成した MousePeek.ts の全コードです。


import { _decorator, Component, Node, Vec2, Vec3, Camera, input, Input, EventMouse, EventKeyboard, KeyCode, view, math, systemEvent, SystemEventType } from 'cc';
const { ccclass, property } = _decorator;

/**
 * MousePeek
 * カメラにアタッチして使用する、マウス方向への視点ずらしコンポーネント。
 */
@ccclass('MousePeek')
export class MousePeek extends Component {

    @property({
        tooltip: 'このフラグが false の場合、キー入力に関係なく視点ずらしを行いません。'
    })
    public enabledPeek: boolean = true;

    @property({
        tooltip: 'このキーを押している間だけ、マウス方向へカメラをずらします。\n例: KeyCode.SHIFT_LEFT, KeyCode.SPACE など。'
    })
    public peekKey: KeyCode = KeyCode.SHIFT_LEFT;

    @property({
        tooltip: '画面中心からマウス方向へのオフセットの最大距離(ワールド座標)。\n値を大きくすると、より遠くまでカメラを移動させます。'
    })
    public maxOffset: number = 200;

    @property({
        tooltip: 'カメラが目標位置へ追従するスピード。\n値が大きいほど素早く追従し、小さいほどゆっくり移動します。'
    })
    public lerpSpeed: number = 8;

    @property({
        tooltip: 'true: カメラのワールド座標を移動します。\nfalse: カメラノードのローカル座標を移動します。'
    })
    public useWorldSpace: boolean = true;

    @property({
        tooltip: 'true の場合、X方向のオフセットを無効化します(上下方向のみの視点ずらし)。'
    })
    public lockX: boolean = false;

    @property({
        tooltip: 'true の場合、Y方向のオフセットを無効化します(左右方向のみの視点ずらし)。'
    })
    public lockY: boolean = false;

    @property({
        tooltip: 'true にすると、実行中にオフセット情報をログ出力します(デバッグ用)。\nゲームビルドには影響しません。'
    })
    public debugDrawGizmo: boolean = false;

    private _camera: Camera | null = null;
    private _basePosition: Vec3 = new Vec3();      // カメラの基準位置
    private _targetOffset: Vec3 = new Vec3();      // 現在目標としているオフセット
    private _currentOffset: Vec3 = new Vec3();     // 実際に適用中のオフセット(補間用)
    private _isPeekKeyPressed: boolean = false;    // 視点ずらしキーが押下中かどうか
    private _lastMousePos: Vec2 = new Vec2();      // 直近のマウス座標(スクリーン座標)

    onLoad() {
        // カメラコンポーネントを取得
        this._camera = this.getComponent(Camera);
        if (!this._camera) {
            console.error('[MousePeek] このコンポーネントは Camera コンポーネントを持つノードにアタッチしてください。');
        }

        // 初期位置を基準位置として保存
        if (this.useWorldSpace) {
            this._basePosition.set(this.node.worldPosition);
        } else {
            this._basePosition.set(this.node.position);
        }

        this._currentOffset.set(Vec3.ZERO);
        this._targetOffset.set(Vec3.ZERO);
    }

    onEnable() {
        // 入力イベント登録
        input.on(Input.EventType.MOUSE_MOVE, this._onMouseMove, this);
        input.on(Input.EventType.MOUSE_DOWN, this._onMouseDown, this);
        input.on(Input.EventType.MOUSE_UP, this._onMouseUp, this);

        input.on(Input.EventType.KEY_DOWN, this._onKeyDown, this);
        input.on(Input.EventType.KEY_UP, this._onKeyUp, this);
    }

    onDisable() {
        // 入力イベント解除
        input.off(Input.EventType.MOUSE_MOVE, this._onMouseMove, this);
        input.off(Input.EventType.MOUSE_DOWN, this._onMouseDown, this);
        input.off(Input.EventType.MOUSE_UP, this._onMouseUp, this);

        input.off(Input.EventType.KEY_DOWN, this._onKeyDown, this);
        input.off(Input.EventType.KEY_UP, this._onKeyUp, this);
    }

    start() {
        // 実行開始時点でもう一度基準位置を更新しておく(他スクリプトで位置が変わっている可能性に備える)
        if (this.useWorldSpace) {
            this._basePosition.set(this.node.worldPosition);
        } else {
            this._basePosition.set(this.node.position);
        }
    }

    update(deltaTime: number) {
        if (!this._camera) {
            return;
        }

        // 毎フレーム、基準位置を更新しておくことで、
        // 他のスクリプトでカメラを移動させつつ、その位置からのオフセットとして MousePeek を適用できる。
        if (this.useWorldSpace) {
            this._basePosition.set(this.node.worldPosition);
        } else {
            this._basePosition.set(this.node.position);
        }

        // 視点ずらしが有効で、キーが押されている場合のみ、ターゲットオフセットを更新
        if (this.enabledPeek && this._isPeekKeyPressed) {
            this._updateTargetOffsetFromMouse();
        } else {
            // キーが離されている場合は、ターゲットオフセットをゼロに戻す
            this._targetOffset.set(Vec3.ZERO);
        }

        // 現在のオフセットをターゲットオフセットへ補間(滑らかに移動)
        Vec3.lerp(this._currentOffset, this._currentOffset, this._targetOffset, math.clamp01(this.lerpSpeed * deltaTime));

        // 実際のカメラ位置を反映
        const newPos = new Vec3(
            this._basePosition.x + this._currentOffset.x,
            this._basePosition.y + this._currentOffset.y,
            this._basePosition.z + this._currentOffset.z
        );

        if (this.useWorldSpace) {
            this.node.setWorldPosition(newPos);
        } else {
            this.node.setPosition(newPos);
        }

        if (this.debugDrawGizmo) {
            // 簡易的なデバッグ出力(毎フレーム出すと多いので、必要に応じてコメントアウトしてください)
            // console.log('[MousePeek] base=', this._basePosition, 'offset=', this._currentOffset);
        }
    }

    /**
     * マウス移動イベント
     */
    private _onMouseMove(event: EventMouse) {
        event.getLocation(this._lastMousePos);
    }

    /**
     * マウスボタン押下イベント
     * MOUSE_BUTTON_LEFT / RIGHT などを peekKey に設定した場合に対応
     */
    private _onMouseDown(event: EventMouse) {
        const buttonCode = this._convertMouseButtonToKeyCode(event.getButton());
        if (buttonCode === this.peekKey) {
            this._isPeekKeyPressed = true;
        }
        event.getLocation(this._lastMousePos);
    }

    private _onMouseUp(event: EventMouse) {
        const buttonCode = this._convertMouseButtonToKeyCode(event.getButton());
        if (buttonCode === this.peekKey) {
            this._isPeekKeyPressed = false;
        }
        event.getLocation(this._lastMousePos);
    }

    /**
     * キーボード押下イベント
     */
    private _onKeyDown(event: EventKeyboard) {
        if (event.keyCode === this.peekKey) {
            this._isPeekKeyPressed = true;
        }
    }

    private _onKeyUp(event: EventKeyboard) {
        if (event.keyCode === this.peekKey) {
            this._isPeekKeyPressed = false;
        }
    }

    /**
     * 現在のマウス位置から、カメラのオフセット目標を計算する。
     * 画面中心からマウス位置へのベクトルを正規化し、maxOffset を掛ける。
     */
    private _updateTargetOffsetFromMouse() {
        // 画面サイズを取得
        const size = view.getVisibleSize();
        const screenCenter = new Vec2(size.width * 0.5, size.height * 0.5);

        // 画面中心 → マウス位置 へのベクトル
        const dir = new Vec2(
            this._lastMousePos.x - screenCenter.x,
            this._lastMousePos.y - screenCenter.y
        );

        if (dir.lengthSqr() <= 0.0001) {
            // ほぼ中心ならオフセットなし
            this._targetOffset.set(Vec3.ZERO);
            return;
        }

        dir.normalize();

        // ロック設定に応じて軸を制限
        if (this.lockX) {
            dir.x = 0;
        }
        if (this.lockY) {
            dir.y = 0;
        }

        // 再度、ゼロベクトルでないか確認
        if (dir.lengthSqr() <= 0.0001) {
            this._targetOffset.set(Vec3.ZERO);
            return;
        }

        // 2Dゲーム想定のため、Z はそのまま(0 のまま)
        this._targetOffset.set(
            dir.x * this.maxOffset,
            dir.y * this.maxOffset,
            0
        );
    }

    /**
     * マウスボタンインデックスを KeyCode に変換するヘルパー。
     * - 左クリック: KeyCode.MOUSE_BUTTON_LEFT
     * - 右クリック: KeyCode.MOUSE_BUTTON_RIGHT
     * - 中クリック: KeyCode.MOUSE_BUTTON_MIDDLE
     */
    private _convertMouseButtonToKeyCode(button: number): KeyCode {
        switch (button) {
            case EventMouse.BUTTON_LEFT:
                return KeyCode.MOUSE_BUTTON_LEFT;
            case EventMouse.BUTTON_RIGHT:
                return KeyCode.MOUSE_BUTTON_RIGHT;
            case EventMouse.BUTTON_MIDDLE:
                return KeyCode.MOUSE_BUTTON_MIDDLE;
            default:
                return KeyCode.NONE;
        }
    }
}

コードのポイント解説

  • onLoad()
    – アタッチされたノードから Camera コンポーネントを取得し、存在しなければ console.error を出して処理を止めます。
    – カメラの現在位置を _basePosition として保存し、オフセット計算の基準にします。
  • onEnable() / onDisable()
    input.on(...) でマウス・キーボードイベントを登録/解除します。
    – このコンポーネント単体で入力を完結させるため、外部の InputManager などには依存しません。
  • update(deltaTime)
    – 毎フレーム、カメラの「基準位置」を更新(他スクリプトで移動させられていても、その位置からオフセットを足す設計)。
    enabledPeek_isPeekKeyPressed をチェックし、必要に応じて _updateTargetOffsetFromMouse() で目標オフセットを更新。
    Vec3.lerp_currentOffset_targetOffset に補間し、スムーズなカメラ移動を実現。
  • _updateTargetOffsetFromMouse()
    view.getVisibleSize() で画面サイズを取得し、画面中心座標を計算。
    – 「画面中心 → マウス位置」のベクトルを正規化し、maxOffset を掛けてオフセット量に変換。
    lockX / lockY が有効な場合は、その軸を 0 にして一方向だけの視点ずらしにできます。
  • マウスボタン対応
    peekKeyKeyCode.MOUSE_BUTTON_RIGHT などを設定した場合に備え、
    _convertMouseButtonToKeyCode() でマウスボタンを KeyCode に変換して扱っています。

使用手順と動作確認

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

  1. エディタの Assets パネルで任意のフォルダ(例: assets/scripts)を右クリックします。
  2. Create → TypeScript を選択し、ファイル名を MousePeek.ts にします。
  3. 自動生成された MousePeek.ts をダブルクリックして開き、内容をすべて削除してから、前述のコードをそのまま貼り付けて保存します。

2. テスト用シーンとカメラの準備

  1. Hierarchy パネルで、テスト用のシーンを開くか、新規シーンを作成します。
  2. すでに Main Camera が存在する場合は、それを利用します。存在しない場合は:
    • Hierarchy パネルの空白部分を右クリック → Create → 3D Object → Camera を選択してカメラを作成します。
  3. カメラノードを選択し、Inspector パネルで Camera コンポーネントが付いていることを確認します。
    もし付いていなければ、Add Component → Rendering → Camera から追加してください。

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

  1. Hierarchy でカメラノード(例: Main Camera)を選択します。
  2. Inspector パネルの下部で Add Component → Custom → MousePeek を選択し、コンポーネントを追加します。

4. プロパティの設定例

Inspector 上で、MousePeek コンポーネントの各プロパティを以下のように設定してみます:

  • Enabled Peek: true
  • Peek Key: KeyCode.SHIFT_LEFT(一覧から選択)
  • Max Offset: 250
  • Lerp Speed: 10
  • Use World Space: true
  • Lock X: false
  • Lock Y: false
  • Debug Draw Gizmo: false(最初はオフでOK)

5. プレビューでの動作確認

  1. エディタ右上の Play ボタン(再生)を押して、ゲームビューで再生します。
  2. 再生中に、マウスカーソルをゲームビュー内でぐるぐる動かしてみます。この時点では、まだカメラは動きません。
  3. 左Shiftキー(Peek Key で指定したキー)を押しっぱなしにします。
  4. 左Shiftを押したままマウスを動かすと、画面中心からマウスの方向へ、カメラがスッとオフセットされていきます。
    • マウスを画面の右上に持っていくと、カメラも右上方向へ移動します。
    • キーを離すと、カメラは元の位置にスムーズに戻ります。

6. よくある調整パターン

  • オフセットが大きすぎる/小さすぎる
    Max Offset を調整します。
    – 2Dアクションなら 150〜300 程度、見下ろしマップなら 300〜600 程度を目安にするとよいです。
  • 動きがカクカクする/遅すぎる
    Lerp Speed を上げると、目標位置への追従が速くなります(例: 5 → 15)。
    – ピタッと追従させたい場合は 20 以上にしてみてください。
  • 左右だけ or 上下だけに動かしたい
    – 左右だけ → Lock Ytrue に。
    – 上下だけ → Lock Xtrue に。
  • 右クリックで視点ずらししたい
    Peek KeyKeyCode.MOUSE_BUTTON_RIGHT に変更します。

まとめ

MousePeek コンポーネントは、

  • カメラノードにアタッチするだけで、
  • 特定キーを押している間だけマウス方向へ視点をずらし、
  • キーを離すとスムーズに元の位置に戻る

という「マウス視点移動」を、外部スクリプトに一切依存せずに実現します。

このスクリプト単体で完結しているため、

  • プレイヤー追従カメラに「視界を少し先まで伸ばす」機能を後付けする
  • 探索ゲームで「Shiftキーを押している間だけ遠くを見渡す」ギミックを簡単に追加する
  • 右クリックを押している間だけ「敵のいる方向を少し覗き込む」などの演出を作る

といった用途に、そのまま再利用できます。

プロパティから挙動を細かく調整できるようにしているので、ゲームごとに「どのくらい動かすか」「どのキーで発動するか」を変えながら、同じコンポーネントを使い回せます。
新しいプロジェクトでも MousePeek.ts を 1 ファイルコピーしてカメラにアタッチするだけで、同じ視点操作をすぐに導入できるはずです。

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

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

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

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

Unity 6 C#スクリプト 100本ノック

新品価格
¥1,230から
(2025/12/29 10:28時点)

Cocos Creator100本ノックTypeScriptで書く!

新品価格
¥1,250から
(2025/12/29 10:32時点)

URLをコピーしました!