ゲームエンジンGodot4.0で3Dスマホゲームを作りたいと思いますが、その前にお勉強しています。
Godot_v4.0-rc1_win64.exe.zipを使用しています。
目的
敵をシーンファイル化して、複数設定できるようにします。
またBulletがEnemyに当たった場合、お互い攻撃してHPが0になったら消える様にします。
ベースプロジェクト
下記で作成したプロジェクトをベースに機能追加をします。
【Godot 4.0】スマホ3Dゲームを作るための勉強 その9 敵を検知するとホーミングするようにしたい
https://qiita.com/FootInGlow/items/5b1c78669e2d56d98f1b
github(Godotのプロジェクトマネージャーからインポートして利用できます)
https://github.com/footinglow/Godot4/tree/main/02_study/S09_enemy_001
Enemyブランチをシーンファイルにする
Enemyを右クリックして「ブランチをシーンとして保存」を実行します。
ダイアログが表示されるので問題なければそのまま保存します。
Enemyとその子ノードがひとつのシーンになりました。映画のカチンコ🎬アイコンが表示されます。
またファイルシステムにはenemy.tscnとして保存されました。
Enemyを増やす
Enemyを追加しましょう。
globalを右クリックして、「Instantiate Child Scene」を実行します。
enemy.tscnを選択して「開く」ボタンを押下します。
Enemy2が追加されました。
Enemy2を選択して、インスペクタのNode3D/Transform/positionのX=-2.0m、Z=-8mを設定します。
Enemyを倒す
Enemy、Bulletに攻撃力とHPを設定してHPが0になったら消えるようにしましょう。
攻撃力は1秒間に削るHP量にします。
enemy.gdに追加
@export var m_attack_power_ps = 1.0
@export var m_hp = 10
bullet.gdに追加
@export var m_attack_power_ps = 1.0
@export var m_hp = 1
BulletがEnemyとバトルする
攻撃力を1秒間に削るHP量にしたので、_physics_processで継続的にBulletがEnemyを接触しているか判定するようにします。
Enemyを「EnemyGroup」グループに設定する
Bulletが衝突したノードが攻撃対象であるか判定を簡単にするため、グループ機能を利用します。
グループに登録したノードは検索できるようになります。
enemy.tscnを開いてEnemyを選択します。
インスペクタの横のノード/グループを選択します。
「EnemyGroup」と入力して、追加ボタンを押下します。
追加されました。
シーンのEnemyノードの右にはグループに参加していることを表す四角の中に●があるアイコンが表示されました。このアイコンの上をマウスオーバーすると「Node is in this group: EnemyGroup」と表示されるので、「EnemyGroup」グループに登録されたことがわかります。
bullet.gdにバトル処理を追加
_physics_process()のmove_and_slide()実行後にバトル処理を追加しました。
extends CharacterBody3D
@export var m_v_dir = Vector3.FORWARD
@export var m_d_speed_mps = 2.0
@export var m_attack_power_ps = 1.0
@export var m_hp = 1
var m_v3_enemy_pos = null
func _physics_process(delta):
if m_v3_enemy_pos:
m_v_dir = m_v3_enemy_pos - transform.origin
else:
m_v_dir = Vector3.FORWARD
velocity = m_v_dir.normalized() * m_d_speed_mps
move_and_slide()
# バトル判定
for index in range(get_slide_collision_count()):
var collision = get_slide_collision(index)
if (collision.get_collider() == null):
continue
if collision.get_collider().is_in_group("EnemyGroup"):
var enemy = collision.get_collider()
# Bulletの攻撃
enemy.m_hp -= m_attack_power_ps * delta
# Enemyの攻撃
m_hp -= enemy.m_attack_power_ps * delta
# HPが0になったら消える
if m_hp < 0:
queue_free()
func discover_enemy(enemy_pos):
m_v3_enemy_pos = enemy_pos
func _on_visible_on_screen_notifier_3d_screen_exited():
queue_free()
move_and_slide()の後がバトルの処理です。
衝突情報をチェックして、Enemyを検索します。
-
for index in range(get_slide_collision_count()):
get_slide_collision_count()により、move_and_slide()の結果、衝突した回数(衝突により方向を変えた回数)を取得し、forループします -
var collision = get_slide_collision(index)
index番目の衝突情報をcollisionとして取得 -
if (collision.get_collider() == null):
collision衝突情報がない場合continueして次を検索 -
if collision.get_collider().is_in_group("EnemyGroup"):
衝突したノードが「EnemyGroup」に属しているかチェックします -
var enemy = collision.get_collider()
「EnemyGroup」グループに所属しているノードインスタンスを取得します。
Enemyと接触している場合、お互いの攻撃力を使用してHPを削ります。
1秒間でHPを削る量なので、deltaを掛けた攻撃量をHPから減算しています。
- enemy.m_hp -= m_attack_power_ps * delta
- m_hp -= enemy.m_attack_power_ps * delta
ループを抜けた後、Bulletのhpが0になっていたらBulletを消します。
- if m_hp < 0:
- queue_free()
EnemyのHPチェック処理を追加
enemy.gdを開いてhpをチェックする処理を追加します。EnemyのHPが0になったら、Enemyを削除します。
_physics_process()が追加した処理です。
extends CharacterBody3D
@export var m_attack_power_ps = 1.0
@export var m_hp = 10
func _physics_process(delta):
if m_hp < 0.0:
queue_free()
func _on_bullet_sensor_body_entered(body):
body.discover_enemy(transform.origin)
実行しましょう。
Bulletが10個ぶつかるとEnemyが消えるようになりました。
バグ修正:Bulletが残る
Enemyが消えたあとに、残るBulletがいます。
Enemyを倒してもその前にBulletに設定した目標位置m_v3_enemy_posが残っているためです。
やり方はいくつかありそうですが、ほぼ目標位置に到達した場合(Enemyがいる場合はぶつかって止まる)目標位置を削除することで対処します。
extends CharacterBody3D
@export var m_v_dir = Vector3.FORWARD
@export var m_d_speed_mps = 2.0
@export var m_attack_power_ps = 1.0
@export var m_hp = 1
var m_v3_enemy_pos = null
func _physics_process(delta):
if m_v3_enemy_pos:
m_v_dir = m_v3_enemy_pos - transform.origin
# 向かっている途中でEnemyが消えた場合、ほぼm_v3_enemy_posの位置まできたら目標位置を消して再び前進する
if m_v_dir.length() < 0.1:
m_v3_enemy_pos = null
else:
m_v_dir = Vector3.FORWARD
velocity = m_v_dir.normalized() * m_d_speed_mps
move_and_slide()
# バトル判定
for index in range(get_slide_collision_count()):
var collision = get_slide_collision(index)
if (collision.get_collider() == null):
continue
if collision.get_collider().is_in_group("EnemyGroup"):
var enemy = collision.get_collider()
# Bulletの攻撃
enemy.m_hp -= m_attack_power_ps * delta
# Enemyの攻撃
m_hp -= enemy.m_attack_power_ps * delta
# HPが0になったら消える
if m_hp < 0:
queue_free()
func discover_enemy(enemy_pos):
m_v3_enemy_pos = enemy_pos
func _on_visible_on_screen_notifier_3d_screen_exited():
queue_free()
_physics_process()メソッドのm_v3_enemy_posが設定されている場合の処理の中に下記の処理を追加しました。
0.1は十分近づいた値として適当に設定したものです。
# 向かっている途中でEnemyが消えた場合、ほぼm_v3_enemy_posの位置まできたら目標位置を消して再び前進する
if m_v_dir.length() < 0.1:
m_v3_enemy_pos = null
実行しましょう。
Bulletがとまることがなくなりました。
以上