【Godot4.5】エリアチェンジに合わせたカメラスクロールの実装
某ロッ〇マンのようなエリアの区切りに入った際のカメラ演出のサンプルを作ったので簡単に解説をしてみます。

プロジェクトはこちら
シーン概要
まずはノード構成の説明です。

- Playerはそのままプレイヤーキャラクターです。
- Zoneは各エリアを示していてエリア毎に範囲の制限設定をされているカメラと、プレイヤーがエリアに入ったことを通知するためにArea2Dが配置されています。カメラの描画制限範囲とArea2DのCollisionの大きさはほぼ同じ大きさです。
- TransitionCameraはスクロール演出用のカメラで演出時のみ機能します。
スクリプト
シーンの親であるNode2Dには以下のスクリプトがアタッチされています。
extends Node2D
@onready var current_camera = $Zone1/Zone1/Camera2D
#カメラの初期設定
func _ready() -> void:
current_camera.make_current()
current_camera.global_position = current_camera.get_screen_center_position()
内容としてはシーン開始時にZone1から開始するのでそのエリアのカメラをセットするというものになっています。
current_camera.global_position = current_camera.get_screen_center_position()
この部分に関しては範囲制限をしているカメラは映っている画面の中心とプロパティの位置にズレが生じるので、それを修正するためにカメラ位置を見えている画面の中心に置き直しています。
次にPlayerノードのスクリプトです。
extends Sprite2D
func _process(_delta: float) -> void:
if Input.is_action_pressed("ui_left"):
_move_player_and_current_camera(Vector2(-10, 0))
if Input.is_action_pressed("ui_right"):
_move_player_and_current_camera(Vector2(10, 0))
if Input.is_action_pressed("ui_down"):
_move_player_and_current_camera(Vector2(0, 10))
if Input.is_action_pressed("ui_up"):
_move_player_and_current_camera(Vector2(0, -10))
#現在のカメラをプレイヤー移動に追従させる
func _move_player_and_current_camera(move_vector : Vector2):
$"..".current_camera.position += move_vector
position += move_vector
矢印キーの入力でプレイヤーと現在のカメラの位置を加算するという単純な内容になっています。
最後にTransitionCameraのスクリプトです。
extends Camera2D
var current_camera
func _on_zone_area_entered(area: Area2D) -> void:
#最初の検出はトランジションがいらないのでカメラ設定だけする
if not current_camera:
current_camera = get_parent().current_camera
return
#ゲーム進行を一時中断
get_tree().paused = true
#ゾーンカメラの最後の位置を調整
current_camera.global_position = current_camera.get_screen_center_position()
#カメラ遷移のはじまりの位置
position = current_camera.global_position
#遷移用カメラをアクティブ化
show()
make_current()
#新規ゾーンのカメラを取得
var area_camera = area.get_node("Camera2D")
#カメラ遷移アニメーションの作成
var tween = get_tree().create_tween()
#シーンのポーズでツイーンアニメーションを止めないよう設定
tween.set_pause_mode(Tween.TWEEN_PAUSE_PROCESS)
#新規カメラ位置へ移動
tween.tween_property(self, "position", area_camera.global_position, 1.0)
#新規カメラを現在カメラに変更
tween.tween_callback(_switch_current_camera.bind(area_camera))
#ゲームを進行させる
tween.tween_callback(_continue)
hide()
func _continue():
get_tree().paused = false
func _switch_current_camera(next_area_camera : Camera2D):
next_area_camera.make_current()
current_camera = next_area_camera
get_parent().current_camera = next_area_camera
Playerノードが各Zoneで進入した際に_on_zone_area_entered関数が呼ばれカメラスクロール演出が実行されるという構成になっています。
ブロックごとに見ていきましょう。
#最初の検出はトランジションがいらないのでカメラ設定だけする
if not current_camera:
current_camera = get_parent().current_camera
return
こちらはシーン開始時にZone1にプレイヤーが配置された際にも反応してしまうため演出を実行しないためのコードです。
#ゲーム進行を一時中断
get_tree().paused = true
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#シーンのポーズでツイーンアニメーションを止めないよう設定
tween.set_pause_mode(Tween.TWEEN_PAUSE_PROCESS)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#ゲームを進行させる
tween.tween_callback(_continue)
func _continue():
get_tree().paused = false
これらは全体のゲーム進行を一時停止しつつ、スクロール演出は実行するための処理になっています。
元ネタがそのような演出だったのと、一時停止しないと前エリアのカメラ位置がズレてしまったため実装しています。
#ゾーンカメラの最後の位置を調整
current_camera.global_position = current_camera.get_screen_center_position()
こちらは現在カメラに切り替える前に旧エリアに配置されているカメラ位置を調整するためのものになっています。この処理をしないとスクロール開始時のカメラ位置がズレる可能性があること、エリアを戻った際にスクロール位置がズレてしまうので結構重要です。
#カメラ遷移のはじまりの位置
position = current_camera.global_position
#遷移用カメラをアクティブ化
show()
make_current()
#新規ゾーンのカメラを取得
var area_camera = area.get_node("Camera2D")
この辺りは演出用カメラの開始位置&次エリアのカメラの取得と演出用カメラのアクティブ化をしています。
#カメラ遷移アニメーションの作成
var tween = get_tree().create_tween()
#シーンのポーズでツイーンアニメーションを止めないよう設定
tween.set_pause_mode(Tween.TWEEN_PAUSE_PROCESS)
#新規カメラ位置へ移動
tween.tween_property(self, "position", area_camera.global_position, 1.0)
スクロール演出を担当しているのがここのTweenクラスです。tween.tween_property関数でノード、プロパティ、ツイーン目標の値、演出時間を指定しています。
#新規カメラを現在カメラに変更
tween.tween_callback(_switch_current_camera.bind(area_camera))
func _switch_current_camera(next_area_camera : Camera2D):
next_area_camera.make_current()
current_camera = next_area_camera
get_parent().current_camera = next_area_camera
tween.tween_callback関数で演出の最後に新エリアのカメラに切り替えを行う関数を呼び出しています。
振り返り
今回のサンプルシーンをつくるにあたってつまづいた箇所が二つありました。
一つ目はカメラをプレイヤーの子として配置すると、プレイヤーの位置が加算されるので位置を用いたアニメーションが難しくなるということです。この問題の回避方法としてカメラはプレイヤーの子には直接ぶら下げずにスクリプトでプレイヤーと同じ分だけ移動させるという方法を取りました。
二つ目はカメラに範囲制限を行っていると画面上の見た目の位置とカメラ自体の位置にかなりズレが生じるということです。これを修正するためにはget_screen_center_position関数が非常に重要だと分かりました。
今回はエリアごとにカメラを配置&演出用カメラを用意という方法を取りましたが、Marker2Dでエリア毎のカメラ範囲とカメラ位置の記憶を行うことで一つのカメラでも実行できそうです。
Discussion