【Godot】スプリット画面でローカル協力プレイ - SubViewportで作る分割画面

作成: 2026-07-09

GodotのSubViewportで2人ローカル協力プレイの分割画面を作る方法を解説。左右2つのSubViewportの構成、world_2d/world_3dで同じ世界を共有する仕組み、入力の振り分け、パフォーマンス注意点までを図とコードで学びます。

友達と1台の画面で遊ぶローカル協力プレイ——2人の視点を左右に並べた 分割画面(スプリットスクリーン) は、その定番の見せ方です。でも「1つのゲーム世界を、2つのカメラで別々に映す」をどう作ればいいのか、最初は見当がつきません。

そこで使うのが SubViewport です。この記事では、2つのSubViewportで画面を分ける構成から、world_2d/world_3d同じ世界を共有 する仕組み、 入力の振り分け 、そしてパフォーマンス注意点までを解説します。

分割画面のイメージ。1つのゲーム世界を、左右2つの画面に分けて、それぞれのプレイヤーを追うカメラで映している

この記事でわかること

  • 分割画面の構成(SubViewportを2つ並べる
  • world_2d / world_3d同じ世界を共有 する
  • プレイヤーごとの 入力の振り分け
  • 分割画面の パフォーマンス 注意点

Sponsored

分割画面の構成:SubViewportを2つ並べる

分割画面の基本は、 画面を2つに区切り、それぞれに SubViewport を1つずつ置く ことです。左右分割なら、画面の左半分と右半分に、独立した映像を映します。

分割画面の構成図。画面を左右に2分割し、左に SubViewportContainer(中に SubViewport とプレイヤー1を追う Camera)、右に SubViewportContainer(中に SubViewport とプレイヤー2を追う Camera)を配置。それぞれが別々の視点を映す

ノード構成はこうなります。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共有の図。1つのゲーム世界(プレイヤー1とプレイヤー2が同じフィールドにいる)を、左のSubViewportのカメラと右のSubViewportのカメラが別々の位置から映す。world_2dを共有することで、両方の画面に同じ世界が映る

これは 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_2dworld_3d に読み替えるだけで、同じ考え方が使えます。

Sponsored

入力を振り分ける

分割画面では、 1つのキーボード(やパッド)を2人で分け合う ため、入力をプレイヤーごとに振り分けます。

入力の振り分けの図。P1はWASDキーの入力アクション(p1_move)でプレイヤー1を操作、P2は矢印キーやゲームパッドの入力アクション(p2_move)でプレイヤー2を操作。同じキーボードでも別々のアクションに割り当てることで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人プレイ、レースゲームの対戦、パーティゲームのミニゲーム——ローカルマルチはどれも、この分割画面が土台です。ここまでの部品を組み合わせると、こうなります。

2人協力分割画面の完成イメージ図。左の画面はプレイヤー1(青いマネキン)を中心に映し、右の画面はプレイヤー2を中心に映す。2人は同じフィールドにいて、それぞれのカメラが各自を追っている

組み立ての流れは3ステップです。

  1. 画面を分けるHBoxContainerSubViewportContainer を2つ並べ、各 SubViewport にカメラを置く
  2. 世界を共有viewport_b.world_2d = viewport_a.world_2d で同じ世界を映す
  3. 入力を分ける :P1とP2に別々のアクションを割り当て、各カメラを各プレイヤーに追従させる

ポイントは2つです。

  • 世界は1つ、画面は2つ :ゲーム世界(プレイヤー・敵・地形)は1セットだけ作り、それを2つのSubViewportで共有します。世界を二重に作ると、状態がずれてバグの温床になります。
  • カメラは各SubViewportの中に :カメラはそれぞれのSubViewportの子として置き、担当プレイヤーを追わせます。これで「1つの世界を、2人それぞれの視点で」映せます。

パフォーマンス注意

分割画面には、避けられないコストがあります。 画面を2つレンダリングするため、描画負荷がおよそ2倍 になることです。

分割画面のパフォーマンスの図。通常は世界を1回描画するが、分割画面では左右2つのSubViewportで世界を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人と入力の振り分けを足せば、ローカル協力プレイの土台が完成します。

さらに学ぶために