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("グループ名") で、当たった何かがそのグループなら、てな感じの処理が書ける。このあたり、レイヤーとマスクを使うことでも処理ができそうな気もするけれど、まだそこまで調べてなかったり。
[ ツッコむ ]
以上です。