MENU

Godot 4 でWiz系ダンジョンRPG開発 (プレイヤーの移動) [002]

この記事では Godot 4 でダンジョンRPGのプレイヤーを移動させるための設定・コードについてお伝えします。この記事までを完成させることで、上記画像のようなダンジョンが実装可能です。(上記画像をクリックすると動きがわかります)

前回はダンジョンを作りました。その内容は以下のリンクからご参照ください。

この記事ではゲームパッドを使います。PCに接続してください。

目次

プレイヤーの移動

このステップでは、迷宮の「セルの中心」にプレイヤーのカメラを配置し、キーボード入力(W,A,S,Dなど)に応じて1マス (2m) ずつの移動と、90度ごとの旋回を行う仕組みを構築します。

プレイヤー (Camera3D) の配置と設定

Main シーンを開き、Main ルートノードを右クリックし Add Child Node から Camera3D を追加してください。

Camera3D の Inspector を開き、Transform の Position の値を以下の通りに変更します。Rotation が 0 であることも確認してください。

PositionX: 1.0, Y:1.0, Z:1.0
RotationX: 0.0, Y:0.0, Z:0.0

GDScript の作成と割り当て

画面左上のツリーで Camera3D ノードを右クリックし Attach Script を選択してください。設定ウィンドウで以下の項目になっていることを確認し、作成をクリックします。

言語GDScript
パスres://camera_3d.gd
※ デフォルト値
extends Camera3D

# Wiz系RPGの移動・旋回に関する定数定義
const MOVE_STEP: float = 2.0    # GridMapのセルサイズに合わせた1マスの移動距離
const ROTATE_STEP: float = 90.0 # 1回の旋回角度(度数法)

func _ready() -> void:
	# 現時点では初期化処理は不要なためパス
	pass

func _unhandled_input(event: InputEvent) -> void:
	# キーボードのキーやボタンが「押された瞬間」だけを検知する
	# 押しっぱなしによる連続移動(暴走)や、キーを離した瞬間のイベントを弾く
	if not event.is_pressed() or event.is_echo():
		return

	# プロジェクト設定の「インプットマップ」で自作したアクション名と紐付け
	if event.is_action_pressed("move_forward"):
		move_forward()
		
	elif event.is_action_pressed("move_backward"):
		move_backward()
		
	elif event.is_action_pressed("turn_left"):
		rotate_horizontal(ROTATE_STEP) # 左回転(プラス方向)
		
	elif event.is_action_pressed("turn_right"):
		rotate_horizontal(-ROTATE_STEP) # 右回転(マイナス方向)

# 1マス前進する関数
func move_forward() -> void:
	# カメラが現在向いている正面方向(ローカルの-Z方向)のベクトルを取得
	var forward_vector: Vector3 = -global_transform.basis.z
	
	# ピッチ(上下の首振り)によるY軸のブレを排除し、完全な水平ベクトルにする
	forward_vector.y = 0
	forward_vector = forward_vector.normalized()
	
	# 現在の座標に「向き × 1マスの距離」を加算して移動
	global_position += forward_vector * MOVE_STEP

# 1マス後退する関数
func move_backward() -> void:
	var forward_vector: Vector3 = -global_transform.basis.z
	forward_vector.y = 0
	forward_vector = forward_vector.normalized()
	
	# 前進とは逆に、現在の座標から「向き × 1マスの距離」を減算して移動
	global_position -= forward_vector * MOVE_STEP

# 90度旋回する関数
func rotate_horizontal(deg: float) -> void:
	# Godotの回転関数(rotate_y)はラジアンを要求するため、度数法(Degree)から変換して実行
	rotate_y(deg_to_rad(deg))

エディタがコード編集画面 (Scriptタブ) に切り替わったら、エディタ内にある初期コードをすべて消去し、上記のスクリプトをコピー&ペーストしてください。

コードを記述した後、メニューバーの Project から Project Settings を選択します。

Input Map タブをクリックします。新規アクションを追加欄に move_forward と入力し、Add をクリックして登録してください。

追加された move_forward の右側にある「+」ボタンを押し、キーボードの W を押して登録します。同じようにキーボードの↑キー、ゲームパッドの上ボタンでも同様に行います。

キーボードの↑キーやゲームパッドの上ボタンを押すだけで候補を選択できるので、一覧から探すより楽です。

同様に下、左、右についても登録します。

move_forwardWキー, キーボードの↑キー, ゲームパッドの上ボタン
move_backwardSキー, キーボードの↓キー, ゲームパッドの下ボタン
turn_leftAキー, キーボードの←キー, ゲームパッドの左ボタン
turn_rightDキー, キーボードの→キー, ゲームパッドの右ボタン

動作確認

画面右上にある再生ボタン (F5キーでも可) で、プロジェクトを実行します。初回起動時はメインシーンの選択を求められるので「現在のシーンを選択」をクリックしてください。

登録したボタンで上下左右に動き回れるかを確認してください。

衝突判定

残念ながら、上記の対応のままだと壁をすり抜けてしまいます。対策として衝突判定が必要です。

extends Camera3D

# Wiz系RPGの移動・旋回に関する定数定義
const MOVE_STEP: float = 2.0    # GridMapのセルサイズに合わせた1マスの移動距離
const ROTATE_STEP: float = 90.0 # 1回の旋回角度(度数法)

# メッシュライブラリ(MeshLibrary)で定義した「Wall(壁)」のアイテムID
# ※通常、ライブラリ内で最初に作ったものは 0 または 1 になります。
# もし壁をすり抜けてしまう場合は、この数値を 0 に変更してみてください。
const WALL_ITEM_ID: int = 1

# 迷宮データ(GridMap)への参照(型指定)
# ※Mainノードの直下にGridMapノードが配置されている想定のパスです
@onready var grid_map: GridMap = $"../GridMap"

func _unhandled_input(event: InputEvent) -> void:
	# キーボードやボタンが「押された瞬間」だけを検知する(長押し暴走防止)
	if not event.is_pressed() or event.is_echo():
		return

	# 自作インプットマップに応じた分岐
	if event.is_action_pressed("move_forward"):
		try_move(MOVE_STEP)      # 前進を試みる
		
	elif event.is_action_pressed("move_backward"):
		try_move(-MOVE_STEP)     # 後退を試みる
		
	elif event.is_action_pressed("turn_left"):
		rotate_horizontal(ROTATE_STEP)
		
	elif event.is_action_pressed("turn_right"):
		rotate_horizontal(-ROTATE_STEP)

# 移動の成否を判定して処理する関数
func try_move(step_distance: float) -> void:
	# 1. カメラが現在向いている正面方向(ローカルの-Z方向)のベクトルを取得
	var forward_vector: Vector3 = -global_transform.basis.z
	
	# Y軸(上下)の傾きを排除し、完全な水平ベクトルにする
	forward_vector.y = 0
	forward_vector = forward_vector.normalized()
	
	# 2. 移動「予定」のグローバル3D座標を算出
	var target_global_pos: Vector3 = global_position + (forward_vector * step_distance)
	
	# 3. グローバル座標を、GridMap内の3次元整数座標(マップの配列インデックス)に変換
	# to_localでGridMap基準のローカル座標にし、local_to_mapで「何行何列目」の整数(Vector3i)にします
	var map_coord: Vector3i = grid_map.local_to_map(grid_map.to_local(target_global_pos))
	
	# 4. 指定したマス(整数座標)に配置されているアイテムのIDを取得
	var cell_item_id: int = grid_map.get_cell_item(map_coord)
	
	# 5. もし移動先のマスにあるアイテムIDが「壁」と同じなら、移動を拒否して関数を抜ける
	if cell_item_id == WALL_ITEM_ID:
		# ここに「壁にぶつかった時のドンという効果音や画面の揺れ」を後から追加できます
		return
		
	# 6. 壁がなければ(空。または床データであれば)実際にカメラの座標を更新する
	global_position = target_global_pos

# 90度旋回する関数
func rotate_horizontal(deg: float) -> void:
	rotate_y(deg_to_rad(deg))

camera_3d.gd のコードを上記のように書き換えます。書き換えた後、再生ボタン(F5キー) を押してテストしてください。壁に向かって上ボタンを押したときに動きが止まれば成功です。

機能追加 (コントローラーのLRボタンで左右移動)

上記のままでも動きます。しかし、コントローラーのLRボタンで平行移動したい人もいるでしょう。その処理を追記していきます。

Project メニューから Project Setting を選択してください。Input Map タブをクリックします。

新規アクションを追加 で move_left, move_right を追加してください。そのあと、それぞれの右側にある「+」ボタンを押し、ゲームパッドのLボタンとキーボードのQキー、ゲームパッドのRボタンとを追加します。

move_leftキーボードのQキー, ゲームパッドのLボタン
move_rightキーボードのEキー, ゲームパッドのRボタン
extends Camera3D

const MOVE_STEP: float = 2.0
const ROTATE_STEP: float = 90.0
const WALL_ITEM_ID: int = 1

@onready var grid_map: GridMap = $"../GridMap"

func _unhandled_input(event: InputEvent) -> void:
	if not event.is_pressed() or event.is_echo():
		return

	# 前後移動(Vector3の組み込み定数を使わず、固定の方向ベクトルを指定して確実化)
	if event.is_action_pressed("move_forward"):
		try_move_direction(Vector3(0, 0, -1))  # 前(-Z方向)
	elif event.is_action_pressed("move_backward"):
		try_move_direction(Vector3(0, 0, 1))   # 後(+Z方向)
		
	# 左右の平行移動
	elif event.is_action_pressed("move_left"):
		try_move_direction(Vector3(-1, 0, 0))  # 左(-X方向)
	elif event.is_action_pressed("move_right"):
		try_move_direction(Vector3(1, 0, 0))   # 右(+X方向)
		
	# 左右の90度旋回
	elif event.is_action_pressed("turn_left"):
		rotate_horizontal(ROTATE_STEP)
	elif event.is_action_pressed("turn_right"):
		rotate_horizontal(-ROTATE_STEP)

func try_move_direction(local_direction: Vector3) -> void:
	var direction_vector: Vector3 = global_transform.basis * local_direction
	
	direction_vector.y = 0
	direction_vector = direction_vector.normalized()
	
	var target_global_pos: Vector3 = global_position + (direction_vector * MOVE_STEP)
	
	var map_coord: Vector3i = grid_map.local_to_map(grid_map.to_local(target_global_pos))
	
	var cell_item_id: int = grid_map.get_cell_item(map_coord)
	
	if cell_item_id == WALL_ITEM_ID:
		return
		
	global_position = target_global_pos

func rotate_horizontal(deg: float) -> void:
	rotate_y(deg_to_rad(deg))

camera_3d.gd のコードを上記のように書き換えてください。そのあと、実際に動かしてゲームパッドの L, R ボタンが機能するかを確認します。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

コメント

コメントする

CAPTCHA


目次