2020/04/02(木) [n年前の日記]
#1 [godot] Godot Engineで弾を撃つ
Godot Engine でプレイヤーの弾を撃つ処理をどう書けばいいのか悩んでいたのだけど。公式ドキュメントのFPS関連チュートリアル記事を眺めて、なんとなく分かってきた。環境は、Windows10 x64 1909 + Godot Engine 3.2.1 x64。
_パート2 - Godot Engine latest ドキュメント
とりあえず、プレイヤーの弾を担当するシーン、PlayerBullet.tscn を作成しておいて…。
プレイヤーキャラを担当するシーン、Player.tscn に割り当てたスクリプトの中で、以下のような感じで処理。ちなみにプレイヤーは、KinematicBody から作った。
var bullet = preload("res://assets/PlayerBullet.tscn") で、事前にプレイヤー弾のシーンを読み込んでおいて、bullet.instance() でインスタンスを作成。しかるべきノードに、add_child(clone) で、子ノードとして追加。これで一応画面上にプレイヤーの弾が次々に発生してくれた。
_パート2 - Godot Engine latest ドキュメント
とりあえず、プレイヤーの弾を担当するシーン、PlayerBullet.tscn を作成しておいて…。
プレイヤーキャラを担当するシーン、Player.tscn に割り当てたスクリプトの中で、以下のような感じで処理。ちなみにプレイヤーは、KinematicBody から作った。
extends KinematicBody var bullet = preload("res://assets/PlayerBullet.tscn") # ... var shot_counter = 0 func _ready(): shot_counter = 0 # ... func _physics_process(delta): # ... # born player bullets shot_counter += delta if shot_counter >= (4 * 1.0 / 60.0): shot_counter = 0 _born_player_bullet(translation) # ... func _born_player_bullet(pos): var clone = bullet.instance() clone.translation = pos clone.translation.z -= 2.5 var scene_root = get_tree().root.get_children()[0] scene_root.add_child(clone)
var bullet = preload("res://assets/PlayerBullet.tscn") で、事前にプレイヤー弾のシーンを読み込んでおいて、bullet.instance() でインスタンスを作成。しかるべきノードに、add_child(clone) で、子ノードとして追加。これで一応画面上にプレイヤーの弾が次々に発生してくれた。
◎ プレイヤーの弾の移動。 :
プレイヤーの弾は、画面奥へと飛んでいってほしい。そのあたりの処理を試したり。
プレイヤーの弾は、Area で用意した。
スクリプトは以下のような感じで。
translation の z に値を加算、というか減算してみたら、奥方向に移動していく感じで動いてくれた。
ただ、こういう動かし方でいいのか分からんけど…。KinematicBody の場合は move_and_slide() を呼んで動かしてたけど、Area の場合は move_and_slide() を呼ぼうとしても「そんなメソッドは無い」と言われてしまって。しかし、こんな動かし方をしてもいいのだろうか…。どうなんだろう…。
そのままだと、ずっとどこまでも飛んで残り続けてしまうので、時間を測って一定時間が経ったら自分を殺す、みたいな処理も一応入れた。本当は画面外に出たかどうかチェックして消すべきだろうけど、どうやってソレを調べればいいのか…。
プレイヤーの弾は、Area で用意した。
Area MeshInstance CollisionShape
スクリプトは以下のような感じで。
extends Area var counter = 0 export var kill_time = 2.0 export var speed = 1.5 var velocity = Vector3() func _ready(): counter = 0 func _physics_process(delta): counter += delta if counter >= kill_time: queue_free() return velocity = Vector3(0, 0, -speed * 60 * delta) translation += velocity
translation の z に値を加算、というか減算してみたら、奥方向に移動していく感じで動いてくれた。
ただ、こういう動かし方でいいのか分からんけど…。KinematicBody の場合は move_and_slide() を呼んで動かしてたけど、Area の場合は move_and_slide() を呼ぼうとしても「そんなメソッドは無い」と言われてしまって。しかし、こんな動かし方をしてもいいのだろうか…。どうなんだろう…。
そのままだと、ずっとどこまでも飛んで残り続けてしまうので、時間を測って一定時間が経ったら自分を殺す、みたいな処理も一応入れた。本当は画面外に出たかどうかチェックして消すべきだろうけど、どうやってソレを調べればいいのか…。
◎ 衝突判定で悩んだ。 :
プレイヤーの弾、及び、敵を、Area から作っていったのだけど。何かと衝突したら body_entered シグナルが発生するはず、と思って処理を書いたら無反応で悩んだり。
調べてみたら、body_entered は、xxxxBody と衝突した時にだけ発生するようで…。StaticBody とか、RigidBody とか、KinematicBody とか。
Area と Area が衝突した際は、area_entered が発生するらしい。
しかし、自分に何が当たったのかを、どうやって調べたらいいのか…。ググってみたら、どうやらグループというものが使えそうな気配。
プレイヤーの弾には「player_bullet」を、敵には「enemy」を、グループ登録しておいて…。例えば敵の側の area_entered と接続した関数内で、以下のように書けば…。
このあたり、レイヤーとマスクを使うことでも処理ができそうな気もするけれど、まだそこまで調べてなかったり。
調べてみたら、body_entered は、xxxxBody と衝突した時にだけ発生するようで…。StaticBody とか、RigidBody とか、KinematicBody とか。
Area と Area が衝突した際は、area_entered が発生するらしい。
しかし、自分に何が当たったのかを、どうやって調べたらいいのか…。ググってみたら、どうやらグループというものが使えそうな気配。
プレイヤーの弾には「player_bullet」を、敵には「enemy」を、グループ登録しておいて…。例えば敵の側の area_entered と接続した関数内で、以下のように書けば…。
func _on_EnemyZako_area_entered(area): if area.is_in_group("player_bullet"): # Collide with player bullets damage_timer = 0.3 hp -= area.attack_point print("Hit. My HP = " + String(hp)) if hp <= 0: queue_free() # I am dead.area.is_in_group("グループ名") で、当たった何かがそのグループなら、てな感じの処理が書ける。
このあたり、レイヤーとマスクを使うことでも処理ができそうな気もするけれど、まだそこまで調べてなかったり。
[ ツッコむ ]
以上です。