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ノードを使わずにスクリプト内の処理で測っている。
これで、プレイヤーや敵がダメージを受けた時に、少しは見た目で分かりやすくなった…かどうかは怪しいけれど、とりあえず簡易の処理は追加できた。
次回はタイトル画面やゲームオーバー画面を作成してみる。
[ ツッコむ ]
以上です。