【Godot Engine】メモ・解決したこと【GDScript】
テキストファイルの内容をPoolStringArrayに格納する
Godot Engine 3.4.4
Godot Engine 4.0.3
Pythonでいうところのf.readlines()に相当するものが欲しかったけどなさそう。
whileで1行ずつeofまで読み込んでいく必要があるみたい。
Hello
Godot
Engine
Godot 3.x
func readlines() -> PoolStringArray:
var txt = File.new()
var txt_pool = PoolStringArray()
txt.open("res://sample1.txt", txt.READ)
while txt.get_position() < txt.get_len():
txt_pool.append(txt.get_line())
txt.close()
return txt_pool
func _ready():
print(readlines())
Godot 4.x
func readlines() -> PackedStringArray:
var txt = FileAccess.open("res://sample1.txt", FileAccess.READ)
var txt_packed = PackedStringArray()
while txt.get_position() < txt.get_length():
txt_packed.append(txt.get_line())
txt.close()
return txt_packed
func _ready():
print(readlines())
↓
[Hello, Godot, Engine]
参考:
:= という表記について
Godot Engine 3.4.4
Pythonのセイウチ演算子(Walrus Operator)とは異なる。
var num := 10
は
var num: int = 10
と同じ。
静的に型を付けることで異なる型の値を代入しようとすると
エラーになるので代入ミスを防ぐのに役立つ。
int型に小数を代入した場合はエラーにはならないけれど、端数は切り捨てられるので注意。
func _ready():
var num: int = 10
num += 0.1
print(num) # 10
var num2 = 10
num2 += 0.1
print(num2) # 10.1
参考:
Godot Engine 3.5
Godot Engine 4.0.3
コードでシグナルを扱う
Godot 3.x
接続元ノード.connect("シグナル名", 接続先ノード, 呼び出したい関数, [引数1,引数2,...], flags=0)
自身のノードはself
で参照可能。
flagsを指定するとワンショットにさせたり遅延させたりできる。[1]
signal
キーワードは
発信元ノード.emit_signal("シグナル名", 引数1, 引数2, ...)
とセットで使う。emit_signalの方は引数をArrayでまとめて渡すのではなく一つずつ渡す。
extends Node
signal test
func _ready() -> void:
connect("test",self,"greet")
some_func()
func some_func() -> void:
emit_signal("test","Hello Godot 3!")
print("Signal emitted!")
func greet(greeting: String) -> void:
print(greeting)
Godot 4.x
シグナルはクラスになり、関数の指定方法が文字列からCallableに変わるなどいった変更が
加わりました。
Godot 3でのやり方も残されていますが、関数はCallableとして指定し、接続先ノードの指定は無くなりました。
connect時にコールバック関数の引数を指定する場合はCallableのbindメソッドを使います。
extends Node
signal test
func _ready() -> void:
test.connect(greet)
some_func()
func some_func() -> void:
test.emit("Hello Godot 4!")
print("Signal emitted")
func greet(greeting: String) -> void:
print(greeting)
Godot Engine 3.5.1
メモ。
翻訳機能を使う際に.csvファイルに空行があるとうまく動かないので注意。
Godot Engine 3.5.1
HTML5にエクスポートしたゲームの画面サイズがおかしいときに見直すところ
エクスポート→Canvas Resize Policy : Adaptive
プロジェクト設定→ウィンドウ→
モード : 2d(あるいはviewport)
アスペクト : keep
Godot Engine 3.5.1
辞書型はデータの追加順で順序を保持している。
JSONから読み込んだ場合は保証されない。
print
で中身を表示するとアルファベット順でキーが並ぶがiterateする分には問題無し。
Godot Engine 3.5.1
Godot Engine 4.0.3
シグナルは自動読み込みで管理すると便利。
SignalManager.gdは自動読み込みに設定してグローバル変数を有効にしておく。
extends Node
signal example1
Emitter
|--Receiver
extends Node
func _ready():
#SignalManager.emit_signal("example1") # Godot 3.x
SignalManager.example1.emit() # Godot 4.x
extends Node
func _ready() -> void:
#SignalManager.connect("example1",self,"foo") # Godot 3.x
SignalManager.example1.connect(foo) # Godot 4.x
func foo() -> void:
print("Signal emitted!")
Signal emitted!
Godot Engine 3.5.2
ブラウザゲームでセーブ機能を実現するにはuser://
上でセーブファイルの読み書きをすればOK。
ブラウザ上なら自動的にIndexedDB上に保存されるはず。
JavaScriptの呼び出しと待機処理
Godot Engine 3.5.2
Godot Engine 4.0.2
※以前に記事でまとめた内容を一部編集してコードスニペットとしてこちらに移行。
var window = JavaScript.get_interface("window")
if window:
yield(get_tree().create_timer(3.0), "timeout")
window.alert("Hello Godot Engine 3")
var window = JavaScriptBridge.get_interface("window")
if window:
await get_tree().create_timer(4.0).timeout
window.alert("Hello Godot Engine 4")
Godot Engine 4.0.3
外部のリソースを読み込む
load関数が読み込めるのはres://
内のものに限るのでuser://
などから画像や音源を読み込む場合について。
func load_external_texture(path: String) -> Texture2D:
var image := Image.new()
image.load(path)
return ImageTexture.create_from_image(image)
func load_external_sound(path: String) -> AudioStreamMP3:
var file := FileAccess.open(path, FileAccess.READ)
var audio := AudioStreamMP3.new()
audio.data = file.get_buffer(file.get_length())
file.close()
return audio
似た方法で.ogg
も読み込もうとしたものの、どうやら[1]によると4系だとv4.0.3時点では
読み込めないようです。(loadで読み込む分には問題無いです)
参考:
Godot Engine 4.0.3
テキストファイルをエクスポートする
res://
内の.txt
ファイルはリリースビルドではエクスポートされないので注意。
必要であればエクスポート時のオプション「リソース以外のファイル/フォルダーをエクスポートするためのフィルター」で*.txt
を明示的に指定する必要あり。
※拡張子が.csv
のものはローカライズ用のファイルとして扱われるためか、指定してもリリースビルドでは読み込めません。なのでその意図がない場合は.txt
などに変更しましょう。
Godot Engine 4.0.3
タイルにアニメーションを実装する
ちょっとハマったのでメモ。
タイルセットのセットアップからAnimatedTexture
を追加して各フレームにテクスチャを追加するやり方だとうまくいきません。
代わりにアニメーション用のスプライトシートを用意する必要があります。
- スプライトシートをタイルセットに追加
- 「アトラスにタイルを自動的に作成しますか?」→「いいえ」
- ベースタイルのとこからスプライトシートの先頭のタイル(=1フレーム目)のみ右クリック→タイルの作成
- 選択メニューからアニメーションのColumnsにフレーム数を指定
- Framesに要素を追加
2で「はい」を選択すると3で全てのタイルが作成されている状態になりますが、この状態だとFramesに要素を追加することができないので注意してください。
参考:
Godot Engine 4.0.3
外部の翻訳ファイル(csv)を読み込む
外部(res://
外)の翻訳ファイルを読み込む必要がある場合
Translationオブジェクトを生成してからTranslationServerに追加すればOKです。
res://
内に翻訳ファイルを追加したときにエディタ上でやっていたのと同じことをコードで
実装するわけです。
_,en,ja
MOB1,Zombie,ゾンビ
MOB2,Skeleton,スケルトン
MOB3,Spider,クモ
func load_external_translation() -> void:
var file := FileAccess.open("user://external_trans.csv", FileAccess.READ)
var file_content: Array[PackedStringArray]
file_content = []
while file.get_position() < file.get_length():
file_content.append(file.get_csv_line())
file.close()
var locales := file_content[0] as PackedStringArray
for locale_idx in len(locales)-1:
var translation := Translation.new()
translation.locale = locales[locale_idx+1]
var message_lines := file_content.slice(1)
for message_line_idx in len(message_lines):
var key := message_lines[message_line_idx][0] as String
var translated := message_lines[message_line_idx][locale_idx+1].c_unescape() as String
translation.add_message(key, translated)
TranslationServer.add_translation(translation)
※エラー処理などは適宜行ってください
参考:
Godot Engine 4.2.0
ラジオボタンを実装する
ラジオボタン専用のノードはありませんが、簡単に実装できます
-
CheckBox
ノードを追加 -
BaseButton
->Button Group
に新規 Button Group(リソース)を追加 - 他の
CheckBox
にも先程と同じButton Groupをコピー&貼り付けでグループ化完了
複数のグループが必要なら、グループ毎にButton Groupに名前を付けて保存する方が望ましいですね
Godot Engine 4.2.0
Grow Directionを設定する(4.x)
layout_direction
を変更するだけで事足りることが多いと思いますが一応・・・。
3.xでは目立つ所にありましたが、4.xからは変更されています
次のようにプロパティを辿る必要があります
Control
-> Layout Mode = Anchors
-> Anchor Preset = カスタム
-> Grow Direction
特にLabelノードでは重宝しますね
Godot Engine 4.2.0
任意の形状のボタンを作る
TextureButtonにtexture_normal
をセットするだけではボタンの形状はsize
と同じになるので透過部分もクリック判定を持ちます。
透過部分をマスクするには次の通りです。
まずtexture_normal
用のテクスチャとclick_mask
用のテクスチャをpng形式で用意します。同じもので問題ありません。ここではそれぞれをtexture_normal.png
, click_mask.png
とします。
次にそれらをres://
に追加後, click_mask.png
を選択したらインポートタブの「名前を付けてインポート」からBitMapを選び、作成元をAlpha
にして再インポートをクリックします。
これでclick_mask.png
をBitMapリソースとして設定できるようになりました。
TextureButton
-> Textures
-> Click Mask
にclick_mask.png
を設定します。
あとはtexture_normal
にtexture_normal.png
を設定すれば完了です。
透過部分に判定が無いことを確認してみてください。
Godot Engine 4.2.0
res://
内にとあるファイルが存在するかどうかの確認は
FileAccess.file_exists
とResourceLoader.exists
を使い分けよう。
リソースとしてインポートされる画像や音楽などのファイルはResourceLoader.exists
その他のファイルはFileAccess.file_exists
で判定するのが良さそう。
というのも公式ドキュメント曰く「エクスポートバージョンでは.png
などの素材ファイルそのものはゲームに含まれておらず.import
形式のものが利用される」から。[1]
.txt
などのリソースではないものはResourceLoader.exists
でチェックできませんし、そもそもデフォルトのオプションではエクスポート自体されないので注意。[2]
強引ですがリソースとしてインポートされるものであればFileAccess.file_exists("res://img1.png.import")
みたく書いちゃえば動きます。
ちなみにプロジェクトアイコンとして設定されている画像はそのままでもエクスポートされているようです。
Godot Engine 4.2.2
ui_text_
から始まる組み込みのインプットマップを除去すると
文字の入力を受け付けるノード(Line/Text/Code)Edit
の動作に支障をきたすので注意。
例えばLineEdit
のシグナルtext_submitted
に関してはui_text_submit
に登録されているキーに依存するのでそれらを除去するとシグナルが発行されません。
また、focus_mode
をNone
にすると入力自体受け付けなくなります(そりゃそう)。
自然な仕様ではありますが、組み込みのインプットマップを除去するときは気をつけましょう。
Godot Engine 4.2.2
ダブルクリックを検知できるようにする
Control
を継承したノードであれば以下のコードでOKです。
extends Button
signal double_clicked
func _ready() -> void:
gui_input.connect(_on_gui_input)
func _on_gui_input(event: InputEvent) -> void:
if event is InputEventMouseButton and event.double_click:
double_clicked.emit()
print("double clicked!")
Godot Engine 4.2.2
Callable.unbind
の活用例
次のようなシーンがあるとする。
Control:
LineEdit
Button
そしてButton
がクリックされたとき、あるいはEnterキーが押されたときに
入力されたテキストをもとに何か処理をしたい、という場合を想定する。
text_submitted
は入力されたテキストを接続先の関数にバインドするのでそれをunbind
する例。
extends Control
func _ready() -> void:
$Button.pressed.connect(foo)
$LineEdit.text_submitted.connect(foo.unbind(1))
func foo() -> void:
print($LineEdit.text)
※unbind
する代わりに関数を修正して$Button.pressed.connect(foo.bind($LineEdit.text))
と書いても
接続時点の値(空文字列)がバインドされるので意味はない。
参考:
Godot Engine 4.3
ライセンステキストを生成する
以前までは
ライセンスの遵守
にはGodot自体のライセンス表記に加えてGodotが使用する一部のサードパーティライブラリ(FreeType, mbedTLS, ENet)については別途ライセンス表記が必要であると記載されていましたが、現在ではその記載は無くなっています。
代わりにサードパーティのライセンス表記については
COPYRIGHT.txt
をゲームと一緒に同包するのがよいとのこと。
ではブラウザ版のゲームなどではどう対応するのがいいのかというと、Engine
シングルトンから直接ライセンス情報を取得することができるので、これで対応してみることに。
バージョンが変わっても正しく取得できるらしいので便利ですね。
RichTextLabel
に追記するサンプルコードは以下の通り。テキストは適宜整形。
func add_credit_text() -> void:
$RichTextLabel.text += "\n----------------------------------------\n"
var copyright_info := Engine.get_copyright_info()
var license_info := Engine.get_license_info()
for info: Dictionary in copyright_info:
for part: Dictionary in info["parts"]:
$RichTextLabel.text += "%s: %s, (C) %s\n"%[info["name"], part["license"], part["copyright"]]
$RichTextLabel.text += "\n------------------------------------------------------------------------------------\n\n"
for license_name: String in license_info:
$RichTextLabel.text += "%s: \n%s\n"%[license_name, license_info[license_name]]
$RichTextLabel.text += "------------------------------------------------------------------------------------\n"
エディタのヘルプから確認できるライセンステキストと、内容は同じになっているはず...。