友達と1台の画面で遊ぶローカル協力プレイ——2人の視点を左右に並べた 分割画面(スプリットスクリーン) は、その定番の見せ方です。でも「1つのゲーム世界を、2つのカメラで別々に映す」をどう作ればいいのか、最初は見当がつきません。
そこで使うのが SubViewport です。この記事では、2つのSubViewportで画面を分ける構成から、world_2d/world_3d で 同じ世界を共有 する仕組み、 入力の振り分け 、そしてパフォーマンス注意点までを解説します。
この記事でわかること
- 分割画面の構成(SubViewportを2つ並べる)
world_2d/world_3dで 同じ世界を共有 する- プレイヤーごとの 入力の振り分け
- 分割画面の パフォーマンス 注意点
分割画面の構成:SubViewportを2つ並べる
分割画面の基本は、 画面を2つに区切り、それぞれに SubViewport を1つずつ置く ことです。左右分割なら、画面の左半分と右半分に、独立した映像を映します。

ノード構成はこうなります。HBoxContainer で左右に並べ、各 SubViewportContainer の中に SubViewport を入れます。
Root
└── HBoxContainer # 左右に並べる
├── SubViewportContainer # 左半分
│ └── SubViewport
│ └── Camera2D # プレイヤー1を追う
└── SubViewportContainer # 右半分
└── SubViewport
└── Camera2D # プレイヤー2を追う
SubViewportContainer:SubViewportを画面上に表示する枠。ここのサイズが分割画面の1つ分の大きさになりますSubViewport:実際に映像をレンダリングする場所。中にカメラを置きますCamera2D/Camera3D:各SubViewportに1つずつ。それぞれのプレイヤーを追わせます(Camera2Dの追従 参照)
これで「2つの画面枠」ができました。でも、このままだと2つのSubViewportは 別々の空っぽの世界 を映しています。次に、両方で同じゲーム世界を映す設定が必要です。
同じ世界を共有する:world_2d / world_3d
分割画面の肝が、 2つのSubViewportに同じゲーム世界を共有させる ことです。世界を2つ作るのではなく、 1つの世界を2つのカメラで見る のです。

これは world_2d(2D)または world_3d(3D)を共有することで実現します。片方のSubViewportの world を、もう片方に代入します。
@onready var viewport_a: SubViewport = $HBoxContainer/LeftContainer/SubViewport
@onready var viewport_b: SubViewport = $HBoxContainer/RightContainer/SubViewport
func _ready() -> void:
# 右の画面(B)に、左の画面(A)と同じ2Dワールドを共有させる
viewport_b.world_2d = viewport_a.world_2d
これで、viewport_a に配置したプレイヤー・敵・地形が、viewport_b の画面にも 同じもの として映ります。あとは各SubViewportのカメラを、それぞれのプレイヤーの位置に追従させれば、「同じ世界を、2人それぞれの視点で見る」分割画面の完成です。3Dなら world_2d を world_3d に読み替えるだけで、同じ考え方が使えます。
入力を振り分ける
分割画面では、 1つのキーボード(やパッド)を2人で分け合う ため、入力をプレイヤーごとに振り分けます。

やり方は、 プレイヤーごとに別々の入力アクションを用意する ことです。InputMap で、P1用とP2用のアクションを定義します。
# player.gd(各プレイヤーに、自分のアクション接頭辞を持たせる)
@export var action_prefix := "p1" # P1は"p1"、P2は"p2"をInspectorで設定
func _physics_process(_delta: float) -> void:
var input := Input.get_vector(
action_prefix + "_left", action_prefix + "_right",
action_prefix + "_up", action_prefix + "_down")
velocity = input * SPEED
move_and_slide()
- キーボードを分ける :P1は
p1_left/p1_right…(WASD)、P2はp2_left/p2_right…(矢印キー)に割り当てます - ゲームパッドで分ける :
Input.get_vector()の代わりに、デバイスID(event.device)でパッドごとに振り分ければ、2つのコントローラーで遊べます
action_prefix を @export にしておけば、同じ player.gd を使い回し、Inspectorで「このプレイヤーはP1」「こっちはP2」と設定するだけで済みます。
実践:2人協力の分割画面を組む
協力アクションの2人プレイ、レースゲームの対戦、パーティゲームのミニゲーム——ローカルマルチはどれも、この分割画面が土台です。ここまでの部品を組み合わせると、こうなります。

組み立ての流れは3ステップです。
- 画面を分ける :
HBoxContainerにSubViewportContainerを2つ並べ、各SubViewportにカメラを置く - 世界を共有 :
viewport_b.world_2d = viewport_a.world_2dで同じ世界を映す - 入力を分ける :P1とP2に別々のアクションを割り当て、各カメラを各プレイヤーに追従させる
ポイントは2つです。
- 世界は1つ、画面は2つ :ゲーム世界(プレイヤー・敵・地形)は1セットだけ作り、それを2つのSubViewportで共有します。世界を二重に作ると、状態がずれてバグの温床になります。
- カメラは各SubViewportの中に :カメラはそれぞれのSubViewportの子として置き、担当プレイヤーを追わせます。これで「1つの世界を、2人それぞれの視点で」映せます。
パフォーマンス注意
分割画面には、避けられないコストがあります。 画面を2つレンダリングするため、描画負荷がおよそ2倍 になることです。

同じ世界を2回描くので、重い3Dシーンやたくさんのエフェクトがあると、フレームレートに影響します。対策としては、
- 各SubViewportの解像度を控えめに する(分割ぶん小さくなるので、そもそも高解像度は不要)
- 影やポストプロセスなど 重い描画を減らす
- ローエンド端末を想定するなら、描画数を計測してから調整する(フレームレート管理 参照)
「分割画面は描画2回分」と頭に入れておけば、パフォーマンス設計で慌てずに済みます。
おまけ:先に知っておくと良いこと
- 3人・4人にも広げられる :
SubViewportContainerを4つ並べれば、4分割の画面も作れます。GridContainerを使うと2×2に配置できます。 - UIは画面ごとに :各プレイヤーのHPやスコアは、それぞれのSubViewport内(または各SubViewportContainerの上)に置いて、画面ごとに表示します。
- オンライン対戦は別の仕組み :ここで扱ったの は1台で遊ぶローカルマルチです。ネットワーク越しの対戦は、
MultiplayerAPIなど別の仕組みになります。
まとめ
- 分割画面は、画面を分けて
SubViewportを2つ 置き、それぞれに各プレイヤーを追うカメラを入れる world_2d/world_3dを共有 すれば、1つの世界を2つのカメラで映せる(世界を二重に作らない)- 入力は プレイヤーごとに別アクション を定義して振り分ける(WASD/矢印、またはパッド)
- カメラは 各SubViewportの中 に置き、担当プレイヤーに追従させる
- 分割画面は 描画がおよそ2倍 。解像度や重い描画を調整する
まずは2つのSubViewportで世界を共有し、片方のカメラを動かして「同じ世界が2画面に映る」ところから始めてみてください。そこにプレイヤー2人と入力の振り分けを足せば、ローカル協力プレイの土台が完成します。
さらに学ぶために
- SubViewportの活用法 ——SubViewportとViewportTextureの基礎
- Camera2D実践テクニック集 ——各プレイヤーを追うカメラの作り方
- InputMapによるキーバインド管理 ——プレイヤーごとの入力アクション
- Godot公式ドキュメント:Using Viewports ——Viewportの一次情報