Godot4.1.3でシューティングゲームを作っていきます。
ボスキャラBigFireに全方位弾を発射させる
今回は攻撃方法を実装します。ボスBigFireの中心から一定周期で全方位(20°毎)に弾を発射するように実装します。
方針
シーンやスクリプト間の関係はなるべく疎にしたいと思います。
一定周期で全方位弾を発射するだけなので、全方位弾を発射するために必須とするボスBigFire情報は「発射位置」のみです。位置だけなら、ボスBigFireシーンの子ノードは自動的に親の位置を引き継ぐことができますので、一定周期で全方位弾を生成するシーンを1個作って、BigFireシーンに子ノードとして追加することで、勝手に全方位弾を発射するようにします。
github
本記事で実装したものをgithubで公開しています。
修正および新規追加ファイル
全方位弾発射用シーン「fire_360_bullet_periodically.tscn」とスクリプトを新規実装して、ボスBigFileシーン「boss_bigfile.tscn」に子ノードとして追加します。
全方位弾発射用シーンを追加
一定時間毎に全方位弾を発射するシーンを追加します。
res://character/enemy_bulletに新規シーンを追加して、fire_360_bullet_periodically.tscnに名称変更します。ルートノードはMarker3Dにします。子ノートにTimerノードを追加して名称をFireTimerに変更しました。
発射周期を管理するTimerノードのタイムアウト時間は、スクリプトからタイマーを開始する時に設定するので、Wait Timeプロパティは、ディフォルトの設定のままで良いです。
タイムアウト後は自動でタイマーを再開してほしいので、One Shotプロパティもディフォルトのままオフです。
全方位弾発射用シーンのスクリプトを実装
ルートノードにスクリプトをアタッチして下記のように修正します。
res://character/enemy_bullet/fire_360_bullet_periodically.gd
extends Marker3D
var _d_fire_interval_sec = 2.0
var _i_fire_interval_deg = 20
var _scn_bullet : PackedScene = preload("res://character/enemy_bullet/enemy_bullet.tscn")
func _ready():
$FireTimer.start(_d_fire_interval_sec)
func _on_fire_timer_timeout():
for deg in range(0, 360, _i_fire_interval_deg):
var ins = _scn_bullet.instantiate()
var dir = Vector3(cos(deg_to_rad(deg)), 0.0, sin(deg_to_rad(deg)))
ins.set_pos( global_position, global_position + dir)
g_val.node_bullets.add_child(ins)
スクリプトの説明をします。
_readyメソッド内で、全方位弾を発射するためのタイマーを開始するために、周期時間を指定してstartメソッドを実行します。
var _d_fire_interval_sec = 2.0
func _ready():
$FireTimer.start(_d_fire_interval_sec)
次にタイムアウト時に、全方位弾のインスタンスを生成するように実装します。
まず、タイムアウトを検知したいので、FireTimerノードのtimeoutシグナルをスクリプトの_on_fire_timer_timeoutメソッドに接続します。
最初のfor文では、「range(0, 360, 20)」分繰り返します。つまり、0~360を20刻みなので、0, 20, 40, ....320, 340まで18回ループします。
ループの中では、敵の弾のインスタンスを生成したあと、dirに弾が移動する向きを設定しています。XZ平面上で、ある角度を向いている方向のx座標はcos関数、z座標はsin関数で得ることができるので、
dir = Vector3( cos(角度), 0, sin(角度) )
で移動する向き、dirを計算することができます。sinとcosの引数の単位はラジアンなので、角度はdeg_to_rad関数でラジアンに変換する必要があります。
あとは敵の弾のインタンスのset_posメソッドを使って、現在の位置と移動方向の位置を設定します。
現在の位置 :global_position
移動方向の位置:global_positionにdirを足した値
最後に敵の弾のインスタンスを動的インスタンスの管理ノードに追加します。
var _i_fire_interval_deg = 20
var _scn_bullet : PackedScene = preload("res://character/enemy_bullet/enemy_bullet.tscn")
func _on_fire_timer_timeout():
for deg in range(0, 360, _i_fire_interval_deg):
var ins = _scn_bullet.instantiate()
var dir = Vector3(cos(deg_to_rad(deg)), 0.0, sin(deg_to_rad(deg)))
ins.set_pos( global_position, global_position + dir)
g_val.node_bullets.add_child(ins)
全方位弾発射用シーンをボスBigFireに追加
ボスBigFireシーン(res://character/boss/boss_bigfire.tscn)に、全方位弾発射用シーン(re_360_bullet_periodically.tscn)を追加するだけです。
以上です。