Godotでアクションゲームやシューティングを作っていると、デバッグ中に「無敵モードほしいな…」と思う瞬間、めちゃくちゃ多いですよね。
でも、素直に実装しようとすると、

  • プレイヤーの take_damage() の中に if god_mode: return を書いたり
  • 敵やトラップ側の攻撃判定に「プレイヤーが無敵なら当てない」条件を入れたり
  • シーンごとに「デバッグ用フラグ」をバラバラに置いてしまったり

…と、あちこちに「無敵モード」の条件分岐が散らばっていきます。
継承ベースで PlayerBasegod_mode を持たせて、そこから全部のプレイヤーを派生させる、なんてやり方もできますが、結局「無敵モードのためだけに継承構造をいじる」のは本末転倒ですよね。

そこで今回は、「無敵モード」をひとつの独立したコンポーネントとして切り出してしまいましょう。
プレイヤーや敵にポン付けするだけで、HPが減らなくなり、攻撃判定も自動で無視できる GodMode コンポーネントを実装していきます。

【Godot 4】デバッグが一瞬で快適に!「GodMode」コンポーネント

この「GodMode」コンポーネントの思想はシンプルです。

  • 「無敵かどうか」の状態をコンポーネントに集約する
  • ダメージ処理側は「GodMode を見に行くだけ」にする
  • シーンのどこにでも、必要なときだけアタッチして使い回せる

深い継承ツリーや、巨大な Player.gd にデバッグ用のコードを足していくのではなく、
「無敵」という機能を 1 ノード・1 スクリプトに閉じ込めて合成で足す、というアプローチですね。


フルコード:GodMode.gd


extends Node
class_name GodMode
## GodMode (無敵モード) コンポーネント
##
## ・デバッグ用の「無敵モード」を提供するコンポーネントです。
## ・HPが減らなくなり、攻撃判定を無視するための「単一の真実のソース」として機能します。
##
## 使い方の基本:
##   - 無敵にしたいオブジェクト(プレイヤーや敵など)の子ノードとして、このコンポーネントを追加します。
##   - ダメージ処理側では、このコンポーネントを探して is_invincible() をチェックします。
##   - コンソールやデバッグUIから toggle() を呼んで ON/OFF することもできます。

@export var enabled: bool = true:
    ## 無敵モードが有効かどうか。
    ## デフォルト true にしておくと、シーンを再生した瞬間から無敵になります。
    ## テストプレイ中だけ有効にしたいなら、false にしておいて、
    ## 入力やコンソールから toggle() で切り替えるのもアリです。
    set(value):
        enabled = value
        _apply_debug_visual()

@export var show_debug_icon: bool = true:
    ## 無敵中に「デバッグ用のアイコン」を表示するかどうか。
    ## ON にしておくと、エディタ/ゲーム画面上で今どのキャラが無敵か一目でわかります。
    set(value):
        show_debug_icon = value
        _apply_debug_visual()

@export var debug_icon_color: Color = Color(1, 1, 0, 0.6):
    ## 無敵中のデバッグアイコンの色(デフォルトは半透明の黄色)。
    set(value):
        debug_icon_color = value
        _apply_debug_visual()

@export var debug_icon_radius: float = 16.0:
    ## デバッグアイコンの半径(円のサイズ)。
    set(value):
        debug_icon_radius = value
        _apply_debug_visual()

## 内部用: デバッグ表示用の子ノード(必要なら自動生成)
var _debug_draw_node: Node2D


func _ready() -> void:
    ## シーンに追加されたときに、必要に応じてデバッグ表示をセットアップします。
    _ensure_debug_draw_node()
    _apply_debug_visual()

    # エディタ上でのテストもしやすいように、ログを出しておきます。
    # (うるさい場合はコメントアウトしてください)
    if Engine.is_editor_hint():
        return
    if enabled:
        print_debug("[GodMode] Enabled on: ", get_parent())
    else:
        print_debug("[GodMode] Disabled on: ", get_parent())


func is_invincible() -> bool:
    ## 外部から参照するための API。
    ## 攻撃・ダメージ処理側は、これを呼んで true ならダメージをスキップします。
    return enabled


func set_enabled(value: bool) -> void:
    ## スクリプトから有効/無効を切り替えるときに使うヘルパー。
    enabled = value


func toggle() -> void:
    ## 無敵モードをトグル(ON/OFF)します。
    enabled = not enabled
    if not Engine.is_editor_hint():
        print_debug("[GodMode] Toggled: ", enabled, " on ", get_parent())


func _ensure_debug_draw_node() -> void:
    ## デバッグ表示用の Node2D を用意します。
    ## すでに存在する場合は何もしません。
    if _debug_draw_node and is_instance_valid(_debug_draw_node):
        return

    # 親が 2D 系ノードでない場合でも最低限動くように、自前で Node2D をぶら下げます。
    _debug_draw_node = Node2D.new()
    _debug_draw_node.name = "GodModeDebugDraw"
    add_child(_debug_draw_node)
    _debug_draw_node.owner = get_tree().edited_scene_root if Engine.is_editor_hint() else null

    # カスタム描画用に _draw() を持つスクリプトを動的にアタッチ
    var draw_script := GDScript.new()
    draw_script.source_code = _get_debug_draw_script_source()
    draw_script.reload()
    _debug_draw_node.set_script(draw_script)


func _apply_debug_visual() -> void:
    ## デバッグアイコンの表示/非表示と見た目を更新します。
    if not _debug_draw_node or not is_instance_valid(_debug_draw_node):
        return

    _debug_draw_node.visible = enabled and show_debug_icon
    _debug_draw_node.set(&"color", debug_icon_color)
    _debug_draw_node.set(&"radius", debug_icon_radius)
    _debug_draw_node.queue_redraw()


func _get_debug_draw_script_source() -> String:
    ## デバッグ用 Node2D のスクリプトを文字列で返します。
    ## GodMode 本体とは分離しておきたいので、ここで動的に生成しています。
    return '''
extends Node2D

var color: Color = Color(1, 1, 0, 0.6)
var radius: float = 16.0

func _draw() -> void:
    # 半透明の円を描画して「無敵中」を視覚化します。
    draw_circle(Vector2.ZERO, radius, color)

func _process(_delta: float) -> void:
    # 常に再描画しておく(色やサイズが動的に変わったときにも対応)
    queue_redraw()
'''


# ---------------------------------------------------------
# 便利なユーティリティ: 親ノードに対する補助関数
# ---------------------------------------------------------

func get_owner_root() -> Node:
    ## このコンポーネントがぶら下がっている「ルートオブジェクト」を返します。
    ## 通常は get_parent() と同じですが、将来的にラッパーを挟みたいときのために関数化。
    return get_parent()


func debug_print_state() -> void:
    ## 現在の無敵状態をログに出力します(デバッグコンソール用)。
    print_debug("[GodMode] enabled = ", enabled, " on ", get_parent())

使い方の手順

ここからは、実際にプレイヤーや敵に組み込む手順を見ていきましょう。
コンポーネント指向らしく、「どのシーンにも同じ手順でポン付け」できるようにしてあります。

① コンポーネントをプロジェクトに追加する

  1. res://components/ など、コンポーネント用のフォルダを作成します。
  2. そこに GodMode.gd を作成し、上記のコードをコピペします。
  3. Godot エディタを再読み込みすると、GodMode がスクリプトクラスとして認識されます。

② プレイヤーに GodMode をアタッチする

例として、2D アクションゲームのプレイヤーを考えます。

Player (CharacterBody2D)
 ├── Sprite2D
 ├── CollisionShape2D
 └── GodMode (Node)  ← このノードに GodMode.gd をアタッチ
  1. プレイヤーシーンを開きます。
  2. Player の子として Node(または Node2D)を追加し、名前を GodMode にします。
  3. そのノードに GodMode.gd をアタッチします。
  4. インスペクタで enabledshow_debug_icon を好みに設定します。

③ ダメージ処理で GodMode を参照する

重要なのは、「ダメージ処理側は GodMode コンポーネントを見に行くだけ」にすることです。
プレイヤー本体のスクリプトを例にします。


# Player.gd (例)
extends CharacterBody2D

@export var max_hp: int = 10
var hp: int

var god_mode: GodMode

func _ready() -> void:
    hp = max_hp
    # 子ノードから GodMode コンポーネントを探す
    god_mode = get_node_or_null("GodMode")
    # 無くても動くようにしておくと、別シーンへの再利用が楽になります
    if not god_mode:
        push_warning("GodMode component is not attached to Player. No invincibility available.")


func take_damage(amount: int) -> void:
    # 1. GodMode が存在して、かつ無敵ならダメージを無視
    if god_mode and god_mode.is_invincible():
        print_debug("[Player] Damage ignored due to GodMode.")
        return

    # 2. 通常のダメージ処理
    hp -= amount
    print_debug("[Player] Took damage: ", amount, " / HP: ", hp)

    if hp <= 0:
        die()


func die() -> void:
    print_debug("[Player] Dead.")
    queue_free()

ポイントは、プレイヤー自身は「無敵のロジック」をまったく持たないことです。
「無敵かどうか」は GodMode コンポーネントに聞くだけ。
これで、プレイヤーのスクリプトはシンプルなまま、デバッグ用の無敵機能を後付けできます。

④ 敵や動く床にもそのまま使い回す

同じ要領で、敵キャラや動く床にも GodMode をアタッチできます。

例:敵キャラ

Enemy (CharacterBody2D)
 ├── Sprite2D
 ├── CollisionShape2D
 └── GodMode (Node)

# Enemy.gd (例)
extends CharacterBody2D

var hp: int = 3
var god_mode: GodMode

func _ready() -> void:
    god_mode = get_node_or_null("GodMode")

func take_damage(amount: int) -> void:
    if god_mode and god_mode.is_invincible():
        print_debug("[Enemy] Damage ignored due to GodMode.")
        return

    hp -= amount
    if hp <= 0:
        queue_free()

例:動く床(トゲ床だけどデバッグ中は当たらないようにしたい)

SpikyPlatform (StaticBody2D)
 ├── Sprite2D
 ├── CollisionShape2D
 └── GodMode (Node)

# SpikyPlatform.gd (例)
extends StaticBody2D

var god_mode: GodMode

func _ready() -> void:
    god_mode = get_node_or_null("GodMode")

func on_player_touch(player: Node) -> void:
    # プレイヤー側にダメージを与える前に、自分が無敵なら攻撃をスキップ
    if god_mode and god_mode.is_invincible():
        print_debug("[SpikyPlatform] Attack ignored due to GodMode.")
        return

    if player.has_method("take_damage"):
        player.take_damage(1)

このように、「攻撃する側」「ダメージを受ける側」どちらにでも同じコンポーネントを付けられるのが、合成の強みですね。


メリットと応用

この GodMode コンポーネントを導入すると、具体的にどんなメリットがあるかを整理してみます。

  • シーン構造がスッキリする
    無敵モードのために Player.gd や Enemy.gd を肥大化させる必要がなく、
    「無敵」という機能は GodMode.gd に完結します。スクリプトの責務がはっきりします。
  • 再利用性が高い
    プレイヤーだけでなく、敵、ギミック、ボス戦のパーツなど、
    どのシーンにも同じコンポーネントをそのままポン付けできます。
  • デバッグフローが楽になる
    例えば「特定のステージだけ敵を無敵にして挙動だけ確認したい」といったときに、
    対象シーンに GodMode を追加して enabled = true にするだけで OK です。
  • 本番コードにデバッグ条件が混ざらない
    本番用のダメージ処理は「GodMode があれば見る」だけで、
    プロジェクト全体の「無敵モード」が 1 箇所にまとまります。

さらに、コンポーネント同士を組み合わせる発想とも相性が良いです。
たとえば、

  • Health コンポーネント(HP 管理)
  • DamageReceiver コンポーネント(ダメージ受付)
  • GodMode コンポーネント(無敵フラグ)

をそれぞれ独立させておけば、
「敵Aは HP あり + ダメージ受付あり + 無敵なし」
「ボスコアは HP あり + ダメージ受付あり + 無敵あり」
というように、ノードを組み合わせるだけで挙動を構成できるようになります。

改造案:入力で GodMode をトグルする

最後に、ちょっとした改造案として、
「キーボードのキーで GodMode を ON/OFF する」サンプルを載せておきます。

プレイヤー側に、こんな関数を追加してみましょう。


# Player.gd の一部に追加

func _input(event: InputEvent) -> void:
    # InputMap で "toggle_god_mode" アクションを定義しておく(例: F1 キー)
    if event.is_action_pressed("toggle_god_mode"):
        if god_mode:
            god_mode.toggle()
            god_mode.debug_print_state()
        else:
            push_warning("GodMode component is not attached, cannot toggle.")

InputMap で toggle_god_mode を F1 などに割り当てておけば、
テストプレイ中にワンキーで無敵モードを切り替えられます。
このあたりも、「無敵のロジックが GodMode にまとまっている」おかげで、
プレイヤー側は god_mode.toggle() を呼ぶだけで済むのが嬉しいところですね。

こんな感じで、「継承より合成」でデバッグ用機能をコンポーネント化していくと、
プロジェクトが大きくなっても管理しやすいコードベースになっていきます。
ぜひ、自分のゲームにも GodMode コンポーネントを導入して、快適なデバッグライフを送りましょう。