プラグインのインポート

注釈

このチュートリアルでは、汎用プラグインの作成方法を理解していることを前提としています。よく理解していない場合は、プラグインの作成ページを参照してください。また、Godotのインポートシステムに精通していることを前提としています。

はじめに

インポートプラグインは、カスタムリソースをGodotによってインポートし、ファーストクラスのリソースとして扱うことができる特別なタイプのエディタツールです。エディタ自体には、PNG画像、ColladaおよびglTFモデル、Ogg Vorbisサウンドなどの一般的なリソースを処理するための多くのインポートプラグインがバンドルされています。

このチュートリアルでは、単純なインポートプラグインを作成して、カスタムテキストファイルをマテリアルリソースとしてロードする方法を示します。このテキストファイルには、色の3つのチャネルを表すコンマで区切られた3つの数値が含まれ、結果の色はインポートされたマテリアルのアルベド(メインカラー)として使用されます。この例では、純粋な青の色(赤がゼロ、緑がゼロ、青がフル)が含まれます:

0,0,255

構成

まず、インポートプラグインの初期化と破棄を処理する汎用プラグインが必要です。最初に plugin.cfg ファイルを追加してみましょう:

[plugin]

name="Silly Material Importer"
description="Imports a 3D Material from an external text file."
author="Yours Truly"
version="1.0"
script="material_import.gd"

次に、必要に応じてインポート プラグインを追加および削除する material_import.gd ファイルが必要です:

# material_import.gd
tool
extends EditorPlugin

var import_plugin

func _enter_tree():
    import_plugin = preload("import_plugin.gd").new()
    add_import_plugin(import_plugin)

func _exit_tree():
    remove_import_plugin(import_plugin)
    import_plugin = null

このプラグインがアクティブになると、インポートプラグインの新しいインスタンス(間もなく作成します)が作成され、add_import_plugin() メソッドを使用してエディタに追加されます。クラスメンバー import_plugin に参照を保存し、後で削除するときに参照できるようにします。remove_import_plugin() メソッドは、メモリをクリーンアップするためにプラグインが非アクティブ化されたときに呼び出され、インポートプラグインが利用できなくなったことをエディタに知らせます。

インポートプラグインは参照型なので、 free() 関数を使用してメモリから明示的に解放する必要はありません。スコープ外になると、エンジンによって自動的に解放されます。

エディタインポートプラグイン クラス

ショーの主役は EditorImportPluginクラス です。ファイルの処理方法を知る必要がある場合にGodotによって呼び出されるメソッドの実装を担当します。

プラグインのコーディングを始めましょう:

# import_plugin.gd
tool
extends EditorImportPlugin

func get_importer_name():
    return "demos.sillymaterial"

最初のメソッドは get_importer_name() です。これは、特定のファイルでどのインポートが使用されたかを知るためにGodotによって使用されるプラグインの一意の名前です。ファイルを再インポートする必要がある場合、エディタは呼び出すプラグインを認識します。

func get_visible_name():
    return "Silly Material"

get_visible_name() メソッドは、インポートする型の名前を返す役割を果たし、インポートドックでユーザーに表示されます。

この名前は、「名前を付けてインポート」の続きとして選択する必要があります。 例えば、 「愚かな素材としてインポート」。 好きな名前を付けることができますが、プラグインにはわかりやすい名前を付けることをお勧めします。

func get_recognized_extensions():
    return ["mtxt"]

Godotのインポートシステムは、拡張子によってファイルタイプを検出します。get_recognized_extensions() メソッドでは、このプラグインが理解できる各拡張機能を表す文字列の配列を返します。拡張子が複数のプラグインによって認識される場合、ユーザーはファイルをインポートするときに使用するプラグインを選択できます。

ちなみに

.json.txt などの一般的な拡張機能は、多くのプラグインで使用される場合があります。また、プロジェクト内には、ゲームのデータにすぎず、インポートしてはならないファイルが存在する可能性もあります。データを検証するには、インポート時に注意が必要です。ファイルが整形式であることを期待しないでください。

func get_save_extension():
    return "material"

インポートされたファイルは、プロジェクトのルートにある .import フォルダに保存されます。それらの拡張子は、インポートするリソースの種類と一致する必要がありますが、Godotは何を使用するのかを知ることができないため(同じリソースに対して複数の有効な拡張機能が存在する可能性があるため)、インポートで使用される内容を宣言する必要があります。

マテリアルを読み込むので、このようなリソースタイプには特別な拡張子を使用します。シーンをインポートする場合は、scn を使うことができます。汎用リソースは res 拡張子を使用できます。ただし、これはエンジンによって強制されません。

func get_resource_type():
    return "SpatialMaterial"

インポートされたリソースには特定のタイプがあるため、エディタはリソースがどのプロパティスロットに属しているかを知ることができます。これにより、 「ファイルシステム」 ドックから 「インスペクタ」 のプロパティーにドラッグ&ドロップできます。

この例では、3Dオブジェクトに適用できるclass_SpatialMaterialです。

注釈

同じ拡張子から異なるタイプをインポートする必要がある場合は、複数のインポートプラグインを作成する必要があります。インポートコードを別のファイルに抽象化して、重複を避けることができます。

オプションとプリセット

プラグインは、ユーザーがリソースのインポート方法を制御できるように、さまざまなオプションを提供できます。選択したオプションのセットが一般的な場合は、異なるプリセットを作成して、ユーザーが簡単に使用できるようにすることもできます。次の図は、エディタでオプションがどのように表示されるかを示しています:

../../../_images/import_plugin_options.png

プリセットが多く、数値で識別される可能性があるため、名前を使用して参照できるように列挙型を使用することをお知めします。

tool
extends EditorImportPlugin

enum Presets { PRESET_DEFAULT }

...

列挙型が定義されたので、インポート プラグインのメソッドを見てみましょう:

func get_preset_count():
    return Presets.size()

get_preset_count() メソッドは、このプラグインが定義するプリセットの量を返します。現在、プリセットは1つしかありませんが、Presets 列挙のサイズを返すことで、このメソッドを将来に備えて作成できます。

func get_preset_name(preset):
    match preset:
        PRESET_DEFAULT:
            return "Default"
        _:
            return "Unknown"

ここに、get_preset_name() メソッドがあります。これは、ユーザーに表示されるプリセットに名前を付けるため、必ず短く明確な名前を使用してください。

ここで match ステートメントを使用して、コードをより構造化できます。 これにより、将来的に新しいプリセットを簡単に追加できます。catch allも何かを返す時に使います。Godotでは、定義したプリセット数を超えるプリセットは要求されませんが、常に安全側にあることをお勧めします。

プリセットが1つしかない場合は、名前を直接返すだけですが、これを行う場合は、プリセットを追加するときに注意する必要があります。

func get_import_options(preset):
    match preset:
        PRESET_DEFAULT:
            return [{
                       "name": "use_red_anyway",
                       "default_value": false
                    }]
        _:
            return []

これは、使用可能なオプションを定義する方法です。get_import_options() はDictionaryの配列を返し、各Dictionaryにはユーザーに表示されるオプションをカスタマイズするためにチェックされるいくつかのキーが含まれます。次の表は、可能なキーを示しています:

キー タイプ(型) 説明
name 文字列 オプションの名前。表示すると、アンダースコアはスペースになり、最初の文字は大文字になります。
default_value 任意 このプリセットのオプションの既定値。
property_hint 列挙値 ヒントとして使用する PropertyHint 値の1つ。
hint_string 文字列 プロパティのヒントテキスト。GDScriptの export ステートメントに追加するのと同じです。
usage 列挙値 使用法を定義する PropertyUsageFlags 値の1つ。

name キーと default_value キーは必須で、残りは省略可能です。

`` get_import_options`` メソッドはプリセット番号を受け取るので、異なるプリセット(特にデフォルト値)ごとにオプションを設定できることに注意してください。この例では match ステートメントを使用しますが、多くのオプションがあり、プリセットが値のみを変更する場合は、最初にオプションの配列を作成してから、プリセットに基づいて変更することができます。

警告

get_import_options メソッドは、プリセットを定義しない場合でも呼び出されます( get_preset_count を0にして返す)。空であっても配列を返さなければならず、さもないとエラーが発生します。

func get_option_visibility(option, options):
    return true

get_option_visibility() メソッドの場合、すべてのオプション(つまり、定義した単一のオプション)が常に表示されるため、単に true を返します。

特定のオプションを表示する必要がある場合は、別のオプションが特定の値で設定されている場合にのみ、このメソッドにロジックを追加できます。

import メソッド

ファイルのリソースへの変換を担当するプロセスの重い部分は、import() メソッドでカバーされます。サンプルコードは少し長いので、いくつかの部分に分けてみましょう:

func import(source_file, save_path, options, r_platform_variants, r_gen_files):
    var file = File.new()
    var err = file.open(source_file, File.READ)
    if err != OK:
        return err

    var line = file.get_line()

    file.close()

インポートメソッドの最初の部分は、ソースファイルを開いて読み取ります。File クラスを使用してこれを行い、エディタが提供する source_file パラメーターを渡します。

ファイルを開くときにエラーが発生した場合は、それを返して、インポートが失敗したことをエディタに知らせます。

var channels = line.split(",")
if channels.size() != 3:
    return ERR_PARSE_ERROR

var color
if options.use_red_anyway:
    color = Color8(255, 0, 0)
else:
    color = Color8(int(channels[0]), int(channels[1]), int(channels[2]))

このコードは、前に読み込んだファイルの行を取得し、コンマで区切られた断片に分割します。 3つよりも多いまたは少ない場合、ファイルは無効であると見なされ、エラーが報告されます。

次に、新しい Color 変数を作成し、入力ファイルに従ってその値を設定します。use_red_anyway オプションが有効な場合、代わりに色を純粋な赤に設定します。

var material = SpatialMaterial.new()
material.albedo_color = color

この部分は、インポートされたリソースである新しい SpatialMaterial を作成します。その新しいインスタンスを作成し、そのアルベド色を以前に取得した値として設定します。

return ResourceSaver.save("%s.%s" % [save_path, get_save_extension()], material)

ここで作成したリソースをディスクに保存するため、これは最後の部分であり、非常に重要な部分です。保存されたファイルのパスが生成され、save_path パラメーターを介してエディタによって通知されます。これは拡張子なしで来るため、string formating を使用して追加することに注意してください。このために、以前に定義した get_save_extension メソッドを呼び出すので、それらが同期しなくなることはありません。

また、ResourceSaver.save() メソッドから結果を返すため、このステップでエラーが発生した場合、エディタはそれを認識します。

プラットフォームバリアントと生成されたファイル

お気付きかもしれませんが、このプラグインは import メソッドの2つの引数を無視しています。これらは戻り引数(したがって、名前の先頭に r があります)で、これはエディタがimportメソッドを呼び出した後にこれらの引数から読み込むことを意味します。どちらも情報を格納できる配列です。

ターゲットプラットフォームに応じて異なる方法でリソースをインポートする必要がある場合、r_platform_variants 引数が使用されます。platform バリアントと呼ばれますが、feature tags の存在に基づいているため、同じプラットフォームでも設定に応じて複数のバリアントを使用できます。

プラットフォームバリアントをインポートするには、拡張機能の前に機能タグを付けて保存してから、タグを r_platform_variants 配列にプッシュして、エディタがそれを認識できるようにする必要があります。

たとえば、モバイルプラットフォーム用に別のマテリアルを保存したとします。次のような処理を行う必要があります:

r_platform_variants.push_back("mobile")
return ResourceSaver.save("%s.%s.%s" % [save_path, "mobile", get_save_extension()], mobile_material)

r_gen_files 引数は、インポートプロセス中に生成され、保持する必要がある追加のファイル用です。 エディタはこれを見て依存関係を理解し、余分なファイルが誤って削除されないようにします。

これは配列でもあるので、保存するファイルの完全なパスで埋められます。たとえば、次のパス用に別のマテリアルを作成し、別のファイルに保存してみましょう:

var next_pass = SpatialMaterial.new()
next_pass.albedo_color = color.inverted()
var next_pass_path = "%s.next_pass.%s" % [save_path, get_save_extension()]

err = ResourceSaver.save(next_pass_path, next_pass)
if err != OK:
    return err
r_gen_files.push_back(next_pass_path)

プラグインを試す

これは理論的なものですが、インポートプラグインが完了したので、テストしてみましょう。(紹介セクションで説明した内容を含む)サンプルファイルを作成し、test.mtxt として保存してください。次に、プロジェクト設定でプラグインを有効にします。

すべてがうまくいけば、インポートプラグインがエディタに追加され、ファイルシステムがスキャンされ、カスタムリソースがファイルシステムドックに表示されます。選択して[インポート]ドックにフォーカスを合わせると、選択する唯一のオプションが表示されます。

シーンにMeshInstanceノードを作成し、そのMeshプロパティに新しいSphereMeshを設定します。インスペクタでMaterialセクションを展開し、ファイルシステムドックからMaterialプロパティにファイルをドラッグします。オブジェクトは、インポートされたマテリアルの青色でビューポートで更新されます。

../../../_images/import_plugin_trying.png

[インポートドック]に移動し、"Use Red Anyway"オプションを有効にし、[再インポート]をクリックします。これにより、読み込まれたマテリアルが更新され、代わりに赤い色を示すビューが自動的に更新されます。

以上です!最初のインポートプラグインが完了しました。自分の好きなフォーマットのプラグインを作ってみましょう。これは、カスタム・フォーマットでデータを作成し、それをあたかもネイティブ・リソースであるかのようにGodotで使用する場合に非常に便利です。これは、インポート・システムがいかに強力で拡張可能であるかを示しています。