2020/11/17(火) [n年前の日記]
#1 [godot] Godot Engineでダメージエフェクトを作成
Godot Engine 3.2.3 x64 を使って3D表示の簡単なシューティングゲームっぽいものを作る。
今回は、プレイヤーや敵に弾が当たったことを分かりやすくするために簡単なダメージエフェクトをつけてみる。
今回は、プレイヤーや敵に弾が当たったことを分かりやすくするために簡単なダメージエフェクトをつけてみる。
◎ プレイヤーの点滅表示処理を追加。 :
やはりアクションゲームの類で自機がダメージを受けたら点滅するものだろう。って一体いつの時代の話ですか…昭和か…。何にせよ、とりあえず点滅表示処理を追加してみる。
プレイヤーのシーン、res://assets/Player.tscn を開いて、Timer を2つ追加。DmagaeTimer と BlinkTimer にリネーム。

DamageTimer には、ダメージを受けてから何秒間はダメージを受けないかを指定。

BlinkTimer には、表示と非表示を切り替える時間間隔を指定。

それぞれの timeoutシグナルにメソッドを接続してやる。
スクリプトファイル Player.gd に処理を追加。内容は以下。今回の処理に関係があるところだけ貼っておく。
_Player.gd
これで、プレイヤーに敵弾が当たってダメージを受けると一定時間点滅するようになった。
プレイヤーのシーン、res://assets/Player.tscn を開いて、Timer を2つ追加。DmagaeTimer と BlinkTimer にリネーム。

DamageTimer には、ダメージを受けてから何秒間はダメージを受けないかを指定。
- Wait Time を 2 に。
- One Shot を有効化。

BlinkTimer には、表示と非表示を切り替える時間間隔を指定。
- Wait Time を 0.05 に。

それぞれの timeoutシグナルにメソッドを接続してやる。
- DmagaeTimer や BlinkTimer を選んでから、ノードタブをクリック → シグナルをクリック。
- timeout を右クリックして「接続」。
- Player を選んで「接続」。
スクリプトファイル Player.gd に処理を追加。内容は以下。今回の処理に関係があるところだけ貼っておく。
_Player.gd
# ...
var damaging = false
func _ready():
# ...
damage = 0
damaging = false
# ...
func _physics_process(delta):
# ...
if not damaging:
if damage > 0:
hp -= damage
damage = 0
if hp <= 0:
hp = 0
emit_signal("player_damaged")
damaging = true
$DamageTimer.start()
$BlinkTimer.start()
# ...
func _on_DamageTimer_timeout():
$BlinkTimer.stop()
damage = 0
damaging = false
visible = true
if hp <= 0:
hp = 0
emit_signal("player_died")
func _on_BlinkTimer_timeout():
visible = not visible
- ダメージ中を示すフラグ、damaging を用意。
- ダメージ中は、自分がダメージを受けたかどうかは判定しない。
- ダメージを受けたら、ダメージ中フラグを立ててから、$DamageTimer.start()、$BlinkTimer.start() を呼んで、2つの Timer を開始させる。
- BlinkTimer は指定した時間間隔で timeoutシグナルを何度も繰り返し発行する。そのたびに _on_BlinkTimer_timeout() が呼ばれる。このメソッドの中で、visibleプロパティを反転させて、表示と非表示を切り替えている。
- DmagaeTimer は指定時間が経過すると timeoutシグナルを1回だけ発行。_on_DamageTimer_timeout() が呼ばれる。BlinkTimerを停止させて、ダメージ中フラグを降ろして、表示を有効化。
これで、プレイヤーに敵弾が当たってダメージを受けると一定時間点滅するようになった。
◎ 画面フラッシュを追加。 :
プレイヤーに弾が当たると点滅するようになったけど、これではまだダメージを受けたことが伝わりにくいので、画面を軽くフラッシュさせてみる。
フラッシュさせるためのノードは、HUDシーンに追加する。res:/assets/Hud.tscn を開く。
ColorRectノードを追加。ColorRectノードは指定色で矩形を描画できる。

Flash にリネーム。

Flash (ColorRect) のプロパティを変更。

Timer を追加。FlashTimer にリネーム。この Timer を使って、一定時間が過ぎたらフラッシュ状態を終了させる。

FlashTimer のプロパティを変更。

FlashTimer の timeoutシグナルにメソッドを接続。
Flash の表示順を変更。ノード一覧の一番上に置くことで、HP表示より奥に表示されるようにする。
HUDシーンのスクリプト、res://scripts/Hud.gd を修正。内容は以下。
_Hud.gd
Mainシーンのスクリプト、res://scripts/Main.gd も修正。内容は以下。
_Main.gd
これで、プレイヤーに敵弾が当たってダメージを受けると、一瞬画面がフラッシュするようになった。
フラッシュさせるためのノードは、HUDシーンに追加する。res:/assets/Hud.tscn を開く。
ColorRectノードを追加。ColorRectノードは指定色で矩形を描画できる。

Flash にリネーム。

Flash (ColorRect) のプロパティを変更。
- Color を RGBA=(255, 255, 255, 200) に。
- Size x, y を (1280, 720) に。ゲームウインドウ全体を覆いつくすサイズを指定。

Timer を追加。FlashTimer にリネーム。この Timer を使って、一定時間が過ぎたらフラッシュ状態を終了させる。

FlashTimer のプロパティを変更。
- Wait Time を 0.125 に。0.125秒だけ Flash を表示させる。
- One Shot を有効化。

FlashTimer の timeoutシグナルにメソッドを接続。
- FlashTimer を選択してから、ノードタブをクリック → シグナルをクリック。
- timeout を右クリックして「接続」。
- Hud を選択して「接続」。
Flash の表示順を変更。ノード一覧の一番上に置くことで、HP表示より奥に表示されるようにする。
HUDシーンのスクリプト、res://scripts/Hud.gd を修正。内容は以下。
_Hud.gd
# ...
func _ready():
$Flash.visible = false
# ...
func start_flash():
$Flash.visible = true
$FlashTimer.start()
func _on_FlashTimer_timeout():
$Flash.visible = false
- 初期化処理時に Flashノードを非表示にする。
- 画面フラッシュを開始するメソッド、start_flash() を追加。Flashノードの表示を有効化して、FlashTimer を開始している。
- FlashTimer は指定時間が経過すると timeout シグナルを発行して _on_FlashTimer_timeout() が呼ばれる。Flashノードを非表示にする。
Mainシーンのスクリプト、res://scripts/Main.gd も修正。内容は以下。
_Main.gd
# ...
func damage_player():
# ...
$Hud.start_flash()
# ...
- プレイヤーがダメージを受けると Main.gd内の damage_player() が呼ばれる。その中で $Hud.start_flash() を呼んで Hud の画面フラッシュを開始させる。
これで、プレイヤーに敵弾が当たってダメージを受けると、一瞬画面がフラッシュするようになった。
◎ 敵のダメージエフェクトを追加。 :
敵のスクリプト、res://scripts/EnemyZako.gd を修正して、敵がダメージを受けた時のエフェクト(?)を追加する。
今回は、一定時間(0.25秒)、ランダムに位置がぶれる処理を追加してみた。
_EnemyZako.gd
これで、プレイヤーや敵がダメージを受けた時に、少しは見た目で分かりやすくなった…かどうかは怪しいけれど、とりあえず簡易の処理は追加できた。
次回はタイトル画面やゲームオーバー画面を作成してみる。
今回は、一定時間(0.25秒)、ランダムに位置がぶれる処理を追加してみた。
_EnemyZako.gd
# ...
var damage_timer = 0
var rnd = RandomNumberGenerator.new()
func _ready():
# ...
damage = 0
damage_timer = 0
# ...
func _physics_process(delta):
# ...
check_damage()
damaging_animation(delta)
# ...
func check_damage():
if damage > 0:
hp -= damage
damage = 0
damage_timer = 0.25
if hp <= 0:
hp = 0
emit_signal("enemy_died")
emit_signal("enemy_damaged")
func damaging_animation(delta):
if damage_timer > 0:
damage_timer -= delta
var target_mesh = $EnemyZakoModel
if damage_timer <= 0:
damage_timer = 0
target_mesh.translation = Vector3()
else:
var r = 0.5
var x = rnd.randf_range(-r, r)
var z = rnd.randf_range(-r, r)
target_mesh.translation = Vector3(x, 0, z)
# ...
func _on_EnemyZako_area_entered(area):
if is_queued_for_deletion():
return
# print("playerbullet hit the enemy")
if area.is_in_group("playerbullets"):
damage = area.attack_point
# ...
- var rnd = RandomNumberGenerator.new() で疑似乱数生成器を用意する。
- rnd.randf_range(-r, r) で、-r から r の範囲で乱数を生成。
- 自分(EnemyZako)の座標ではなく、その下にぶら下がってる EnemyZakoModel(表示モデルを担当)の位置をずらしている。
- 時間経過を、Timerノードを使わずにスクリプト内の処理で測っている。
これで、プレイヤーや敵がダメージを受けた時に、少しは見た目で分かりやすくなった…かどうかは怪しいけれど、とりあえず簡易の処理は追加できた。
次回はタイトル画面やゲームオーバー画面を作成してみる。
[ ツッコむ ]
以上です。
