はじめに
ゲームを開発していると、「カメラのズームに反応してほしくないUI」「常に画面の上に表示したいHUD」が必要になります。例えば、HPゲージやスコア表示、一時停止パネル—これらはプレイヤーキャラクターがどこにいても、画面のどこかに固定されるべきです。こうした「カメラに依存しない描画」を実現するのが CanvasLayer です。
この記事では、CanvasLayer の基本的な役割から、layer プロパティで描画順序を制御する方法、follow_viewport_enabled で視点を固定する仕組みまで、実践的に解説します。
CanvasLayerとは?
CanvasLayer は、カメラに依存しない独立した描画レイヤーを作るノードです。CanvasLayer 以下のノードは、カメラのズーム・パン・回転の影響を受けず、常に画面上の固定位置(またはビューポート基準)で描画されます。
例えるなら、映画撮影で複数のレンズを重ねる手法。背景レンズはズームするが、字幕テロップはいつも同じサイズで画面下部に固定—CanvasLayer がそれです。
継承ツリー:
CanvasLayer → Node → Object
このノードを使うべき場面
使うべき場面
- HUD(HP、スコア、タイマー):ゲーム画面の進行に関わらず常時表示
- ミニマップ:画面コーナーに固定表示、ズームに影響されない
- 一時停止パネル:メニューやポーズ画面、暗い半透明パネル
- フェード暗幕:シーン遷移時の黒フェードイン・アウト
- マウスカーソル UI:マウス座標を追従する UI 要素
使わない場面
- プレイ画面の背景やメインキャラクター:通常の Node2D 構造で十分
- カメラの動きに追従する UI:CanvasLayer は向かない(UI のパラレックス効果)
主なプロパティと機能
| プロパティ | 型 | 説明 |
|---|---|---|
layer |
int | 描画順(大きい数値ほど前面に表示。-128〜127) |
offset |
Vector2 | レイヤー全体のオフセット(ピクセル単位) |
follow_viewport_enabled |
bool | true=カメラ追従、false=世界座標固定 |
follow_viewport_scale |
float | follow_viewport_enabled=true のとき、スケール倍率 |
transform |
Transform2D | レイヤーの変換行列(位置、回転、スケール) |
visible |
bool | レイヤー全体の表示/非表示 |
基本的な使用例1:HUD を CanvasLayer に配置
extends Control
# シーン構造:
# Root (Node2D) ← ゲーム世界
# ├─ Player
# ├─ Enemies
# └─ HUDLayer (CanvasLayer) ← HUD は独立
# ├─ HPLabel
# ├─ ScoreLabel
# └─ TimerLabel
@onready var hud_layer = $HUDLayer
@onready var hp_label = $HUDLayer/HPLabel
func _ready():
# HUD レイヤーは常に前面(layer=1)
hud_layer.layer = 1
# カメラに追従しない(固定位置)
hud_layer.follow_viewport_enabled = false
func update_hp(current_hp: int, max_hp: int):
hp_label.text = "HP: %d / %d" % [current_hp, max_hp]
フェード暗幕例2:シーン遷移時のフェードイン・アウト
extends CanvasLayer
@onready var fade_rect = $ColorRect
func _ready():
# フェードレイヤーを最前面に
layer = 100
fade_rect.color = Color.BLACK
fade_rect.modulate.a = 0.0 # 最初は透明
func fade_in(duration: float):
var tween = create_tween()
tween.tween_property(fade_rect, "modulate:a", 1.0, duration)
await tween.finished
func fade_out(duration: float):
var tween = create_tween()
tween.tween_property(fade_rect, "modulate:a", 0.0, duration)
await tween.finished

もっと使いこなす:layer と follow_viewport の組み合わせ
| 設定パターン | 動作 | 用途 |
|---|---|---|
follow_viewport_enabled=false |
世界座標固定、カメラズーム無視 | HUD、UI(最も一般的) |
follow_viewport_enabled=true |
カメラの動きに追従、ズームと同じ倍率 | パーティクル、背景エフェクト |
follow_viewport_enabled=true |
カメラ追従、ズームの半分(パラレックス効果) | 背景レイアー(視差効果) |
follow_viewport_enabled=true |
カメラに追従しない(固定背景) | 遠い背景、スターフィールド |

実践例:複数レイヤーの階層構造
extends Node2D
# シーン構造:
# Root
# ├─ BackgroundLayer (CanvasLayer, layer=-10)
# │ └─ StarField (視差背景)
# ├─ GameWorld (通常の Node2D)
# │ ├─ Player
# │ └─ Enemies
# ├─ EffectLayer (CanvasLayer, layer=0)
# │ └─ Explosions (パーティクル)
# └─ HUDLayer (CanvasLayer, layer=100)
# ├─ HPBar
# ├─ ScoreLabel
# └─ PauseMenu
@onready var background_layer = $BackgroundLayer
@onready var effect_layer = $EffectLayer
@onready var hud_layer = $HUDLayer
func _ready():
# 背景レイヤー:遠い背景として視差効果
background_layer.layer = -10
background_layer.follow_viewport_enabled = true
background_layer.follow_viewport_scale = 0.3 # カメラズームの30%だけ動く
# エフェクトレイヤー:ゲーム世界と同じズーム
effect_layer.layer = 0
effect_layer.follow_viewport_enabled = true
effect_layer.follow_viewport_scale = 1.0
# HUD レイヤー:UI は常に固定
hud_layer.layer = 100
hud_layer.follow_viewport_enabled = false
func trigger_effect(pos: Vector2):
# エフェクトレイヤーに爆発を生成
var explosion = preload("res://scenes/explosion.tscn").instantiate()
explosion.global_position = pos
effect_layer.add_child(explosion)
CanvasLayer と Control(UI)の違い
| 項目 | CanvasLayer | Control(UI) |
|---|---|---|
| 描画方式 | 2D ワールド座標 | 相対レイアウト(親に基づく) |
| カメラ影響 | 設定で制御可能 | 常に UI 画面空間 |
| スケーリング | 手動で設定 | 自動(アンカー・マージン) |
| 推奨用途 | HUD、エフェクト、背景 | メニュー、UI パネル |
まとめ
CanvasLayer は、ゲームの描画を層別に管理する強力なノードです。layer で描画順序を制御し、follow_viewport_enabled で視点固定/カメラ追従を切り替えれば、HUD からエフェクト、背景まで、多彩なゲーム表現が実現できます。
- 層別描画管理:layer プロパティで複数レイヤーの描画順序を明確に制御
- カメラ独立性:follow_viewport_enabled=false で HUD をカメラの動きから保護
- 視差効果:follow_viewport_scale で遠い背景と近い背景の速度差を演出
次のステップ:これで 2D ノード解説は一区切り。次回からは 3D ノード編(Node3D、MeshInstance3D など)へ進みます。立体的なゲーム表現の世界へようこそ!
シリーズ:Godot 4 ノード解説
- 001: Node2D
- 002: Sprite2D
- 003: CollisionShape2D
- 004: CharacterBody2D
- 005: RigidBody2D
- 006: Area2D
- 007: Control
- 008: Button
- 009: Label
- 010: TextEdit
- 011: ItemList
- 012: TabContainer
- 013: Timer
- 014: Tween
- 015: Camera2D
- 016: ParallexBackground
- 017: TileMap(旧)
- 018: Path2D
- 019: PathFollow2D
- 020: RemoteTransform2D
- 021: Marker2D
- 022: Node
- 023: Resource
- 024: Script
- 025: Signal
- 026: Input
- 027: Physics2DServer
- 028: VisibleOnScreenNotifier2D
- 029: RayCast2D
- 030: ShapeCast2D
- 031: PinJoint2D
- 032: DistanceJoint2D
- 033: HingeJoint2D
- 034: SliderJoint2D
- 035: ConeTwistJoint2D
- 036: Generic6DOFJoint2D
- 037: Polygon2D
- 038: ColorRect
- 039: NinePatchRect
- 040: TextureRect
- 041: StaticBody2D
- 042: TileMapLayer
- 043: AnimationPlayer
- 044: AnimationTree
- 045: AudioStreamPlayer
- 046: AudioStreamPlayer2D
- 047: GPUParticles2D
- 048: CPUParticles2D
- 049: Line2D
- 050: CanvasLayer
この記事はGodot 4.xをもとに執筆しています。


コメント