📦

Godotで独自のリソース / .tresを実装してみる

2022/01/24に公開

概説

Godot Docsのノードの使用をさけるべき場合といろいろな方法にもあるように、Godotにはさまざまな機能をもつノードとは別に、非常に軽量なクラスがあります[1]

その中でもResourceクラスには他のクラスにはない特徴として、リソースファイルの読み書き機能が備わっています。この記事では、Resourceの機能を利用して作る カスタムリソース(独自のリソース).tresリソースファイル とその便利さ、実装方法について紹介したいと思います。


この記事の内容をざっくりと

非常に雑な言い方をすると、Resource派生クラスを用意さえすれば、任意の変数をまとめて格納できるカスタムリソースや、以下のような特徴を持つ独自の.tresリソースファイルを利用することができます。

  • JSONよりも可読性が高い
  • さまざまな型の変数に対応している(Enumも使えます)
  • 読み込み/書き出しが簡単
  • Godotでもテキストエディタでも編集できる
  • データの格納だけでなく、処理もできる

まずはResourceを知る

そもそも、Resourceとはどういう役割を持つクラスなのでしょうか。Godot Docsのリソースの説明が比較的わかりやすいので、引用してみます。

Node は、機能を提供します。例えばスプライトや3Dモデルの表示、物理演算、ユーザーインターフェースの配置などです。一方、Resourceはデータの格納に使います。それ自身はなにもしませんが、代わりにノードが、データの入ったリソースを使用します。

Godotが保存したり、ディスクから読み込んだりするものは、すべてリソースです。これは、シーン(.tscnや.scnファイル)や、画像、スクリプトなどが該当します。リソースの例は、Texture、Script、Mesh、Animation、AudioStream、Font、Translationなどです。

簡単に言えば複数のデータをまとめて格納したり、格納されたデータを外部ファイルとして読み書きできるクラスがResourceです。Resourceクラスがまとめたデータの塊が「リソース」、リソースを外部に保存したのが.tres.res形式のリソースファイルです。

また、他のクラス同様にResourceクラスは継承することができます。派生クラスは、用途に応じた個別のリソースを管理することができます。これらのリソースは、それぞれ違った種類や数の変数を格納することができます[2]

実際に同じResourceクラスを継承した、AtlasTextureDynamicFontの2つのリソースを比較してみましょう。AtlasTextureは内部にint型で管理されるフラグや、Rect2型で示された画像の切り出し範囲を格納しています。これは、Color型やbool型、int型を格納するDynamicFontとは明らかに違いますね。

ただ、どちらもさまざまな型の変数を一箇所にまとめることで、ノード間のデータのやり取りの利便性を高めている点が共通しています。

リソースファイル(.tres)の中身を見てみる

今度はリソースファイルの中身を見てみましょう。例として以下の画像のようなDynamicFont型のリソースファイルSilver.tresを保存し、テキストエディタで開くことにします[3]
Godot Engine上のFontリソースファイルの設定画面
このリソースファイルを、テキストエディタで読み込んでみます

Silver.tresをテキストエディタで開いてみると、以下のように表示されます。

Silver.tres
[gd_resource type="DynamicFont" load_steps=2 format=2]

[ext_resource path="res://**/Silver.ttf" type="DynamicFontData" id=1]

[resource]
size = 17
outline_size = 1
outline_color = Color( 0.254902, 0.266667, 0.054902, 1 )
use_filter = true
extra_spacing_top = 2
extra_spacing_bottom = 1
extra_spacing_char = 3
extra_spacing_space = 4
font_data = ExtResource( 1 )

基本的に.tresファイルはさまざまな型の変数と、その値が書かれたテキストデータであることが分かります。iniやTOMLに似ていて読み易いですね。他にも以下のことが分かります。

  • 1行めにDynamicFont型のリソースであることが宣言されている
  • 3行めにフォントデータであるSilver.ttfのパスが記述されている
  • 変数は基本的に[resource]より下の行に記述されている
  • Color型の変数にも対応している

スクリプトでリソースファイル内の変数を読み込んでみる

.tresファイル内の変数は、スクリプトでも簡単に読み込むことができます。例えば、先ほどのSilver.tresファイルの8行めにあるoutline_colorを参照するには、以下のように記述します。

var resource = load("res://**/Silver.tres")
print(resource.outline_color)
# Color( 0.254902, 0.266667, 0.054902, 1 )と出力されます

きちんとColor型の変数として扱われていますね。コードも非常に簡潔です。

自分で作った独自のリソースやリソースファイルを使いたい

ここまでResourceやその派生クラス、そしてリソースファイルについて見てきました。

さまざまなデータを格納できるリソース自体は便利そうですし、読み込みが簡単なリソースファイル.tresも魅力的です。もしかしたら関わっているプロジェクトで「.tresをJSONの代わりに使ってみたい」と思うかもしれません。

ですが、GodotはJSONファイルのようには.tresファイルを扱うことはできません。Godotで.tresファイルを読み書きするには、まずはリソースの仕様を定めた設計図(Resource派生クラス)を作る必要があります。

自分でDynamicFontのようなクラスを作ることで、はじめて独自のリソースやリソースファイルを扱えるようになるのです。


カスタムリソースを作る

幸いなことに、Resource派生クラスを作って独自のリソースを実装するのは、普段GDscriptに触れているユーザーにとってさほど難しいことではありません。慣れてしまえば、ものの数分で実装することさえ可能です。

ここからは実際にResource派生クラスとカスタムリソースを作ってみましょう。

Resource派生クラスを作る

以下にResource派生クラスのサンプルコードを例示します。ゲームに登場するキャラクターのデータを格納するための必要最小限のクラスです[4]

ご覧の通り、コードの中でいくつかの変数が宣言されており、それぞれにキャラクターの名前やダイアログデータが代入できます。CharacterData型のリソースの誕生です。

character_data.gd
# Recourceクラスを継承したCharacterDataクラスを宣言
extends Resource
class_name CharacterData

# キャラクターの属性やアビリティを扱うenumを宣言
enum CharacterClass {NULL, PLAYER, ENEMY, MOB,}
enum Ability {NULL, MAGIC, MELEE, RANGED, SNEAK}

# リソースで扱われる変数を全て宣言
export (String) var name:String
export (CharacterClass) var character_class:int
export (Vector2) var initial_position:Vector2
export (Array, Ability) var abilities:Array
export (Array, String) var dialog_data:Array

カスタムリソースに値を代入してみる

このCharacterData型のリソースに、データを格納してみましょう。Godotのメニューから新規リソースを押下し、先ほど作ったCharacterDataを選択します。すると、馴染み深いUIでカスタムリソースの編集ができます。
Godot上で、カスタムリソースを作成・編集している様子
CharacterData型のカスタムリソースを作成し、編集している様子

スクリーンショットをご覧いただくと分かるかと思いますが、CharacterDataクラスで宣言された変数がきちんと表示されていますし、enumを使った配列を使うことさえできます。もちろん、値を代入したリソースを.tresファイルとして保存することもできます。試しに武器屋のおじさん.tresというファイル名で保存してみましょう。

保存したリソースファイルの中身を見てみる

保存した武器屋のおじさん.tresファイルを、テキストエディタで開いてみます。

enumが数値で記述されていること、Vector2は型が明示されていることなどがわかります。もちろん、先ほどGodotで編集・保存した内容と相違ありませんね。

武器屋のおじさん.tres
[gd_resource type="Resource" load_steps=2 format=2]

[ext_resource path="res://**/character_data.gd" type="Script" id=1]

[resource]
script = ExtResource( 1 )
name = "武器屋のおじさん"
character_class = 3
abilities = [ 2 ]
dialog_data = [ "ここは武器屋だ", "何を買う?" ]
initial_position = Vector2( 320, 160 )

これで独自のリソース、そしてリソースファイルの実装と動作確認完了です。


カスタムリソースはデータの処理もできる

Godotのリソースが優れている点に、データの格納だけではなく、処理ができることが挙げられます。

先ほど例示したサンプルコードを少し変更し、name変数からファイルパスを生成し出力するメソッド、get_texture_path()を追加してみました。

character_data(改).gd
extends Resource
class_name CharacterData

enum CharacterClass {NULL, PLAYER, ENEMY, MOB,}
enum Ability {NULL, MAGIC, MELEE, RANGED, SNEAK}

export (String) var name:String
export (CharacterClass) var character_class:int
export (Array, Ability) var abilities:Array
export (Array, String) var dialog_data:Array
export (Vector2) var initial_position:Vector2

# - - - - - - - - - ここから下が、新しく追加したコード - - - - - - - - -
# name変数に文字列を結合し、ファイルパスを返すメソッド
func get_texture_path() -> String:
    return "res://file_path_to_texture/%s_texture.tres" % name

これでget_texture_path()メソッドを使って、リソース内に存在しない値を呼び出すことができるようになりました。早速、先ほど保存した武器屋のおじさん.tresで実験をしてみましょう。以下がサンプルコードです。

var resource = load("res://**/武器屋のおじさん.tres")
print(resource.get_texture_path())
# res://file_path_to_texture/武器屋のおじさん_texture.tres と出力されます

「武器屋のおじさん」のテクスチャのファイルパスが出力されたはずです。リソースから直接メソッドを呼び出せるので、コードがシンプルになるのも非常に良いですね。


カスタムリソースのススメ

このように、データの読み書きや処理などを行うことができるResource派生クラスとカスタムリソース(独自のリソース)は非常に便利です。

ゲームに登場するキャラクターなど、同じ構造のデータを複数管理をする状況では大活躍しそうですね。逆に構造が定まってないデータや、専用のクラスを用意するまでもない一点もののデータの管理には、JSONなどの使用を検討してみても良いのかもしれません[5]

他にもいろいろできます

この記事ではResouceを表面をなぞる程度にしかその機能やメリットを紹介できていません。しかしResourceは他のクラス同様にsettergetter、そしてシグナルも扱えますし、他にも多くのメリットがあります[6]

もしまだカスタムリソースを使ったことがないようであれば、これを機にぜひ触れてみてください。そして、もし素敵な使い方や活用方法を発見したら、フィードバックをいただければ幸いです。

脚注
  1. 全てのオブジェクトの継承元である最軽量なObject、自身への参照を追跡可能なReference、そしてリソースファイルの読み書きができるResourceの3つが触れられています。 ↩︎

  2. 他の言語でいうところの構造体と似ていますが、ユーザーがResource派生クラスをインスタンス化してデータを格納する必要はありません。 ↩︎

  3. ここではPoppy Works謹製のSilverフォントを使っています。おすすめです! ↩︎

  4. このコードでは、機能の説明のためにenumなどを使っていますが、もちろんそれらを削って、もっとシンプルなカスタムリソースを実装することもできます。 ↩︎

  5. スプレッドシートのデータを手間をかけずに読み込みたい場合も、JSONやCSVをが有力な選択肢になりますね。 ↩︎

  6. Godot Docsのリソースページにもいくつか提示されているので、ぜひご覧ください。 ↩︎

Discussion