今回は、Godotの解説動画等でお馴染みのGDQuestがGithubにて公開している3DモデルSophiaちゃんを使って、動かしてみたいと思います。
UnityにおけるUnityちゃんみたいな位置づけのキャラクターなんですかね?3Dプラットフォーマーのデフォルトキャラっぽいのでこちらを使ってみたいと思います。
【Godot】3DキャラクターSophiaちゃんを動かしてみる
※Godotのインストール等は既に済んでいる前提で進めていきます。
Sophiaのキャラクターデータをダウンロード・インポートする
まずは、SophiaのキャラクターデータをGithubからダウンロードしましょう。
緑のCodeボタンからZipファイルをダウンロードします。
Godotのファイルシステムの中にダウンロードしたファイルから「addons」をドラッグ&ドロップしてインポートします。
キャラクターデータ毎にフォルダ分けされていますが、今回使用したいのはSophiaちゃんのデータなので「gdquest_sophia」→「sophia_skin.tscn」シーンを開いてみましょう。
すると、こんな感じで待機状態のSophiaちゃんが出てくると思います。
予めスクリプトやアニメーション等が設定されているので、すぐに使えて便利ですね。
今回は、このシーンを継承して使っていきたいと思いますので、ファイルシステム内にあるシーンを右クリックして「新しい継承シーン」をクリックします。
続いて、Ctrl+Sで名前を付けて継承したシーンを適当な名前で保存しておきましょう。私は「Sophia_test.tscn」という名前で保存しておきます。
ステージを作成する
今回は、キャラクターをキー入力で動かせるようにしたいので、ステージを作る必要があります。
というわけで、ステージ用のシーンを作成しましょう。
「シーン」→「新規シーン」よりNode3Dを作ります。名前はStage等分かりやすい名前にしておくと良いでしょう。
まずは、この子ノードとして「StaticBody3D」ノードを作成します。動きがないようなオブジェクトにはこちらのノードが便利です。
Stageノードを右クリックして子ノード追加より「StaticBody3D」ノードを作成します。
作成すると分かりますが、画面上には何も表示されないので見た目の部分となるメッシュも作ります。
StaticBody3Dの子ノードとして「MeshInstance3D」を作成します。
※画像ではStageの子ノードのように見えますが、StaticBody3Dの子ノードです。
作成した時点では相変わらず何も表示されないと思いますが、右のインスペクターからMeshをBoxMeshに変更すると、画像のようにBoxが表示されるかと思います。
ここは、床としてふさわしいと思われるBoxMeshを選択しましたが他にも様々なMeshが用意されているとので色々と試してみてください。
インスペクターからMeshの画像をクリックすると、サイズの調整等を行うことが出来ます。
私もステージっぽくなるように、XとZの値をそれぞれ20mに変更しています。
大体ステージが出来たら保存して、Sophiaのシーンに戻りましょう。
プレイヤースクリプトの作成とInputMapの設定
Godotの3Dでキャラクターを動かすときは、CharacterBody3Dというノードを使います。このノードは、物理演算を適用できるキャラクター用のノードです。
例として
- 壁にぶつかったら止まる
- 重力で落下する
- 床に着いているかどうか判定できる
このような処理を実装したい際にはこちらのノードを活用すると良いでしょう。
継承してきたシーンのSophiaSkinの型をCharacter3Dに変更したいので、SophiaSkinノードを右クリックして「継承のクリア」を選択してクリアします。
続いて、SophiaSkinを右クリックして「型の変更」をクリックします。検索窓に「CharacterBody3D」と入力して変更します。
このままだと当たり判定がないので、当たり判定用のノードである「CollisionShape3D」ノードもSophiaSkinの子ノードとして作成します。
Meshを作成した時と同様に、様々なタイプの当たり判定を実装することが出来ます。
プレイヤー等の場合には「CapsuleShape3D」を使う事が多いんじゃないでしょうか。
こちらを設定したらキャラクターのサイズに合うように調整してください。調整が難しい場合には画面内左上にある「透視投影」から「前面図」にすると調整しやすくなるかと思います。
スクリプトの作成
ここからスクリプトを作成しますが、デフォルトでは既にスクリプトがアタッチされているので、SophiaSkinを右クリックして「スクリプトをデタッチ」して外しておきます。
新しいスクリプトをアタッチするので同様に右クリックして「スクリプトをアタッチ」選択してデフォルトのままで良いのでそのまま作成をクリックします。
extends CharacterBody3D
@export var speed = 4.0 #キャラクターのスピード
@export var jump_force = 6.0 #ジャンプ力
@onready var camera = $Camera3D #キャラクターの中にあるCamera3Dを取得
var gravity = ProjectSettings.get_setting("physics/3d/default_gravity") #Godotの設定にある「重力」の値を取得
func _physics_process(delta):
# 重力処理
if not is_on_floor(): #キャラが床にいないなら重力で下に落とす
velocity.y -= gravity * delta
# 移動処理 プレイヤーの操作を受け取って動かす
var input = Vector3.ZERO
if Input.is_action_pressed("move_forward"):
input.z -= 1
if Input.is_action_pressed("move_backward"):
input.z += 1
if Input.is_action_pressed("move_left"):
input.x -= 1
if Input.is_action_pressed("move_right"):
input.x += 1
input = input.normalized() # 入力の正規化
velocity.x = input.x * speed
velocity.z = input.z * speed
# ジャンプ処理
if Input.is_action_just_pressed("jump") and is_on_floor():
velocity.y = jump_force
move_and_slide() #キャラをスムーズに動かす関数
CharacterBody3Dを操作するための内容なのでCharacterBody3Dノードをextendsで継承します。
キャラクターのスピードやジャンプ力はエディタで数値を変更出来るようにするために、「@export」を付けています。
後ほどInputMapにてキーの設定を行いますが、ASWDキーでキャラクターの移動とスペースキーにてジャンプをさせるつもりです。
移動に関しては、入力の正規化を行い速度を安定させジャンプは「is_on_floor()」を使ってキャラクターが地面に着地している場合にのみジャンプさせるようにします。
move_and_slide()は、障害物に当たったり、床に沿って移動できる便利な関数です。
ゲームにキーの設定を行う
ここから、それぞれのアクションに対応するキーの設定を行います。
まずは、メニューバー「プロジェクト」→「プロジェクト設定…」を開きます。
インプットマップをクリックします。
新しいアクションの追加から
- move_forward → Wキー
- move_backward → Sキー
- move_left → Aキー
- move_right → Dキー
- jump → Spaceキー
を作成します。
手順としては新しいアクションの追加の欄に「move_forward」と入力して追加。右の「+」ボタンをクリックして対応させたいキーを入力します。それぞれのキーに対して同様の手順を行います。
設定が出来たらウィンドウを閉じます。
今更ですが、GodotではZのマイナス方向が前になるので、sophiaを選択して「Rotation」のYの値を-180に変えて前を向くようにしておきます。
カメラの設定
続いて、カメラの設定を行います。
SophiaSkinの子ノードとして「Camera3D」を追加します。
カメラをプレイヤーの後ろに調整します。この辺は後でゲームを実行しながら調整すると良いと思います。
ステージシーンにSophiaを配置する
Sophiaのシーンをステージシーン上に配置します。
ステージシーンに移動して、Stageの子シーンとしてファイルシステムからSophiaのシーンをドラッグ&ドロップします。
このままゲームを実行しても、Sophiaは床をすり抜けて落下してしまうので、床に対して当たり判定の設定を行います。
StaticBody3Dの子ノードに「CollisionShape3D」を作ってインスペクターからShapeをBoxShape3Dに変更します。
Sizeを見た目のMeshに合わせてXとZをそれぞれ20.0mにしておきましょう。
ライティングの設定
このままだと何も光がないのでゲームを実行しても真っ暗な状態です。
編集画面のメニューバーにある縦に3つのマルがあるアイコンをクリックして、空の色や地面の色をお好みにいじってください。出来たら環境をシーンに追加します。
同じように太陽もシーンに追加してあげましょう。
これで、ライティングの設定が出来るのでゲームを実行してみると、こんな感じでステージの上をキー入力でキャラクターの移動が出来るんじゃないかと思います。
移動に合わせてアニメーションさせよう
続いて、キャラクターにアニメーションがないのは寂しいので、移動中には移動用のアニメーション、ジャンプ中にはジャンプ用のアニメーションを設定したいと思います。
という訳で、スクリプトを追加していきます。
extends CharacterBody3D
@export var speed = 4.0
@export var jump_force = 6.0
@onready var camera = $Camera3D
@onready var animation_tree = %AnimationTree
@onready var state_machine : AnimationNodeStateMachinePlayback = animation_tree.get("parameters/StateMachine/playback")
var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")
func _physics_process(delta):
# 重力処理
if not is_on_floor():
velocity.y -= gravity * delta
# 移動処理
var input = Vector3.ZERO
if Input.is_action_pressed("move_forward"):
input.z -= 1
if Input.is_action_pressed("move_backward"):
input.z += 1
if Input.is_action_pressed("move_left"):
input.x -= 1
if Input.is_action_pressed("move_right"):
input.x += 1
input = input.normalized()
velocity.x = input.x * speed
velocity.z = input.z * speed
# ジャンプ処理
if Input.is_action_just_pressed("jump") and is_on_floor():
velocity.y = jump_force
# アニメーションの切り替え
if is_on_floor():
if input.length() > 0:
move() # 移動中
else:
idle() # 待機中
else:
if velocity.y > 0:
jump() # ジャンプ中
else:
fall() # 落下中
move_and_slide()
func idle():
state_machine.travel("Idle")
func move():
state_machine.travel("Move")
func fall():
state_machine.travel("Fall")
func jump():
state_machine.travel("Jump")
func edge_grab():
state_machine.travel("EdgeGrab")
func wall_slide():
state_machine.travel("WallSlide")
元々用意されている「sophia_skin.gd」のコードを見ると分かりますが、アニメーションを切り替える関数があるのでそれをコピーしています。
ステートマシンに関する定義も必要になるので、それらもコピーして貼り付けます。
アニメーションの切り替えに関しては、まずキャラクターが床に乗っているかチェックして、乗っていたらInput.Lengthで移動中か待機中かをチェックします。
床に乗っていないなら、空中にいるのでジャンプや落下中のアニメーションを適用するようにします。
ここまで出来たら、ステージシーンにてゲームを実行してみます。
上手くいっていればこのように移動やジャンプに合わせてアニメーションが再生されると思います。