🔎

Godot : カスタムクラス名を条件文で使う

2022/05/23に公開

概説と結論

この記事では、Godotでカスタムクラスを使用する場合に、条件文にis_class()を使った際の挙動を改善します。

概説

実はGodot 3.4.xでは、クラス名の比較処理に使うisis_class()は、その仕様上、継承元のクラス名にもtrueを返します。また、is_class()はカスタムクラス名にはfalseを返してきます。

カスタムクラスを使った厳密な型の比較をしたい場合には、少し使いづらい仕様です。

結論

is_class()関数はオーバーライドができますので、対象のクラスではオーバーライドしてより正確な値を返すようにします。オーバーライド後は厳密な型の比較にはisではなく、is_class()を使うようにすれば、OK[1]

以下がコード例です。

👍
# is_class()をオーバーライド
func is_class(value) -> bool:
    if value == "カスタムクラス名":
        return true
    else:
	return false
	# elseをfalseにすることで、継承元のクラス名にもfalseを返します


# オーバーライドしたis_class()の使い方
func _ready():
    if self.is_class("カスタムクラス名"):
        # trueだった際の処理をかく

ここからは、Godot 3.4.x時点でのis演算子、is_class()関数の仕様について、コードを交えつつ、説明します。

is演算子とis_class()の挙動を知る

Godotには、インスタンス型チェッカーとしてis演算子、そして型の比較用関数is_class()が用意されています。いずれも、条件文でビルトイン・クラスの比較をしたい場合にはとても便利です。

ただしGodot 3.4.x時点で、これらを使ったカスタムクラス名の比較には少し難があります。

is演算子の挙動

is演算子は、カスタムクラス名、継承元のクラス名のどちらにもtrueを返してくる仕様になっています[2]

is_class()関数の挙動

is_class()関数は、継承元のクラス名にはtrueを返しますが、カスタムクラス名にはfalseを返してきます[3]

実際に試してみる

例として、Node2Dを継承したNodeExtというクラスを作って、isis_class()のテストをしてみます。以下のようなコード[4]で挙動の確認ができます[5]

🤔
extends Node2D
class_name NodeExt # Node2Dを継承したNodeExtクラスの宣言

func _ready():
    # is 演算子の挙動確認 --------------------------
    if self is Node:
        print("このクラスはNodeです") # -> 出力されます

    if self is Node2D:
        print("このクラスはNode2Dです") # -> 出力されます

    if self is NodeExt:
        print("このクラスはNodeExtです") # -> 出力されます

    # is_class() 関数の挙動確認 --------------------
    if self.is_class("Node"):
        print("このクラスはNodeです") # -> 出力されます

    if self.is_class("Node2D"):
        print("このクラスはNode2Dです") # -> 出力されます

    if self.is_class("NodeExt"):
        print("このクラスはNodeExtです") # -> 出力されません

継承元を条件から除外しなくても良い場合は、is演算子が使えることが分かります。一方で、継承元に対してもtrueを返されると不便な場合もあります。

そこで、自分は冒頭に紹介したようにis_class()関数をオーバーライドすることにしました。

is_class()をオーバーライドして、再挑戦

冒頭のコードを挿入したカスタムクラスで、先程の比較文を実行してみましょう。

💡
extends Node2D
class_name NodeExt

func _ready():
    if self.is_class("Node"):
        print("このクラスはNodeです") # -> 出力されません

    if self.is_class("Node2D"):
        print("このクラスはNode2Dです") # -> 出力されません

    if self.is_class("NodeExt"):
        print("このクラスはNodeExtです") # -> 出力されます

見事に、is_class()を使った厳密な型比較ができるようになりました。

get_class()もオーバーライド

なお、Godotにはget_class()という関数も用意されています。こちらは継承関係で直近のビルトイン・クラスの名前を返します。コード例を見てみましょう。

extends Node2D
class_name NodeExt

func _ready():
    print(self.get_class()) # -> 「Node2D」と出力されます

これが不便だと感じる場合には、以下のようにget_class()もオーバーライドしてしまうことができます。

func get_class() -> String:
    return "カスタムクラス名"

以上です。

脚注
  1. 「継承関係の比較もしたいんだ!」という場合には、is_class()をオーバーライドせずに、例えばis_custom_class()のような名前の関数を作って、同じコードを流し込んであげてください。継承元のクラス、カスタムクラスを別の関数で比較できます。
    また少し冗長ですが、型比較専用の変数を作っても同じことができます。ただ、変数を作った場合には、インスタンスが増えた際のメモリ効率が悪そうだと想像しています(素人考えですが)。
    個人的には継承元を含めた比較をする機会が殆どないのと、そういった場合にはis演算子があるため、is_class()をオーバーライドする手段が一番スマートなのではないかと思います。他に良い方法があれば、コメントしていただければ幸いです。 ↩︎

  2. 出典: https://docs.godotengine.org/ja/stable/tutorials/scripting/gdscript/gdscript_basics.html#keywords ↩︎

  3. 出典: https://docs.godotengine.org/ja/stable/classes/class_object.html#class-object-method-is-class ↩︎

  4. 実際には、カスタムクラス内で自身のクラス名を用いたis比較は許可されていないため、このコードを実行しようとするとエラーが返ってきます。あくまで説明のために、自身のカスタムクラス名を条件文に使用している点、ご了承ください。
    なお、同じようなテストをされる場合には、親ノードの子としてカスタムクラスを追加の上、親ノードから比較するようにコードを修正すると、実行ができます。 ↩︎

  5. コードをご覧いただくとわかる通り、直近の継承元のNode2Dのみならず、さらに上位の祖先であるNodeに対してもtrueを返してきます。 ↩︎

Discussion