【Cocos Creator】アタッチするだけ!HiddenWall (隠し通路)の実装方法【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】HiddenWall の実装:アタッチするだけで「触れたら透ける隠し通路」を実現する汎用スクリプト

このコンポーネントは、プレイヤーなどのオブジェクトが触れたタイミングで壁の透明度を下げ、奥が見えるようにする「隠し通路」演出を簡単に実装できるスクリプトです。
ノードにアタッチし、あとはインスペクタで透明度やフェード速度を調整するだけで使えるように設計します。

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

今回の HiddenWall コンポーネントの要件を整理します。

  • 壁として表示されているノードにアタッチするだけで動作する。
  • 「何か」が当たったら透明になり、「離れたら」元の不透明度に戻る。
  • フェード(徐々に透明/不透明)を行い、カクッと切り替わらない。
  • 外部の GameManager やプレイヤー管理スクリプトに依存しない。
  • インスペクタから以下を調整可能にする:
    • 最初の透明度(初期アルファ値)
    • 触れたときの透明度(最低アルファ値)
    • フェード速度
    • トリガー対象のグループ名 or レイヤー(簡易フィルタ)
    • 2D 物理コリジョンを使うか、単純な AABB 当たり判定を使うか

また、防御的な実装として:

  • 必須コンポーネント:
    • Sprite または UIOpacity を持つことを推奨(どちらも無い場合はエラーログ)。
    • 物理判定を使う場合は Collider2D が必要(無い場合は警告ログ)。
  • 利用側が最小限の設定ミスで済むよう、可能な限り自動取得・自動補完を行う。

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

  • usePhysicsTrigger: boolean
    ツールチップ: 「2D 物理コリジョンのトリガーイベントを使用して判定するかどうか。」
    説明: ON の場合は Collider2DonTriggerEnter / onTriggerExit を使用。OFF の場合は、指定ノードとの距離による簡易判定を使う。
  • targetNode: Node | null
    ツールチップ: 「物理を使わない場合に、接触判定の対象とするノード(例: プレイヤー)。」
    説明: usePhysicsTrigger = false のときだけ使用。ここで指定したノードが壁に近づいた/離れたタイミングで透過を切り替える。
  • nonPhysicsTriggerDistance: number
    ツールチップ: 「物理を使わない場合に、targetNode との距離がこの値以下になったら接触とみなす。」
    説明: 単純な距離ベース判定。2D ゲームであれば XZ か XY かはゲーム設計に依存しますが、ここでは 2D を想定して XY 平面で距離を計算する。
  • initialAlpha: number
    ツールチップ: 「開始時の透明度(0〜255)。255 で完全不透明。」
    説明: 壁の通常状態のアルファ値。実行開始時に強制的にこの値に設定される。
  • hitAlpha: number
    ツールチップ: 「プレイヤーが触れている間の透明度(0〜255)。0 に近いほど透ける。」
    説明: 触れたときに目指す透明度。0〜255 の範囲で指定。
  • fadeSpeed: number
    ツールチップ: 「1秒あたりに変化する透明度の量。大きいほど速くフェードする。」
    説明: deltaTime * fadeSpeed だけアルファ値を変化させる。
  • filterGroupName: string
    ツールチップ: 「物理トリガーで有効。指定したグループ名のオブジェクトと接触したときのみ反応する(空文字なら全てに反応)。」
    説明: 物理トリガー時に、otherCollider.node.layer ではなく、otherCollider.node.group(Cocos 3.x ではタグ付け用途に自前プロパティを使うケースも多いですが、ここでは単純な groupName を想定)をチェックする簡易フィルタ。
  • debugLog: boolean
    ツールチップ: 「true にすると、トリガーの開始/終了などのログをコンソールに出力します。」
    説明: 動作確認やデバッグ時に有用。

TypeScriptコードの実装


import { _decorator, Component, Node, Sprite, UIOpacity, Collider2D, ITriggerEvent, Vec3, math, warn, error } from 'cc';
const { ccclass, property } = _decorator;

/**
 * HiddenWall
 * - プレイヤーなどが触れている間だけ透明度を下げる「隠し通路」用コンポーネント。
 * - 他のカスタムスクリプトに依存しない独立コンポーネント。
 */
@ccclass('HiddenWall')
export class HiddenWall extends Component {

    @property({
        tooltip: '2D 物理コリジョンのトリガーイベントを使用して判定するかどうか。\nON: Collider2D の onTriggerEnter/Exit を使用\nOFF: targetNode との距離で簡易判定',
    })
    public usePhysicsTrigger: boolean = true;

    @property({
        tooltip: 'usePhysicsTrigger が false の場合に使用。\nこのノードとの距離が nonPhysicsTriggerDistance 以下になったら「接触」とみなすターゲット(例: プレイヤー)。',
    })
    public targetNode: Node | null = null;

    @property({
        tooltip: 'usePhysicsTrigger が false の場合に使用。\ntargetNode との距離がこの値以下になったら接触とみなす(単位: ワールド座標の長さ)。',
        min: 0,
    })
    public nonPhysicsTriggerDistance: number = 50;

    @property({
        tooltip: '開始時の透明度(0〜255)。255 で完全不透明。\n実行開始時に壁の透明度をこの値にリセットします。',
        min: 0,
        max: 255,
    })
    public initialAlpha: number = 255;

    @property({
        tooltip: 'プレイヤーが触れている間の透明度(0〜255)。\n0 に近いほど透けて見えるようになります。',
        min: 0,
        max: 255,
    })
    public hitAlpha: number = 80;

    @property({
        tooltip: '1秒あたりに変化する透明度の量。\n大きいほど速くフェードします。',
        min: 1,
    })
    public fadeSpeed: number = 300;

    @property({
        tooltip: '物理トリガー使用時のみ有効。\n指定したグループ名のオブジェクトと接触したときだけ反応します。\n空文字のままなら全てのオブジェクトに反応します。',
    })
    public filterGroupName: string = '';

    @property({
        tooltip: 'true にすると、トリガー開始/終了やエラーなどのログをコンソールに出力します。',
    })
    public debugLog: boolean = false;

    // 内部状態
    private _sprite: Sprite | null = null;
    private _uiOpacity: UIOpacity | null = null;
    private _collider: Collider2D | null = null;

    private _isTriggered: boolean = false;      // 現在プレイヤー等が触れているか
    private _currentAlpha: number = 255;        // 実際に適用しているアルファ値
    private _targetAlpha: number = 255;         // この値に向かってフェードする

    onLoad() {
        // Sprite / UIOpacity の取得
        this._sprite = this.getComponent(Sprite);
        this._uiOpacity = this.getComponent(UIOpacity);

        if (!this._sprite && !this._uiOpacity) {
            error('[HiddenWall] Sprite か UIOpacity コンポーネントが見つかりません。このノードにどちらかを追加してください。');
        }

        // Collider2D の取得(物理トリガー使用時のみ)
        if (this.usePhysicsTrigger) {
            this._collider = this.getComponent(Collider2D);
            if (!this._collider) {
                warn('[HiddenWall] usePhysicsTrigger が true ですが、Collider2D が見つかりません。トリガー判定が行えません。');
            }
        }

        // アルファ値の初期化
        this._currentAlpha = math.clamp(this.initialAlpha, 0, 255);
        this._targetAlpha = this._currentAlpha;
        this._applyAlpha(this._currentAlpha);

        if (this.debugLog) {
            console.log('[HiddenWall] onLoad - initialAlpha:', this._currentAlpha, 'usePhysicsTrigger:', this.usePhysicsTrigger);
        }
    }

    start() {
        // 物理トリガーイベントの登録
        if (this.usePhysicsTrigger && this._collider) {
            this._collider.on('onTriggerEnter', this._onTriggerEnter, this);
            this._collider.on('onTriggerExit', this._onTriggerExit, this);
        }
    }

    onDestroy() {
        // イベントの解除(メモリリーク防止)
        if (this.usePhysicsTrigger && this._collider) {
            this._collider.off('onTriggerEnter', this._onTriggerEnter, this);
            this._collider.off('onTriggerExit', this._onTriggerExit, this);
        }
    }

    update(dt: number) {
        // 物理を使わない簡易判定モード
        if (!this.usePhysicsTrigger && this.targetNode) {
            this._updateNonPhysicsTrigger();
        }

        // 現在のトリガー状態に応じて、目標アルファ値を設定
        const desiredAlpha = this._isTriggered
            ? math.clamp(this.hitAlpha, 0, 255)
            : math.clamp(this.initialAlpha, 0, 255);

        if (this._targetAlpha !== desiredAlpha) {
            this._targetAlpha = desiredAlpha;
        }

        // フェード処理
        if (this._currentAlpha !== this._targetAlpha) {
            const direction = this._targetAlpha > this._currentAlpha ? 1 : -1;
            const deltaAlpha = this.fadeSpeed * dt * direction;

            // 目標を超えないように clamp
            if ((direction > 0 && this._currentAlpha + deltaAlpha > this._targetAlpha) ||
                (direction ', ok);
        }

        return ok;
    }

    /**
     * 実際に Sprite / UIOpacity にアルファ値を適用する。
     */
    private _applyAlpha(alpha: number) {
        const a = math.clamp(Math.round(alpha), 0, 255);

        if (this._uiOpacity) {
            this._uiOpacity.opacity = a;
        }

        if (this._sprite) {
            // Sprite の color を通じてアルファを変更
            const color = this._sprite.color;
            color.a = a;
            this._sprite.color = color;
        }
    }
}

コードの主要ポイント解説

  • onLoad
    • SpriteUIOpacity を取得し、どちらも無い場合は error を出して開発者に追加を促します。
    • usePhysicsTrigger が true の場合は Collider2D を取得し、見つからなければ warn を出力します。
    • initialAlpha を現在値と目標値に設定し、即座に反映します。
  • start
    • 物理トリガーモードのときのみ、Collider2DonTriggerEnter / onTriggerExit を登録します。
  • update
    • usePhysicsTrigger = false の場合、_updateNonPhysicsTriggertargetNode との距離を計算し、接触状態を更新します。
    • 現在のトリガー状態に応じて目標アルファ値(initialAlpha or hitAlpha)を決定します。
    • fadeSpeeddt を使って _currentAlpha_targetAlpha に向かって徐々に変化させ、_applyAlpha で実際の見た目に反映します。
  • _updateNonPhysicsTrigger
    • ワールド座標で自分と targetNode の距離を計算し、nonPhysicsTriggerDistance 以下なら接触中とみなします。
    • 接触状態が変わったときだけログを出力(debugLog が true の場合)。
  • _onTriggerEnter / _onTriggerExit
    • 物理トリガーモードで呼ばれるイベントハンドラ。_shouldReactToCollider でフィルタした上で、_isTriggered を true/false に切り替えます。
  • _applyAlpha
    • UIOpacity があれば opacity に直接アルファ値を設定。
    • Sprite があれば color.a にアルファ値を設定。
    • どちらにも対応することで、UI レイヤーでも通常の 2D スプライトでも使えるようにしています。

使用手順と動作確認

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

  1. エディタ左下の Assets パネルで、スクリプトを置きたいフォルダ(例: assets/scripts)を選択します。
  2. そのフォルダ上で右クリック → CreateTypeScript を選択します。
  3. ファイル名を HiddenWall.ts に変更します。
  4. 作成された HiddenWall.ts をダブルクリックして開き、既存のテンプレートコードをすべて削除し、前述の TypeScript コードを丸ごと貼り付けて保存します。

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

  1. Hierarchy パネルで右クリック → Create → 2D オブジェクト(例: Sprite)を選択します。
  2. 作成されたノードの名前を分かりやすく HiddenWallTest などに変更します。
  3. 選択したノードの Inspector を確認し、Sprite コンポーネントが付いていることを確認します。
    • もし付いていなければ、Add ComponentUISprite で追加してください。
  4. UI レイヤーで使う場合は、代わりに UIOpacity を使用しても構いません(Add ComponentUIUIOpacity)。

3. Collider2D の設定(物理トリガーモードを使う場合)

物理コリジョンを使って「プレイヤーがぶつかったら透ける」動作をさせたい場合:

  1. HiddenWallTest ノードを選択します。
  2. Add ComponentPhysics 2D → 適切なコライダー(例: BoxCollider2D)を追加します。
  3. 追加した BoxCollider2DIs Trigger(トリガー)にチェックを入れて、物理的な衝突ではなくトリガー判定のみ行うようにします。

プレイヤー側にも Collider2DRigidBody2D を設定しておくと、2D 物理シミュレーションと連動した自然な動きになります(ただし本コンポーネント自体はプレイヤー側の実装には依存しません)。

4. HiddenWall コンポーネントをアタッチ

  1. HiddenWallTest ノードを選択した状態で、Inspector の下部にある Add Component ボタンをクリックします。
  2. CustomHiddenWall を選択して追加します。

5. プロパティの設定例(物理トリガーモード)

物理トリガーを使って「プレイヤーがぶつかったら透ける」ケースの設定例です。

  1. usePhysicsTriggertrue(デフォルトのまま)
  2. targetNode:未設定のままで OK(物理モードでは使用しません)
  3. nonPhysicsTriggerDistance:デフォルトのままで OK(物理モードでは使用しません)
  4. initialAlpha255(完全不透明な壁)
  5. hitAlpha60100 くらい(好みに応じて調整)
  6. fadeSpeed300600 くらい(数値が大きいほど素早くフェード)
  7. filterGroupName
    • 特定のノード名にだけ反応させたい場合:例としてプレイヤーノード名が Player なら Player と入力。
    • どのオブジェクトと当たっても透けてほしい場合:空文字のままにしておく。
  8. debugLog:動作確認中だけ true にして、ログを見ながら調整するのが便利です。

その後、シーンを再生し、プレイヤー(またはコライダーを持ったオブジェクト)を HiddenWallTest にぶつけてみてください。
接触中は壁が指定した hitAlpha までフェードアウトし、離れると initialAlpha までフェードインして戻るはずです。

6. プロパティの設定例(非物理・距離ベース判定モード)

物理を使わず、「プレイヤーが近づいたら透ける」ようにしたい場合:

  1. HiddenWallTest ノードの HiddenWall コンポーネントで、次のように設定します。
  • usePhysicsTriggerfalse
  • targetNode:プレイヤーノードを Hierarchy からドラッグ&ドロップして割り当て。
  • nonPhysicsTriggerDistance50150 くらい(ゲームスケールに応じて調整)。
  • initialAlpha255
  • hitAlpha60
  • fadeSpeed300
  • filterGroupName:未使用なので空のままで OK。
  • debugLogtrue にすると、距離とトリガー状態がログに出るので調整しやすくなります。

このモードでは、プレイヤーにコライダーや物理ボディが無くても「近づいたら透ける隠し通路」を実現できます。

まとめ

HiddenWall コンポーネントは:

  • 壁ノードにアタッチするだけで、「触れたら透ける隠し通路」演出を簡単に追加できる。
  • 物理トリガー/距離ベース判定の両方に対応しており、ゲームの設計に合わせて選択可能。
  • initialAlpha, hitAlpha, fadeSpeed を変えるだけで、さまざまな雰囲気の壁(うっすら透ける、急に消える、ゆっくりフェードするなど)を表現できる。
  • 外部の GameManager やプレイヤー管理スクリプトに依存せず、完全に独立して動作するため、どのプロジェクトにも簡単に移植可能。

例えば:

  • ダンジョンの隠し通路やシークレットルームの入口。
  • プレイヤーが近づくと姿を現す「幻の壁」。
  • 敵が近づくと透明になり、プレイヤーだけが通れる安全地帯。

といったギミックを、シーン上に壁ノードを配置して HiddenWall をアタッチするだけで量産できます。
一度このコンポーネントをプロジェクトの共通ライブラリとして整備しておけば、以後のゲームでも再利用でき、レベルデザインのスピードアップに大きく貢献します。

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をコピーしました!