1、概要
Godot Engineは、クロスプラットフォームでオープンソース(MITライセンス)の2D/3Dゲームエンジンです。 本記事では、Godot Engineを使用した3Dオブジェクトの作成、3Dキャラクタの作成及び移動制御方法について説明します。 さらに、3D迷路ゲームを作成してみます。
なお、同様にBabylon.jsを使用した3Dグラフィック及び3Dゲームの作り方を説明したものが「Babylon.jsで3Dアニメーションを含むグラフィックを描画してみる」にありますので、興味がありましたら御参照ください。
2、ファイル構成
本記事で使用するデータ、プログラムは、次のGITHUBからダウンロードできます。
そのファイル構成を以下に示します。
Godot_v3.2-How_to_Create_the_3D_Game: 本記事データ・プログラムのダウンロード(GITHUB)
- 3D_Game-101: 3Dオブジェクトの作成、3Dキャラクタの作成及び移動制御用フォルダー
- 3D_Game-102: 3D迷路ゲーム用フォルダー
- LICENSE: ライセンスの説明
- README.md: 説明ファイル
3、3Dゲームの作り方
ここでは、次の画面の様に地面とブロックを作成し、3Dキャラクタを動かす方法を説明します。
Step-0: 準備
「Godot Engine」をインストールしていない方は、「ダウンロード・ページ」からダウンロードし、インストールしてください。 また、本記事で使用するデータ等を上記GITHUBからダウンロードし、解凍してください。
Step-1: 新規プロジェクトの作成
「Godot Engine」を立ち上げると、次の様な画面が表示されます。(初めて立ち上げた場合には、左側のプロジェクトは何も表示されません。)
ここで、「新規プロジェクト」をクリックしてください。
「新規プロジェクトを作成」のダイアログが表示されますので、「参照」をクリックして作成するゲームを保存するフォルダを指定してください。
「プロジェクト名」の欄に適当な名称(ここでは「3D_Game-001」としました)を入力し「フォルダを作成」をクリックしてください。
「プロジェクトパス」の欄にフォルダ名が表示されますので、「作成して編集」をクリックしてください。
Step-2: 「assets」等のコピー
上記Step-0でダウンロードし解凍したファイルで「3D_Game-101」のフォルダー(下の画面で左側)からStep-1「新規プロジェクトの作成」で作成した「3D_Game-101」のフォルダー(下の画面で右側)に「assets」と「JoyStick.gd」をコピーします。 もちろん、「assets」内の各テクスチャー画像やGLB/GLTFアニメーション・ファイルは、自作したもの等に置き換えても構いませんし、「JoyStick.gd」については、下記Referenceに記載したURL等からダウンロードすることもできます。(Note: 「assets」は、Godot画面の左下「res://」上にフォルダーごとドラッグすることでもコピーすることができます。)
Step-3: 3Dオブジェクトの作成
3Dゲームの場合、3Dオブジェクトの他に照明やカメラ(視点)が必要になります。 今回の場合には、3Dキャラクタの移動を制御するためのJoyStickも必要ですので、これらの設置方法を順番に説明します。
Step-3-1: カメラと照明の設置
「3Dシーン」をクリックし「Spatialシーン」を追加します。
「Spatial」を選択し、右クリック後、表示されたメニューから「子ノードを追加」を選択します。
「Camera」を選択し「作成」をクリックすることでカメラが追加されます。
同様に「Spatialシーン」を右クリックし「子ノードを追加」、「DirectionalLight」を選択し「作成」をクリックして照明を追加してください。
Step-3-2: 床用ブロックの設置
次に床用ブロックを作成します。 上記と同様に「Spatial」を右クリックし「子ノードを追加」、「StaticBody」を選択し「作成」をクリックして3Dオブジェクトを追加します。 (Note: StaticBodyは、移動を考慮しない単純なオブジェクトです。)
「StaticBody」を右クリックし「子ノードを追加」、「MeshInstance」を選択し「作成」をクリックしてオブジェクトの形状を追加します。
「StaticBody」を再度右クリックし「子ノードを追加」、「CollisionShape」を選択し「作成」をクリックしてオブジェクトの衝突判定範囲を追加します。
左側で「StaticBody」の「MeshInstance」を選択します。 右側の「Mesh」で[空]の右、下向きマークをクリックし「新規CubeMesh」を選択します。
右側の「Material」で[空]の右、下向きマークをクリックし「新規SpatialMaterial」を選択します。
右側の「Albedo」をクリックし、「Texture」で[空]の右、下向きマークをクリックし「読込み」を選択します。
「ファイルを開く」のダイアログで「assets」をダブルクリックします。
「FloorsRegular0043_L.png」を選択し「開く」をクリックします。
白いブロックにテクスチャ画像が張り付きました。 次に右側の「Uv1」をクリックします。
「Uv1」の「Scale」を x 3 y 2 z 1 に設定します。
ちなみに「Uv1」の「Scale」を x 1 y 1 z 1 に設定した場合、次の画像ではAからFが六面それぞれに設定されます。
左側の「CollisionShape」を選択します。 右側で「Shape」の[空]の右、下向きマークをクリックし「新規BoxShape」を選択します。
「BoxShape」をクリックし「Extents」の値を変更することで、衝突判定範囲を変更・設定できます。
左側の「StaticBody」を選択し、右側の「Transform」をクリック「Translation」の値を入力して、これから作成するオブジェクトの邪魔にならない位置に移動させます。
Step-3-3: 壁用ブロックの設置
壁用ブロックを設置します。 床用ブロックと同様に「StaticBody」、「MeshInstance」及び「CollisionShape」を作成します。 壁用テクスチャー画像として「backdrop-21534_1920.png」を選択します。
左側の「StaticBody2」を選択し、右側の「Transform」をクリック「Translation」の値を入力して、床用ブロックと同様に邪魔にならない位置に移動させます。
Step-3-4: 3Dキャラクタ(プレーヤー)の設置
次は、3Dキャラクタ(プレーヤー)の設置です。 上記と同様に「KinematicBody」、「MeshInstance」及び「CollisionShape」を作成します。(Note: KinematicBodyは、ユーザがコントロール可能な3Dオブジェクトです。)
左下「assets」内から「Boy_002_Walk.glb」と「Boy_003_Idsle.glb」を左上の「KinematicBody」上にドラッグします。(ここで使用する3Dキャラクタは、Blenderで作成し、GLTF/glb形式で保存したもので、原点を足元にしてあります。)
左側で「KinematicBody」の「MeshInstance」を選択します。 右側の「Mesh」で[空]の右、下向きマークをクリックし「新規CylinderMesh」を選択します。
右側で「Top Radius」、「Bottom Radius」及び「Height」それぞれの値をキャラクタサイズに合わせて入力します。
左側で「CollisionShape」を選択し、右側「Shape」の[空]の右、下向きマークをクリックし「新規CylinderShape」を選択します。
右側「Shape」の「Cylinder」を選択し、「Radius」と「Height」の値を設定します。 また、「Transform」の「Translation」の値を変更し、キャラクタ位置に合わせます。
左側の「MeshInstance」を選択し、右側の「Transform」の「Translation」の値を「CollisionShape」と同様に変更します。
左側の「KinematicBody」を選択し「MashInstance」横のアイマークをクリックして「MashInstance」を表示しない設定にします。 さらに、「KinematicBody」を再度選択し、右側の「Transform」の「Translation」の値を邪魔にならない位置に変更します。
Step-3-5: Virtual Joystickの設置
最後に画面上にJoyStickを表示させます。 左側で「Spatial」を選択し右クリックして「子ノードを追加」、「Node2D」を選択し「作成」をクリックして2Dオブジェクトを追加します。
作成した「Node2D」を右クリックして「Sprite」を2個作成します。(「Sprite」は「Node2D」の配下にあります。)
「Node2D」をダブルクリックして名前を「JoyStick」に変更します。(「Node2D」を右クリックし、名前の変更を選択しても良い)
同様に「Sprite」を「BigCircle」に、「Sprite2」を「SmallCircle」に名前を変更します。 これら名前の変更は、JoyStick用スクリプトにオブジェクトを合わせるためです。
左側で「BigCircle」を選択し、右側の「Texture」で[空]の右、下向きマークをクリックし「読込み」を選択します。 「ファイルを開く」のダイアログ・ボックスで「big_circle_N.png」を選択し。「開く」をクリックします。
上記と同様に「SmallCircle」のテクスチャーとして「orange_ball_01.png」を選択してください。
左側で「JoyStick」を選択し、右側「Transform」の「Position」の値を設定し、画面上でJoyStickが使いやすい場所に移動させてください。
Step-4: 床用と壁用ブロックをシーンとして保存
左側で「StaticBody」を右クリックし「ブランチをシーンとして保存」を選択してください。
次のダイアログボックスが表示されますので、デフォルトのまま「保存」をクリックします。 また、「StaticBody2」についても同様に保存してください。 これは、スクリプト内での再利用を容易にするためです。
Step-5: Virtual JoyStickにスクリプトをアッタッチ
JoyStickにその動作を記述するスクリプトをアタッチします。 左側で「JoyStick」を右クリックし「スクリプトをアタッチ」を選択します。
次のダイアログボックスが表示されますので、「res://JoyStick.gd」を選択し「読込み」をクリックします。
Step-6: Spatialにスクリプトをアッタッチ
次に「Spatial」に床や壁、3Dキャラクタを表示させるスクリプトをアッタッチします。 「Spatial」を右クリックし「スクリプトをアタッチ」を選択します。
今回は、対応するスクリプトがありませんので、デフォルトのまま「作成」をクリックします。
次のような画面が表示されますので、ここにスクリプトを追加します。
スクリプトの内容は次の通りです。 なお、Step-0でダウンロードしたファイルから当該スクリプトをコピーしてJoyStickと同様に「スクリプトをアタッチ」しても良いと思います。
extends Spatial
# Declare member variables here. Examples:
var Ground_x = 10 # 10 x 10 のブロック数の床を表示させます。
var Ground_y = 0
var Ground_z = 10
var Block_size = 2 # ブロックのサイズ(StaticBodyのサイズに合わせる)
var Player_x = 0 # プレーヤーの表示位置
var Player_y = 0
var Player_z = 0
# Called when the node enters the scene tree for the first time.
func _ready():
# pass # Replace with function body.
Create_Ground() # 床の表示
Create_Player() # プレーヤの表示
Create_Block() # 壁の表示(今回は1個のみ)
# Called every frame. 'delta' is the elapsed time since the previous frame.
#func _process(delta):
# pass
# Create Ground
func Create_Ground():
var Floor_org = load("res://StaticBody.tscn")
for i in range(Ground_x):
for j in range(Ground_z):
var Floor = Floor_org.instance()
add_child(Floor)
Floor.transform.origin.x = Block_size / 2 + (i - Ground_x / 2) * Block_size
Floor.transform.origin.y = -1
Floor.transform.origin.z = Block_size / 2 + (j - Ground_z / 2) * Block_size
func Create_Player():
var Player = get_node("KinematicBody")
Player.set_scale(Vector3(1.0, 1.0, 1.0))
Player.transform.origin.x = 0
Player.transform.origin.y = 0
Player.transform.origin.z = 0
# Create Block
func Create_Block():
var Block_org = load("res://StaticBody2.tscn")
var Block = Block_org.instance()
add_child(Block)
Block.transform.origin.x = 3 # 壁の表示位置
Block.transform.origin.y = 1
Block.transform.origin.z = -2
Step-7: KinematicBodyにスクリプトをアッタッチ
Step-6と同様に「KinematicBody」にスクリプトをアッタッチします。
「KinematicBody」用スクリプトの内容は次の通りです。 JoyStickの動きと矢印キー入力に応じてプレーヤーが動きます。
extends KinematicBody
const speed = 1 # プレーヤーの移動速度
var Temp
var joystickVector
var player_quat_s
var player_quat_g
var player_quat_t = 0.2
onready var player = get_node(".")
onready var player_body = get_node("Boy_002_Walk")
onready var player_idle = get_node("Boy_003_Idle")
func _ready():
Temp = get_parent().get_node("JoyStick").connect('move', self, '_on_JoystickMove')
player_quat_s = Quat(Vector3(0, 1, 0), atan2(0, 0))
player_body.hide()
player_idle.show()
get_node("Boy_002_Walk/AnimationPlayer").play("Boy_202_Walk")
func _process(delta):
Temp = delta
player_move()
func _on_JoystickMove(vector): # JoyStickの動きに応じたプレーヤーの移動
if abs(vector.x) > abs(vector.y):
if vector.x > 0:
joystickVector = "right"
# player.move_and_slide(Vector3(speed, 0, 0), Vector3()) # 4方向移動
elif vector.x < 0:
joystickVector = "left"
# player.move_and_slide(Vector3(speed * -1, 0, 0), Vector3())
elif abs(vector.x) < abs(vector.y):
if vector.y > 0:
joystickVector = "forward"
# player.move_and_slide(Vector3(0, 0, speed), Vector3())
elif vector.y < 0:
joystickVector = "backward"
# player.move_and_slide(Vector3(0, 0, speed * -1), Vector3())
else:
joystickVector = "stay"
if vector.length() > 0:
var force = Vector3(vector.x, 0, vector.y).normalized() * speed
player.move_and_slide(force, Vector3()) # 8方向移動
player_idle.hide()
player_body.show()
get_node("Boy_002_Walk/AnimationPlayer").play("Boy_202_Walk")
player_quat_g = Quat(Vector3(0, 1, 0), atan2(vector.x, vector.y))
player_quat_s = player_quat_s.slerp(player_quat_g, player_quat_t)
player_body.transform = Transform(player_quat_s)
player_idle.transform = Transform(player_quat_s)
else:
player_body.hide()
player_idle.show()
get_node("Boy_003_Idle/AnimationPlayer").play("Boy_203a_Idle")
func player_move(): # 矢印キー入力によるプレーヤー移動
var direction = Vector3()
if Input.is_action_pressed("ui_right"):
direction.x += 1
if Input.is_action_pressed("ui_left"):
direction.x -= 1
if Input.is_action_pressed("ui_down"):
direction.z += 1
if Input.is_action_pressed("ui_up"):
direction.z -= 1
if direction.length() > 0:
var force = direction.normalized() * speed
player_quat_g = Quat(Vector3(0, 1, 0), atan2(direction.x, direction.z))
player_quat_s = player_quat_s.slerp(player_quat_g, player_quat_t)
player_body.transform = Transform(player_quat_s)
player_idle.transform = Transform(player_quat_s)
Temp = player.move_and_slide(force, Vector3(0, 0, 0))
player_idle.hide()
player_body.show()
get_node("Boy_002_Walk/AnimationPlayer").play("Boy_202_Walk")
else:
player_body.hide()
player_idle.show()
get_node("Boy_003_Idle/AnimationPlayer").play("Boy_203a_Idle")
スクリプトを全て入力すると次のようになります。 ここで、左上の「シーン」を右クリックします。
左下の「Spatial.tscn」を右クリックし「メインシーンとして設定」を選択します。
Step-8: カメラと照明の位置調整
左側の「Camera」を選択し、右側の「Transform」から「Translation」及び「Rotation Degrees」の値を設定し、カメラの位置と向きを設定します。
ここで、右上のマークをクリックしてみて下さい。 作成した画面が表示され、プレーヤの移動制御ができます。 プレーヤーを壁ブロックに近づけてみて下さい、壁にぶつかると止まると思います。 これで、CollisionShapeの設定が有効であることを確認できます。 しかしながら、若干画面が暗いと思います。
次に左側の「DirectionLight」を選択し、右側の「Transform」から「Translation」及び「Rotation Degrees」の値を設定し、ライトの位置と向きを設定します。 また、「Shadow」、「Enabled」の「オン」をマークします。
再度、右上のマークをクリックしてライトの変化と影が付くのを確認してください。
Step-9: 追随するカメラに変更
上記までは、固定カメラでの画像でした。 ここで、左側の「Camera」を「KinematicBody」上にドラッグしてみてください。
移動した「Camera」を選択し、右側「Transform」の「Translation」の値を変更してください。
Step-10: プレーヤーのアニメーションについて
上記、「KinematicBody.gd」でアニメーション表示を指定するのは、次の記述です。
get_node("Boy_002_Walk/AnimationPlayer").play("Boy_202_Walk")
"Boy_202_Walk"の出目は、次の通りです。
「KinematicBody」の「Boy_002_Walk」右のマークをダブルクリックし、「新規の継承」をクリックする。
新たな画面で「AnimationPlayer」の「Boy_202_Walk」が歩くアニメーション本体です。
同様に「Boy_003_Idle」右のマークをダブルクリックし、「新規の継承」をクリックし、「AnimationPlayer」の「Boy_203a_Idle」がアイドル状態のアニメーションです。
4、迷路ゲーム
Step-0でダウンロードしたファイルから迷路ゲーム(3D_Game-102)をインポートしてみましょう。 Godot Engineを立ち上げて、「インポート」を選択して下さい。
「ファイルを開く」のダイアログボックスで、当該フォルダー(3D_Game-102)を指定し「project.godot」を選択後「開く」をクリックしてください。
編集画面が表示されますので、色々変更してみて下さい。 メニュー画面を追加した他は、3Dオブジェクトの設置やプレーヤの移動等は、上記3項とほとんど同一です。
5、おまけ
同様な3Dゲームとして、「簡単に外見を変更できるキューブ・キャラクタの作り方」と「キューブ・キャラクタによる倉庫番タイプのゲーム」がありますので、興味がありましたらクリックしてみで下さい。 キューブキャラクタのデモ画面を次に示します。
6、Reference
- Godot Engine: クロスプラットフォームかつオープンソース(MITライセンス)の2D/3Dゲームエンジン
- Godot_v3.2-How_to_Create_the_3D_Game: 本記事で使用したプログラム及びデータ
- Godot_v3.2-Cube_Characters: 簡単に外見を変更できるキューブ・キャラクタの作り方
- Godot_v3.2-Sokoban_Cube: キューブ・キャラクタによる倉庫番タイプのゲーム
- Godot_v3.2-Basic_Shaders: 各種シェーダーの作り方
- Godot-Virtual-Joystick: 本記事で使用したJoyStick