Godot 音シュー13「シーンのインスタンスとアニメ 2/2」 preload/ instance/ add_child
作ったシーンをノードにくっ付けよう。実はコレ、「大昔」にやってたようだが、どうもそれは間違ったやり方だったようだ。こわいこわい。間違いポイントは、「一旦queue_free()
したノードは2度とadd_child()
出来ない」って事を知らなかった所。今回はちゃんとやろう。
スクリプト
左エリアに貼るスクリプトだけ持ってきた。
extends MeshInstance var spaMat #自身が持つSpaialMaterialの情報操作用 var colorIn = Color("#ff0000") #ビートキューブが入った時の色(赤) var colorOut = Color("#3fffffff") #ビートキューブが出た時の色(白) var nLabelScore #得点表示用Label_score2の操作用 var nCamera #Cameraノード用 var sGood = preload("res://Good.tscn") #goodSceneインスタンス用 var sMiss = preload("res://Miss.tscn") #missSceneインスタンス用 func _ready(): #メッシュとマテリアルが入ってないとエラーになるから注意 spaMat = get_surface_material(0) #0番目に入ってるヤツ ゲット nLabelScore = get_node("../../../Label_score2") #得点表示用Label_score2ゲット nCamera = get_node("../../Camera") func _physics_process(delta): if Input.is_action_just_pressed("myA"): #キーを押した時瞬間 if spaMat.albedo_color == colorIn: #ビートキューブが入っている場合 spaMat.albedo_color = colorOut #連打禁止、色戻す #加点 G.myScore = G.myScore + 10 nLabelScore.text = str(G.myScore) #Good表示(シーンのインスタンス) var inst = sGood.instance() inst.rect_position = nCamera.unproject_position(global_transform.origin) add_child(inst) else: #ビートキューブが入っていない場合 #減点 G.myScore = G.myScore - 50 nLabelScore.text = str(G.myScore) #Miss表示(シーンのインスタンス) var inst = sMiss.instance() inst.rect_position = nCamera.unproject_position(global_transform.origin) add_child(inst) #キューブ入ったよ(signal from 「Area_l」) func _on_Area_l_body_entered(body): #色変えるぜ spaMat.albedo_color = colorIn #キューブ出たよ(signal from 「Area_l」) func _on_Area_l_body_exited(body): #色戻すぜ spaMat.albedo_color = colorOut
変更部分の解説
[1] 変数
L8
- カメラを変数として取るため用意する。今回はエリアの3D位置を、カメラから見た2D位置に変換したポジションを取得したいので、どのカメラから見たか情報をゲットする必要がある。
L10~11
preload()
でシーンを変数に入れる。「大昔」参照。- あくまでも「シーン」の型で入るので、このままではノードにくっ付けられない。
[2] _ready()
L18
- プレイヤに付けたカメラノードをゲットしただけ。
[3] if
L23
- コメントに書いた通り。色を戻してやらないと、連打でいつまでも得点が入ってしまう。
L28
- 新しく変数を用意し、そこに
instance()
させたものを入れる。 instance()
は「シーン」を「ノード」にする関数。"sGood"は「シーン」としての型だが、新しく作った変数"inst"は「ノード」としての型になる。- だが"inst"は、ノードはノードでも元の「シーン」のルートノードの型、つまり「Control」ノードとしての扱いになる。ここ重要!
- たまたま最近覚えた
get_class()
で見て確認したけど、get_class()
、最強だな。
L29
- 変数"inst"は「Control」ノード扱いなので、そのプロパティにRect > Positionを持っている。ここをいじって表示位置を変えようって考え。
unproject_position()
は、3D空間の物体が2Dのゲーム画面のどの位置になるか教えてくれる関数。簡単に言うと、3次元位置を2次元位置に変換するって事。で、どのカメラを基にしてるかをドット"."の前に置く。
- カッコ内の
global_transform.origin
はこのスクリプトが付いてる「MeshInstance」ノードの中心。グローバル位置。 - これで、3D空間所にある「MeshInstance」が2D画面上のどの位置にあるのかが、"inst"に入った。
L30
add_child()
でノードを追加する。これで画面上に表示されることとなる。- 単に
instance()
するだけじゃダメね。add_child()
しないと見えないから。
L35~38
- こっちはミスった時のインスタンス。やってることは上と同じ。
以上の事を上下左右のエリアにやればいいだけなので、省略。
if Input.is_action_just_pressed("myA"):
の所の"myA"を"myD"、"myW"、"myS"にそれぞれ変えるのを間違わなければだいじょぶだろう。あと、
func _on_Area_l_body_entered(body): func _on_Area_l_body_exited(body):
の所は、"l"をそれぞれ"r"、"u"、"d"に。
忘れてた
アニメのAutoplayを有効にしとかないとadd_child()
した瞬間にアニメが始まらない。青くしておくこと。
もうちょい
これで、タイミングよく指定のキーが押されればアニメする「Good」表示が、変なところでキーを押せばアニメする「Miss」表示が出る。
だが、instance()
してadd_child()
しまくると追加されるノードがドンドコ増えていくので、適当な時間が経ったら消えるようにスクリプトする。
extends Control var myTimer = 0.5 func _process(delta): myTimer = myTimer -delta if myTimer < 0.0: queue_free()
消えるスクリプトは、なんでもyield()
っていうのを使うのがイイらしいんだが、よく分からんかったので"delta"で数えて消す。
インスタンスのまとめ
preload()
してinstance()
してadd_child()
。queue_free()
忘れずに。
だな。
ふむ
文字表示の場所、OK!
一応これで、「音ゲー」部分はOKとして、次からは「シューター」部分の作成に入ろうかと思う。