0
0

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.

GogotのArrayMeshで落とし穴を作る

Posted at

目的

 Godot4.0のArrayMeshを作って、落とし穴を作ります。

 前々回の下記記事でArrayMeshからCollisionShape3Dを設定する方法を書きましたが、本当に動的に作成したCollisionShape3Dが使えるのか確認したいと思います。

やりたいこと

 地面にカーソルで操作できる穴を作ります。
 地面の上にはRigidBody3Dの球や長方形を置いておいて、穴が下を通ると落ちることを確認します。
 落ちる.png

新規プロジェクト作成してノードを追加する

 Godotで新規プロジェクトを作成して、下記のようにノードを構成します。
 またHoleOnGroundを右クリックして「スクリプトをアタッチ」してください。
スクリーンショット (662).png

 obj1~obj4は球を配置、obj5のみ長方形を配置しています。

ノード名:ノードクラス名 インスペクタ変更値
World : Node3D
 ┝━Camera3D Position : (0m, 4m, 7m) Rotation : (-45°, 0°, 0°)
 ┝━DirectionalLight3D Position : (-10m, 0m, 0m) Rotation : (-10°, 45°, 0°) shadow:オン
 ┝━HoleOnGround : StaticBody3D
 ┃  ┝━MeshInstance3D  設定不要(GDScriptから設定する)
 ┃  ┗━CollisionShape3D 設定不要(GDScriptから設定する)
 ┗━Objects : Node3D
┝━obj1 : RigidBody3D Position : (-2m, 4m, 0m)
┃  ┝━MeshInstance3D 新規SphereMesh3D Radius=0.2m, Height:0.4m
┃  ┗━CollisionShape3D 新規SphereShape3D Radius=0.2m
┝━obj2 : RigidBody3D Position : (0m, 4m, 3m)
┃  ┝━MeshInstance3D 同上
┃  ┗━CollisionShape3D 同上
┝━obj3 : RigidBody3D Position : (0m, 4m, -2m)
┃  ┝━MeshInstance3D 同上
┃  ┗━CollisionShape3D 同上
┝━obj4 : RigidBody3D Position : (2m, 4m, 0m)
┃  ┝━MeshInstance3D 同上
┃  ┗━CollisionShape3D 同上
┗━obj5 : RigidBody3D Position : (-2m, 4m, -2m)
  ┝━MeshInstance3D 新規BoxMesh3D x:0.5m, y:1m, z:0.5m
  ┗━CollisionShape3D 新規BoxShape3D x:0.5m, y:1m, z:0.5m

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

GDScriptを作成する

下記のように実装しました。

extends StaticBody3D

#
# カーソルキー操作でhole(落とし穴)を動かす
#
const SPEED = 3.0

# 地面の大きさ
var m_d_ground_w_m : float = 20.0
var m_d_ground_d_m : float = 20.0

# 落とし穴
var m_v3_hole_pos = Vector3(0, 0, 0)
var m_d_hole_x_m : float = 1.0
var m_d_hole_z_m : float = 1.0

func _physics_process(delta):
	var velocity = Vector3.ZERO
	var input_dir = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
	var direction = (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
	if direction:
		velocity.x = direction.x
		velocity.z = direction.z

	# holeの位置を移動
	m_v3_hole_pos += velocity.normalized() * SPEED * delta
	set_hole_and_ground_array_mesh()
	
# 三角形2つで四角形を作る
# l:Left, t:Top, r:Right, b:bottom
func add_square(vertices, normals, x_l, z_t, x_r, z_b):
	vertices.push_back(Vector3(x_l, 0, z_t))
	vertices.push_back(Vector3(x_r, 0, z_t))
	vertices.push_back(Vector3(x_r, 0, z_b))
	normals.push_back(Vector3.UP)
	normals.push_back(Vector3.UP)
	normals.push_back(Vector3.UP)

	vertices.push_back(Vector3(x_r, 0, z_b))
	vertices.push_back(Vector3(x_l, 0, z_b))
	vertices.push_back(Vector3(x_l, 0, z_t))
	normals.push_back(Vector3.UP)
	normals.push_back(Vector3.UP)
	normals.push_back(Vector3.UP)
	

# 落とし穴ArrayMeshを作成する
func set_hole_and_ground_array_mesh():

	var vertices = PackedVector3Array()
	var normals  = PackedVector3Array()
	# 地面のXZ平面上の左上、右下の位置を設定
	var ground_LT = Vector3(-m_d_ground_w_m/2.0 , 0, -m_d_ground_d_m/2.0)
	var ground_RB = Vector3( m_d_ground_w_m/2.0 , 0,  m_d_ground_d_m/2.0)
	# holeのXZ平面上の左上、右下の位置を設定
	var hole_size = Vector3( m_d_hole_x_m , 0,  m_d_hole_z_m)
	var hole_LT = m_v3_hole_pos - hole_size/2.0
	var hole_RB = m_v3_hole_pos + hole_size/2.0

	# 3×3グリッドの上3ブロック
	add_square(vertices, normals, ground_LT.x, ground_LT.z ,ground_RB.x, hole_LT.z)
	# 3×3グリッド、左(holeの左)
	add_square(vertices, normals, ground_LT.x, hole_LT.z ,hole_LT.x, hole_RB.z)
	# 3×3グリッド、右(holeの右)
	add_square(vertices, normals, hole_RB.x, hole_LT.z ,ground_RB.x, hole_RB.z)
	# 3×3グリッドの下3ブロック
	add_square(vertices, normals, ground_LT.x, hole_RB.z ,ground_RB.x, ground_RB.z)
	
	var arrays = Array()
	arrays.resize(ArrayMesh.ARRAY_MAX)
	arrays[ArrayMesh.ARRAY_VERTEX] = vertices
	arrays[ArrayMesh.ARRAY_NORMAL] = normals

	var tmpMesh = ArrayMesh.new()
	tmpMesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
	$MeshInstance3D.mesh = tmpMesh
	
	# MeshからCollision形状(ConcavePolygonShape3D)生成
	$CollisionShape3D.shape = tmpMesh.create_trimesh_shape()

実行します。

 4つの球の真ん中にあるのが、落とし穴です。カーソルキーで動かすことができます。
スクリーンショット (665).png

 地面と落とし穴の色が似ていて見にくいですが、穴が下に来ると球や長方形が落ちます。
 RigitBody3Dが物理エンジンで制御されるので、リアルに落ちていきます。
 スクリーンショット (674).png

スクリプト説明

 スクリプトを説明します。

extends StaticBody3D

#
# カーソルキー操作でhole(落とし穴)を動かす
#
const SPEED = 3.0

SPEEDはカーソルキーによる落とし穴の移動速度を定義しています。

# 地面の大きさ
var m_d_ground_w_m : float = 20.0
var m_d_ground_d_m : float = 20.0

 地面は原点を中心に配置するので、縦横±10mの正方形になります。

# 落とし穴
var m_v3_hole_pos = Vector3(0, 0, 0)
var m_d_hole_x_m : float = 1.0
var m_d_hole_z_m : float = 1.0

 落とし穴をし初期位置と大きさを定義します。m_v3_hole_posは落とし穴の正方形の中心です。

func _physics_process(delta):
	var velocity = Vector3.ZERO
	var input_dir = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
	var direction = (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
	if direction:
		velocity.x = direction.x
		velocity.z = direction.z

	# holeの位置を移動
	m_v3_hole_pos += velocity.normalized() * SPEED * delta
	set_hole_and_ground_array_mesh()

 CharacterBody3DのGDScriptテンプレートからカーソルキー上下左右の処理を流用しました。
 最後にm_v3_hole_posにx軸とz軸の移動量を足して、set_hole_and_ground_array_mesh()を呼び出して落とし穴メッシュを作り直しています。

# 三角形2つで四角形を作る
# l:Left, t:Top, r:Right, b:bottom
func add_square(vertices, normals, x_l, z_t, x_r, z_b):
	vertices.push_back(Vector3(x_l, 0, z_t))
	vertices.push_back(Vector3(x_r, 0, z_t))
	vertices.push_back(Vector3(x_r, 0, z_b))
	normals.push_back(Vector3.UP)
	normals.push_back(Vector3.UP)
	normals.push_back(Vector3.UP)

	vertices.push_back(Vector3(x_r, 0, z_b))
	vertices.push_back(Vector3(x_l, 0, z_b))
	vertices.push_back(Vector3(x_l, 0, z_t))
	normals.push_back(Vector3.UP)
	normals.push_back(Vector3.UP)
	normals.push_back(Vector3.UP)

 4角形を作るメソッドです。
 引数に四角形の左上(x,z)=(x_l,z_y)と右下(x,z)=(x_r, z_b)を指定すると、三角形2つ作って、長方形を作成します。
 時計回りに頂点をpush_backします。
 また今回生成する平面の向きは上(Y軸プラス方向)なので法線はVector3.UPを指定します。

# 落とし穴ArrayMeshを作成する
func set_hole_and_ground_array_mesh():

 ArrayMeshとCollisionShape3Dを作成するメソッドです。


	var vertices = PackedVector3Array()
	var normals  = PackedVector3Array()

 PackedVector3Arrayを作ります。

	# 地面のXZ平面上の左上、右下の位置を設定
	var ground_LT = Vector3(-m_d_ground_w_m/2.0 , 0, -m_d_ground_d_m/2.0)
	var ground_RB = Vector3( m_d_ground_w_m/2.0 , 0,  m_d_ground_d_m/2.0)

	# holeのXZ平面上の左上、右下の位置を設定
	var hole_size = Vector3( m_d_hole_x_m , 0,  m_d_hole_z_m)
	var hole_LT = m_v3_hole_pos - hole_size/2.0
	var hole_RB = m_v3_hole_pos + hole_size/2.0

 地面と落とし穴の左上、右下の座標を求めています。下図が参考になると思います。
(本当はZ軸は手前を向いているので矢印が逆ですが、今回のケースでは大丈夫だと思います)
スクリーンショット (682).png


	# 3×3グリッドの上3ブロック
	add_square(vertices, normals, ground_LT.x, ground_LT.z ,ground_RB.x, hole_LT.z)
	# 3×3グリッド、左(holeの左)
	add_square(vertices, normals, ground_LT.x, hole_LT.z ,hole_LT.x, hole_RB.z)
	# 3×3グリッド、右(holeの右)
	add_square(vertices, normals, hole_RB.x, hole_LT.z ,ground_RB.x, hole_RB.z)
	# 3×3グリッドの下3ブロック
	add_square(vertices, normals, ground_LT.x, hole_RB.z ,ground_RB.x, ground_RB.z)

 下図のように3×3ブロックに分けて、左の図のように、一番上の3ブロック、下の3ブロックと右の図のように中央(落とし穴)の左、中央の右と、全部で4つの長方形に分けて、四角形のメッシュを作成します。
スクリーンショット (678).png

	var arrays = Array()
	arrays.resize(ArrayMesh.ARRAY_MAX)
	arrays[ArrayMesh.ARRAY_VERTEX] = vertices
	arrays[ArrayMesh.ARRAY_NORMAL] = normals

	var tmpMesh = ArrayMesh.new()
	tmpMesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
	$MeshInstance3D.mesh = tmpMesh
	

 ArrayMeshを生成して、MeshInstance3Dに設定します。

	# MeshからCollision形状(ConcavePolygonShape3D)生成
	$CollisionShape3D.shape = tmpMesh.create_trimesh_shape()

 ArrayMeshからCollision形状(ConcavePolygonShape3D)を生成してCollisionShape3Dに設定しています。

以上

0
0
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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?