Godot 4のコンポーネント指向開発シリーズ、今回はレトロアーケードゲームやシューティングゲーム(STG)で定番の機能**「ScreenWrapper (画面端ループ)」**です。
『パックマン』や『アステロイド』のように、キャラクターが画面の右端から出ると、即座に左端から出現するあの挙動を、ノードを1つ追加するだけで実装します。
このコンポーネントは、親ノードの座標を監視し、画面(ビューポート)の境界線を越えた瞬間、反対側の境界線へワープさせます。
1. コンポーネントのコード (Full Code)
以下のコードをコピーして、ScreenWrapper.gd という名前で保存してください。
class_name ScreenWrapper
extends Node
## 親ノードが画面外に出たら、反対側から出現させる(ループ)コンポーネント
## 親ノードは Node2D (CharacterBody2D, Area2D, Sprite2D等) を想定しています。
## 注意: 固定カメラ(画面切り替えなし)のゲームで最も効果を発揮します。
# --- 設定パラメータ ---
@export_group("Wrap Settings")
@export var horizontal_wrap: bool = true ## 横方向のループを有効にするか
@export var vertical_wrap: bool = true ## 縦方向のループを有効にするか
@export var margin: float = 20.0 ## 画面端からの猶予(スプライトの半分くらいのサイズを指定)
# --- 内部変数 ---
var _parent: Node2D
func _ready() -> void:
_parent = get_parent() as Node2D
if not _parent:
push_error("ScreenWrapper: 親が Node2D ではありません。")
set_process(false)
func _process(_delta: float) -> void:
# 現在の画面サイズ(矩形)を取得
# カメラを使っている場合はカメラの位置考慮が必要ですが、
# ここではシンプルな「ウィンドウサイズ」基準で実装しています。
var screen_rect = get_viewport().get_visible_rect()
var pos = _parent.global_position
# --- 横方向 (X軸) のループ判定 ---
if horizontal_wrap:
# 右端を超えたら -> 左端へ
if pos.x > screen_rect.end.x + margin:
_parent.global_position.x = screen_rect.position.x - margin
# 左端を超えたら -> 右端へ
elif pos.x < screen_rect.position.x - margin:
_parent.global_position.x = screen_rect.end.x + margin
# --- 縦方向 (Y軸) のループ判定 ---
if vertical_wrap:
# 下端を超えたら -> 上端へ
if pos.y > screen_rect.end.y + margin:
_parent.global_position.y = screen_rect.position.y - margin
# 上端を超えたら -> 下端へ
elif pos.y < screen_rect.position.y - margin:
_parent.global_position.y = screen_rect.end.y + margin
2. 使い方チュートリアル
このコンポーネントは、宇宙船や隕石など、「画面内に留まらせたいオブジェクト」なら何にでも使えます。
手順①:対象オブジェクトの準備
- プレイヤーや敵キャラ(
CharacterBody2DやArea2D)を用意します。 - 以前作成した
KeyboardMoverなどを付けて、動ける状態にしておくとテストが楽です。
手順②:コンポーネントのアタッチ
- 対象オブジェクトの子ノードとして
Nodeを追加し、名前をScreenWrapperにします。 - スクリプト
ScreenWrapper.gdをアタッチします。
シーン構成図:
Player (CharacterBody2D)
├── Sprite2D (画像: サイズ約64pxと仮定)
├── CollisionShape2D
├── KeyboardMover (移動用)
└── ScreenWrapper (Node) <-- 今回追加!
手順③:マージン(Margin)の調整
これが最も重要です。
Margin が 0 だと、画像の中心点が画面端に来た瞬間にワープするため、「体が半分残っているのに消えて、反対側から半分出てくる」 という不自然な見た目(ポップ現象)になります。
- 推奨設定: スプライトの幅の半分、または少し大きめの値。
- 例: 画像サイズが 64×64 なら、
Marginは32〜40くらい。 - こうすると、「完全に画面外に出てから、反対側の画面外へワープ」するため、自然に見えます。
- 例: 画像サイズが 64×64 なら、
手順④:実行
ゲームを実行し、画面の端に向かって移動し続けてください。
右から出たら左から、下から出たら上から、自然に戻ってくるはずです。
3. 応用テクニック
隕石や弾丸にも使える
プレイヤーだけでなく、「敵の弾」や「漂う隕石」にこれを付けると、画面内を永遠に飛び交う障害物が簡単に作れます。
(例:ProjectileMover で直進させ、ScreenWrapper でループさせる)
一方向だけループさせる
インスペクターの Horizontal Wrap や Vertical Wrap のチェックを外せば、「横はループするけど、上下は壁で止まる(または落ちる)」といった挙動も作れます。
- 横スクロールSTG:
Vertical WrapをOFFにして、上下移動を制限する。 - プラットフォーマー:
Horizontal WrapをONにして、マリオブラザーズのような「左右が繋がったステージ」を作る。
カメラを使う場合(上級者向け注記)
このコードは get_viewport().get_visible_rect() を使用しているため、**「カメラが固定されているゲーム」**で正常に動作します。
もし広大なマップをカメラが移動するゲームで「特定の部屋の中だけループさせたい」場合は、screen_rect の取得部分を「カメラの現在の枠」や「指定したArea2Dの枠」に書き換える必要があります。
