0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Godot 4.0】スマホ3Dゲームを作るための勉強 その9 敵を検知するとホーミングするようにしたい

Posted at

ゲームエンジンGodot4.0で3Dスマホゲームを作りたいと思いますが、その前にお勉強しています。
Godot_v4.0-rc1_win64.exe.zipを使用しています。

目的

 発射できるようになったので、一定距離に敵を見つけたら向かうようにしたい。

ベースプロジェクト

 下記で作成したプロジェクトをベースに機能追加をします。

 【Godot 4.0】スマホ3Dゲームを作るための勉強 その8 発射する
 https://qiita.com/FootInGlow/items/dd0f63bf7a9cd92801b7

敵をつくる

 CharacterBody3Dで敵を作成します。
 global.tscnを開いて、シーンのglobalを右クリックして子ノードを追加します。
 CharacterBody3Dを追加して、Enemyに名称を変更します。
global.tscn - S09_enemy_001 - Godot Engine 2023_02_28 22_57_23.png

  • Enemy
     Node3/Transform/yに0.5mを設定します。
  • MeshInstance3Dに円柱(シリンダー)型を設定します。
     インスペクタ内のMeshInstance3D/Meshを新規CylinderMeshに設定します。
  • CollisionShape3Dに衝突形状を設定
     MeshInstance3Dと同じ形にします。
     インスペクタ内のCollisionShape3D/Shapeに新規CylinderShape3Dを設定
  • DirectionalLight3D
     敵が暗いので、Node3D/Transform/rotation/xを-10°に設定します。

 実行します。
 発射した弾丸が敵にぶつかるとよけていきます。
 お互いCharacterBody3Dで衝突形状を設定してmove_and_slide()で動かしているので当然ですが、ある程度近づくと敵に向かうようにしたいです。
S09_enemy_001 (DEBUG) 2023_03_01 9_58_15.png

衝突をCollision Layer/Maskでコントロールする

 今はディフォルトの設定をしているので、すべてのCharacterBody3D/StaticBosy3D(RigidBody3D)が衝突し、Area3Dがあれば衝突を検知する状態ですが、ノードをグループ分けして適切に衝突制御、検知をするようにしたいと思います。

Collision Layerを定義する

 衝突・検知の観点でノードのグループを定義します。
 プロジェクトメニューのプロジェクト設定でLayer Names/3D物理を開きます。
 Layer1~4までLayer名を入力します。

スクリーンショット (17).png

Layer名 対象
FloorAndObstacles 床(と先々追加する障害物)
Player プレイヤー
Enemy
Bullet プレイヤーが発射したもの

Player、StaticBody3D(床)、Enemy、BulletにCollision LayerとMaskを設定する

 衝突・検知対象のノード、Player、StaticBody3D(床)、Enemy、BulletにLayerとして種別を設定します。

 自身が属するLayer(グループ)を設定します。
 Maskには自身が衝突/検出する対象を設定します。

 Layerごとに衝突・検出したいものをまとめました。

Layerに設定するもの Maskに設定するもの(衝突・検出したいもの)
FloorAndObstacles なし(検出されますが、自ら衝突/検出したいということはない)
Player FloorAndObstacles(そのうち左右に壁を作って移動制限をしたい)
Enemy なし(今のところ移動予定はないので)
Bullet FloorAndObstacles、Enemy、Bullet※に衝突してほしい。検出はしない ※Bullet同士も重ならないようにしたい

 今のところPlayerは誰からも衝突、検出されませんが一応設定してあります。 

 シーンで対象のノードを選択して、インスペクタで設定します。
 LayerもMaskも対象の番号をチェックしますが、右側の「...」をクリックすると先ほどプロジェクト設定で設定した名称が表示されるので、こちらの方が設定しやすいと思います。

  1. StaticBody3D(床)
     インスペクタのCollisionObject3D/Collisionで設定します。
     FloorAndObstaclesのLayerは1番です。Maskは未選択。
    スクリーンショット (25).png

  2. Player
     インスペクタのCharacterBody3D/Collisionで設定します。
     PlayerはLayerは2です。Maskは1:FloorAndObstaclesを選択しています。
    スクリーンショット (27).png

  3. Enemy
     インスペクタのCharacterBody3D/Collisionで設定します。
     EnemyはLayer3番です。Maskは未選択。
    スクリーンショット (28).png

  4. Bullet
     インスペクタのCharacterBody3D/Collisionで設定します。
     BulletはLayer4番です。
     Bulletは1:FloorAndObstacles、3:Enemy、4:Bulletと衝突します。
    スクリーンショット (23).png

ある程度近づいたら敵に向かうようにしたい

Enemyに円を設定する

 Bulletを賢くして、ある程度近づいたらEnemyに向かうようにします。
 Area3Dをセンサーとして使用します。

 global.tscnのEnemyを右クリックして、Area3Dを追加します。
 名称を「BulletSensor」に変更します。
 BulletSensorを右クリックしてCollisionShape3Dを追加します。
bullet.tscn - S09_enemy_001 - Godot Engine 2023_03_01 21_26_51.png

 Enemyの半径3m以内に近づいたらBulletを検出したいので、CollisionShape3Dに3mの球を設定します。

 BulletSensorの子ノードのCollisionShape3Dを選択します。
 インスペクタ内のCollisionShape3D/Shapeに新規SphereShape3Dを設定。
 SphereShape3Dをクリックして、Raduisを3.0mにします。
(_) global.tscn - S09_enemy_001 - Godot Engine 2023_03_01 10_44_43.png

 3D表示の左上を「上面図」を選択すると、Emnemyのいる位置の周囲に円が表示されています。
 global.tscn - S09_enemy_001 - Godot Engine 2023_03_01 10_46_29.png

 またデバッグメニューの「コリジョン形状を表示」にチェックを入れてから実行しても設定した円が確認できます。
 キャプチャ 2023_03_01 10_46_23.png

Enemyの円でBulletを検出する

 EnemyがBulletを検出できるようにします。

 BulletSensorを選択してCollision LayerとMaskを設定します。
 BulletSensorは誰からも衝突/検出される必要がないので、Layerは未選択です。
 Bulletを検出したいので、Maskは4を選択します。
スクリーンショット (29).png

 シグナルを接続するためにEnemyノードにスクリプトが必要です。
 Enemyを右クリックしてスクリプトをアタッチします。
 
 extendsの1行のみ残してください。

extends CharacterBody3D

 あらためてBulletSensorを選択して、(インスペクタの横の)ノードタブのシグナルを選択します。
 PhysicsBody3Dノードを検出したいので、body_entered(body:Node3D)を右クリックして接続します。
スクリーンショット (30).png

CharacterBody3DやRigidBody3Dなど検出したい場合は、body_xxx シグナルです。
Area3Dを検出したい場合は、area_xxx シグナルを使用するようです。

 Enemyのスクリプトを選択して、接続を押下します。
スクリーンショット (31).png

 BulletSensorがBulletを検知すると、Enemyのスクリプトの_on_bullet_sensor_body_enteredが呼ばれるようになりました。print分を追加して、実行して確認します。

extends CharacterBody3D

func _on_bullet_sensor_body_entered(body):
	print(body)

 実行後、シーンのリモートをクリックしてPlayerなどが見えるようにします。
 Bulletを発射すると、1発目はBulletという名前でシーンインスタンスが追加されます。2発目以降は、@Bullet@2、@Bullet@3...となります。
 BulletがBulletSensorの円の中にはいると、コンソールにノード名がprintされます。

スクリーンショット (41).png

 _on_bullet_sensor_body_enteredシグナルで通知されている「body」のはBulletノードインスタンス本体なので、Bulletのメンバーにアクセスしたり、メソッドを呼ぶことができます。

Enemyの位置をBulletに通知する

 Enemyのスクリプトをこのように変更して、Bulletのdiscover_enemyメソッドを呼びEnemyの位置を通知します。

extends CharacterBody3D

func _on_bullet_sensor_body_entered(body):
	body.discover_enemy(transform.origin)

 bullet.gdスクリプトを開いて、discover_enemyメソッドを実装しましょう。
 Enemyの位置を保存するメンバ変数を宣言して、discover_enemyメソッドが呼ばれたときに保存するようにします。

var m_v3_enemy_pos = null
func discover_enemy(enemy_pos):
	m_v3_enemy_pos = enemy_pos

 つづいて、Enemyを見つけるまでは今まで通り直進、Enemnyを見つけた場合はEnemyに向かうように修正します。

extends CharacterBody3D

@export var m_v_dir = Vector3.FORWARD
@export var m_d_speed_mps = 2.0

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()

func discover_enemy(enemy_pos):
	m_v3_enemy_pos = enemy_pos
	
func _on_visible_on_screen_notifier_3d_screen_exited():
	queue_free()

メンバ変数初期値のVector3(0, 0, -1)をVector3.FORWARDに修正

  • @export var m_v_dir = Vector3.FORWARD
     Vector3に定義されている定数に変更しました
     値としてはVector3(0, 0, -1)なので同じです。

func _physics_process(delta):の修正
 進む方向を示すm_v_dir(Vector3)に設定する方向はEnemyを見つけた場合はEmenyの方向を設定するようにしました

  • if m_v3_enemy_pos:
     m_v3_enemy_posが設定されている場合
  • m_v_dir = m_v3_enemy_pos - transform.origin
     目標位置から現在位置を引くと、現在位置から目標位置へ向かう方向になります。
  • else:
     m_v3_enemy_posが設定されていない場合
  • m_v_dir = Vector3.FORWARD
     今まで通り前進(Z軸マイナス方向)します

 実行します。
 BulletがEnemyに近づくと、Enemyに向きを変えて、最終的にくっつくようになりました。
Screenshot 2023_03_01 11_35_12.png

以上

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?