年末年始なのでGodotでゲームでも作ってみる 4日目

今日の目標

  • スクリプトを書いてみる

Godotで使えるスクリプトについて

GodotではGDScriptの他にVisualScript、C#、C++でスクリプトを書くことができる。メイン言語はGDScriptということなので、今回もGDScriptを使うことにする。

テスト用のSceneの作成

公式ドキュメントのScriptingの内容に従って、LabelとButtonが配置されたSceneを新しく作成する。

Root Nodeに対して右クリックメニューからAttach Scriptを選択。

ScriptのPath(保存先のファイル名)を適当に指定してCreateする。

追加されたScriptファイルを編集する画面が開く。別の編集画面を開いてしまった場合は、Scene dockの赤枠で囲ったScriptマークを押すとこのスクリプト編集画面に戻ってくることができる。

extends Node2D

func _ready():
	get_node("Button").connect("pressed", self, "_on_Button_pressed")

func _on_Button_pressed():
	get_node("Label").text = "HELLO!"

ドキュメントに従って、上記のコードを記述する。

_ready()はAttachされたNode(この場合はRoot NodeのNode2D)がScene上でアクティブになったタイミングで呼ばれるコールバック関数。get_node()はそのNodeの子Nodeの中から、名前が一致するNodeを検索する関数。Sceneに配置されたボタンの名前(Scene dock上にリストアップされている名前)はButtonとなっているので、get_note(“Button”)でこのボタンへの参照が取得できる。get_nodeは子Nodeに対して検索をする関数なので、もしスクリプトが別の場所にアタッチされていたり、Buttonが別の場所(Labelの子Nodeになっていたり)にあったりすると検索に失敗するので注意が必要。

connect(“pressed”, self, “_on_Button_pressed”)では、ボタンにおける”pressed”というSignalがあった場合に、自身(_self)の”_on_Button_pressed”という関数を実行させる、という設定を行っている。この設定はコードで書く代わりにエディタ右側の「Node」からUI上で設定することもできるが、慣れないうちは「どこで何をやっているか」分からなくなりがちなので全てコード上で管理した方が混乱しなくて良さそうだ。

get_node(“Label”).text = “HELLO!” は書いてある通りだが、Labelの文字列をHELLO!に書き換えるためのコード。

この状態でSceneを実行し、ボタンを押してみるとLabelの文字列が変わることが確認できた。

カウンターにしてみる(変数を使ってみる)

コードを少しいじってカウンターを作ってみる。

extends Node2D

var counter = 0

func _ready():
	get_node("Button").connect("pressed", self, "_on_Button_pressed")

func _on_Button_pressed():
	counter += 1
	get_node("Label").text = str(counter)

var counterでカウントアップ用の変数を用意し、_on_Button_pressed()内でカウントアップする。counterの中身はintだが、LabelのtextはString型なので代入する際にはstr関数を使ってintからStringへの変換が必要になる。

実行してボタンを何度か押してみるとちゃんとカウントアップされていることが確認できた。

複数のSceneインスタンスがあった場合の挙動

複数のSceneインスタンスがあった場合の挙動も確認しておこう。

新しい2D Sceneを作成し、そこに先ほど作ったLabelとButtonから構成されるSceneのインスタンスを複数配置して実行してみる。

ボタンを押してみると、それぞれのインスタンスごとに独立してカウンターがカウントアップされていくのを確認できた。

親Sceneから子SceneのNodeを操作する

親Sceneから子SceneのNodeを操作してみる。親SceneにButtonを設置しスクリプトをAttachし、コードを記述する。

extends Node2D

func _ready():
	get_node("Button").connect("pressed", self, "_on_Button_pressed")

func _on_Button_pressed():
	get_node("Node2D3/Label").text = "Nantekottai..."

get_node()ではスラッシュ(/)を使うことで階層を指定してNodeを探索することができる。get_node(“Node2D3/Label”)は親SceneにあるNode2D3というNodeの子NodeのLabelというNodeを探索する。

上記のコードで、子SceneのNodeを操作できることを確認できた。ただし、親Sceneが子Sceneの構造を指定して操作するやり方は、後の子Sceneの構造変更の可能性などを考えるとあまり良いやり方ではないので、基本的には子Sceneの方に関数を定義しておいて、それを呼び出す形にした方が良いだろう。

// 子Sceneのコード
extends Node2D


func sayNantekottai():
	get_node("Label").text = "Nantekottai!"


// 親Sceneのコード
extends Node2D

func _ready():
	get_node("Button").connect("pressed", self, "_on_Button_pressed")

func _on_Button_pressed():
	get_node("Node2D3").sayNantekottai()

コード内から子Sceneのインスタンスを追加してみる

エディタ上で子Sceneを追加し、それらに対して親Sceneから操作を行う方法は分かったが、実際のゲームではインスタンスはコードから生成したいことが多いと思うので、その方法についても調べておく。

extends Node2D

const ChildScene = preload("res://LabelAndButton.tscn")

var instanceNumber = 0

func _ready():
	get_node("Button").connect("pressed", self, "_on_Button_pressed")

func _on_Button_pressed():
	var anInstance = ChildScene.instance()
	anInstance.name = "ChildInstance" + str(instanceNumber)
	anInstance.position = Vector2(instanceNumber * 100, 0)
	instanceNumber += 1
	self.add_child(anInstance)

子Sceneを追加するには、まずpreloadを使って予めtscnファイルの中身をコード上で読み込んでおく必要がある。tscnファイルのパスはFileSystem dockから確認することができる。

上のコードではボタンが押される度にインスタンスが追加されるようになっている。上で読み込んだtscnファイルに対して、instance()を呼ぶことで新しいインスタンスが作られる。作られたインスタンスは後から参照できるようにユニークなnameをつけておきたいので、追加した順番にChildInstance0, ChildInstance1…といったnameをつけるようにした。親Sceneのself.add_childを呼ぶことで、生成した新しい子Sceneインスタンスを親Sceneに追加することができるのだが、同じ場所にインスタンスを追加すると追加されたのかがわかりにくいので、表示位置を表すpositionプロパティに2次元座標(Vector2D)でx: instanceNumber * 100, y: 0の位置に追加されるようにした。

実行してみた様子。親SceneのAdd child sceneボタンを押す度に子Sceneのインスタンスが追加されていく。子Sceneのインスタンスはエディタで追加した時同様独立して動作するので、ボタンを押すとカウンターがそれぞれカウントアップされる。

子SceneをGroupで管理する

子SceneはGroupという機能でグルーピングして管理することができる。親Sceneに別のButton(Button2)を追加して、押されたら全ての子Sceneに対してsayNantekottai関数を呼び出すようにしてみた。

extends Node2D

const ChildScene = preload("res://LabelAndButton.tscn")

var instanceNumber = 0

func _ready():
	get_node("Button").connect("pressed", self, "_on_Button_pressed")
	get_node("Button2").connect("pressed", self, "_on_Button2_pressed")

func _on_Button_pressed():
	var anInstance = ChildScene.instance()
	anInstance.add_to_group("mygroup")
	anInstance.name = "ChildInstance" + str(instanceNumber)
	anInstance.position = Vector2(instanceNumber * 100, 0)
	instanceNumber += 1
	self.add_child(anInstance)

func _on_Button2_pressed():
	get_tree().call_group("mygroup", "sayNantekottai")

上のコードを少し変更し、生成した子Sceneのインスタンスに対して、add_to_group(“mygroup”)という関数を呼んでおくと、インスタンスがmygroupという名前のグループに追加されて管理される。グループ名は文字列で自由に決めて設定できる。タグのような感覚で使えば良い。

グループに追加されたインスタンスに対してはget_tree().call_group()関数を使うことで、一括で関数を呼び出すことができる。get_tree().call_group(“mygroup”, “sayNantekottai”)ではmygroupに登録されている全てのインスタンスのsayNantekottai関数を呼び出す。

実行してみた様子。mygroupに追加した全てのインスタンスに対して一括でsayNantekottaiを呼び出すことができた。

まとめ

スクリプトを使う基本的な方法と、スクリプト上でInstance/Nodeを操作する方法を確認した。ペース配分を間違えたので後3日でゲームが完成するのか不安になってきた。

Pocket

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です