本文へ移動
cccskills
無料GitHub で公開

phantom-camera

Use when using the Phantom Camera addon — PhantomCamera2D/3D with priority-based switching, follow and look-at modes, and tween transitions

インストール方法を見る

含まれるファイル(2)

  • SKILL.md12.7 KB
  • references/follow-modes.md3.4 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Phantom Camera

Related skills: camera-system for hand-rolled camera patterns, tween-animation for the easing concepts the transitions build on.

Addon: Phantom Camera · version v0.11.0.2 · Godot 4.4+ · MIT · source: https://github.com/ramokz/phantom-camera · nodes are GDScript, plus an official C# wrapper API (namespace PhantomCamera) shipped as source .cs files in the addon. Pre-1.0: minor versions may break API.


1. When to use Phantom Camera vs. camera-system

ApproachBest for
Hand-rolled (camera-system skill)One camera, simple follow/shake, no addon dependency
Phantom CameraMultiple camera "shots" that need priority-based switching, dead-zone/group/path/third-person follow logic, and smooth resource-driven tweens between them — Cinemachine-style workflow

Reach for Phantom Camera when a scene needs several distinct camera behaviors (gameplay follow, a cutscene framing, a boss-fight group shot) that swap automatically based on priority, rather than one script juggling every case. It replaces the Camera2D/Camera3D positioning logic itself — you still keep exactly one real Camera2D/Camera3D per viewport; Phantom Camera nodes never render anything on their own.


2. Install & setup

Asset Library (recommended): Godot AssetLib → search "Phantom Camera" → Download (select only the phantom_camera directory) → Project → Project Settings → Plugins → enable it.

GitHub zip: extract addons/phantom_camera/ into the project root, then enable the plugin the same way.

Enabling the plugin auto-registers a PhantomCameraManager autoload singleton and restarts the editor every time _enable_plugin() runs (not just the first time) — expected behavior, not a bug. No manual autoload setup is needed. Six custom node types become available in the "Create New Node" dialog: PhantomCamera2D, PhantomCamera3D, PhantomCameraHost, PhantomCameraNoiseEmitter2D, PhantomCameraNoiseEmitter3D, and PhantomCameraTweenDirector.

C# projects: the addon ships its official wrapper as plain .cs source files under addons/phantom_camera/scripts/** (namespace PhantomCamera) — no NuGet package to add. A C#-enabled Godot project (one with its own generated .csproj, Godot.NET.Sdk) picks these up automatically once the addon folder is present; using PhantomCamera; is then enough (§3–§7).


3. Host + camera model

Two node kinds work together:

  • PhantomCameraHost — add it as a child of your real Camera2D/Camera3D (not the other way around). It reads the highest-priority PhantomCamera2D/3D in the scene and drives the real camera's transform every frame. Only the first PhantomCameraHost child of a given camera is used.
  • PhantomCamera2D / PhantomCamera3D — placed anywhere else in the scene tree (siblings of the player, inside trigger areas, cutscene rigs). Any number can exist; each one describes a candidate "shot" via priority, a follow mode, and (3D only) a look-at mode. They render nothing themselves.
# Scene tree:
# Camera2D (or Camera3D)
#   └─ PhantomCameraHost
# Player (CharacterBody2D)
#   └─ PhantomCamera2D   (priority 0, follow_mode = SIMPLE, follow_target = Player)
# BossArena
#   └─ PhantomCamera2D   (priority 10, follow_mode = GROUP, follow_targets = [Player, Boss])
# CameraRig.gd — on the Camera2D/Camera3D
extends Camera2D

@onready var host: PhantomCameraHost = $PhantomCameraHost

func _ready() -> void:
    # host.camera_2d / host.camera_3d are populated automatically from get_parent()
    var active := host.get_active_pcam()
    print("Active PCam: ", active.name if active else "none")
// CameraRig.cs — on the Camera2D/Camera3D
using PhantomCamera;

public partial class CameraRig : Camera2D
{
    private PhantomCameraHost _host;

    public override void _Ready()
    {
        // Host.Camera2D / Host.Camera3D are populated automatically from GetParent()
        _host = GetNode<Node>("PhantomCameraHost").AsPhantomCameraHost();
        var active = _host.GetActivePhantomCamera();
        GD.Print("Active PCam: ", active is PhantomCamera2D p ? p.Node2D.Name.ToString() : "none");
    }
}

PhantomCameraHost.interpolation_mode (C#: InterpolationMode, enum Auto/Idle/Physics/Manual) controls when the Host updates the real camera: AUTO (default — picks physics or idle based on the active PCam's target), IDLE, PHYSICS, or MANUAL (call host.process(delta) yourself each tick).

host_layers (@export_flags_2d_render / _3d_render) on both the Host and each PCam gate which PCams a given Host will consider — a PCam is only eligible if its host_layers bitmask overlaps the Host's.


4. Priority-based switching

Every PhantomCamera2D/3D has priority: int = 0. The PhantomCameraHost attached to the scene's real camera always follows the highest-priority PCam that shares a host_layers bit with it. Change priority at runtime with set_priority(value) / read with get_priority() — values are clamped to >= 0.

# TriggerArea.gd — raise priority while the player is inside, restore on exit
extends Area2D

@export var area_pcam: PhantomCamera2D

func _ready() -> void:
    area_entered.connect(_on_entered)
    area_exited.connect(_on_exited)

func _on_entered(area: Area2D) -> void:
    if area.get_parent() is CharacterBody2D:
        area_pcam.set_priority(20)

func _on_exited(area: Area2D) -> void:
    if area.get_parent() is CharacterBody2D:
        area_pcam.set_priority(0)
using PhantomCamera;

public partial class TriggerArea : Area2D
{
    [Export] private Node2D _areaPCamNode;
    private PhantomCamera2D _areaPCam;

    public override void _Ready()
    {
        _areaPCam = _areaPCamNode.AsPhantomCamera2D();
        AreaEntered += a => { if (a.GetParent() is CharacterBody2D) _areaPCam.Priority = 20; };
        AreaExited  += a => { if (a.GetParent() is CharacterBody2D) _areaPCam.Priority = 0; };
    }
}

Useful events on each PCam wrapper: BecameActive, BecameInactive, TweenStarted, IsTweening (every frame while transitioning), TweenInterrupted (a higher-priority PCam preempted this tween — argument is the interrupting node), TweenCompleted — subscribe with +=, same names as the GDScript signals in PascalCase.

priority_override: bool is an editor-only "force preview" toggle for quickly previewing a shot without touching priority; it's disabled automatically in exported builds — don't use it for gameplay logic.


5. Follow modes

PhantomCamera2D/PhantomCamera3D follow a target through the FollowMode enum: GLUED, SIMPLE, GROUP, PATH, FRAMED, and THIRD_PERSON (3D only). The reference covers each mode's properties, GDScript and C# setup, GROUP auto-reframe, and the shared options follow_axis_lock, rotate_with_target (2D only), and lookahead, plus is_following() and teleport_position().

See references/follow-modes.md for the mode table, setup examples, and shared follow options.


6. Look-at modes (3D only)

PhantomCamera2D has no look-at system — only rotate_with_target (§5). PhantomCamera3D adds:

enum LookAtMode { NONE = 0, MIMIC = 1, SIMPLE = 2, GROUP = 3 }
ModeBehavior
MIMICCopies the target's rotation directly.
SIMPLELooks straight at look_at_target (single Node3D).
GROUPLooks at the centroid of look_at_targets: Array[Node3D].
extends PhantomCamera3D

func _ready() -> void:
    look_at_mode = LookAtMode.SIMPLE
    look_at_target = get_node("../Boss")
    look_at_damping = true
    look_at_damping_value = 0.25  # single scalar, not per-axis
    up_target = get_node("../GroundNormalMarker")  # overrides `up` continuously
using PhantomCamera;

public partial class BossLookAtSetup : Node
{
    [Export] private Node3D _pCamNode; // has a PhantomCamera3D node/script attached
    [Export] private Node3D _boss;
    [Export] private Node3D _groundNormalMarker;

    public override void _Ready()
    {
        // LookAtMode has no wrapper setter (getter-only) — set it on the underlying node.
        _pCamNode.Set("look_at_mode", (int)LookAtMode.Simple);

        var pCam = _pCamNode.AsPhantomCamera3D();
        pCam.LookAtTarget = _boss;
        pCam.LookAtDamping = true;
        pCam.LookAtDampingValue = 0.25f; // single scalar, not per-axis
        pCam.UpTarget = _groundNormalMarker;
    }
}

Gotcha (from the addon's own runtime warning): combining a non-NONE follow_mode with a non-NONE look_at_mode on the same PhantomCamera3D prints "Using both Look At and Follow Mode on the same PCam3D has not been fully tested yet, proceed with caution!" — treat that combination as experimental and verify it manually before shipping.


7. Tweening between cameras

Each PCam owns a tween_resource: PhantomCameraTween (a Resource — share one .tres across several PCams to reuse timing, or leave each with its own default instance):

enum TransitionType {
    LINEAR = 0, SINE = 1, QUINT = 2, QUART = 3, QUAD = 4, EXPO = 5,
    ELASTIC = 6, CUBIC = 7, CIRC = 8, BOUNCE = 9, BACK = 10,
}
enum EaseType { EASE_IN = 0, EASE_OUT = 1, EASE_IN_OUT = 2, EASE_OUT_IN = 3 }

@export var duration: float = 1.0
@export var transition: TransitionType = TransitionType.LINEAR
@export var ease: EaseType = EaseType.EASE_IN_OUT

These map 1:1 to Godot's built-in Tween.TransitionType / Tween.EaseType names (minus the TRANS_/EASE_ prefixes) — see tween-animation for what each curve looks like.

# Cutscene PCam: slow, elastic-eased transition when it takes priority
extends PhantomCamera3D

func _ready() -> void:
    tween_resource = PhantomCameraTween.new()
    tween_duration = 1.5          # passthrough — writes tween_resource.duration
    # TransitionType/EaseType live on PhantomCameraTween — qualify them:
    tween_transition = PhantomCameraTween.TransitionType.ELASTIC
    tween_ease = PhantomCameraTween.EaseType.EASE_OUT
using PhantomCamera;

public partial class CutsceneCamSetup : Node
{
    [Export] private Node3D _pCamNode; // has a PhantomCamera3D node/script attached

    public override void _Ready()
    {
        var pCam = _pCamNode.AsPhantomCamera3D();
        pCam.TweenResource = PhantomCameraTween.New();
        pCam.TweenDuration = 1.5f;   // passthrough — writes TweenResource.Duration
        pCam.TweenTransition = TransitionType.Elastic;
        pCam.TweenEase = EaseType.EaseOut;
    }
}

tween_on_load: bool = true — if this PCam is already the highest-priority one when it's instantiated at runtime, it tweens the camera into place on load; set false to cut instantly instead. If tween_resource is null, get_tween_duration() returns 0.0 (instant cut).


Implementation checklist

  • Exactly one PhantomCameraHost per real Camera2D/Camera3D, added as its child
  • Every PhantomCamera2D/3D shares a host_layers bit with the Host that should track it
  • Priority changes use set_priority() or the priority property (both route through the setter, which notifies the Host)
  • follow_target / follow_targets assigned before relying on is_following()
  • GROUP follow mode uses follow_targets (array), not follow_target (single node)
  • 3D THIRD_PERSON rotation setters (set_third_person_rotation/_degrees/_quaternion) guard on follow_mode == THIRD_PERSON and no-op with a printed error otherwise — set_follow_distance/set_spring_length/set_collision_mask(_value)/set_shape have no such guard and print nothing
  • Combined follow_mode + look_at_mode on one PhantomCamera3D tested manually (addon marks this untested)
  • tween_resource shared deliberately (same .tres) when multiple PCams should transition identically
  • C# obtains wrappers via AsPhantomCamera2D()/AsPhantomCamera3D()/AsPhantomCameraHost()/AsPhantomCameraTween() — they are plain classes wrapping the node, not Node subclasses, so never class MyCam : PhantomCamera2D
  • C# enum names: only FollowMode/FollowLockAxis are dimension-suffixed (FollowMode2D, FollowLockAxis3D); LookAtMode, TransitionType, EaseType, InterpolationMode are not
  • FollowMode/LookAtMode are getter-only on the wrapper — set them via .Set("follow_mode", (int)FollowMode2D.Simple) on the underlying node
  • Pin the addon version in plugin.cfg/version control — pre-1.0, minor bumps can break API

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Use when working with 2D-specific systems — TileMaps, parallax scrolling, 2D lights and shadows, canvas layers, particles 2D, custom drawing, and 2D meshes in Godot 4.3+

日本語の概要は準備中です。原文の説明を表示しています。

jame581/GodotPrompter8122026年10月12日 更新

Use when working with 3D-specific systems — materials, lighting, shadows, environment, global illumination, fog, LOD, occlusion culling, and decals in Godot 4.3+

日本語の概要は準備中です。原文の説明を表示しています。

jame581/GodotPrompter8122026年10月12日 更新

Use when building character abilities — Resource-based abilities with cost/cooldown/cast, buffs/debuffs, stat modifiers, gameplay tags, and HUD binding

日本語の概要は準備中です。原文の説明を表示しています。

jame581/GodotPrompter8122026年10月12日 更新

Use when creating Godot editor plugins — EditorPlugin, @tool scripts, custom inspectors, and dock panels

日本語の概要は準備中です。原文の説明を表示しています。

jame581/GodotPrompter8122026年10月12日 更新

Use when implementing AI movement — NavigationAgent2D/3D, steering behaviors, behavior trees, and patrol patterns

日本語の概要は準備中です。原文の説明を表示しています。

jame581/GodotPrompter8122026年10月12日 更新

Use when implementing animations — AnimationPlayer, AnimationTree, blend trees, state machines, sprite animation, and code-driven animation

日本語の概要は準備中です。原文の説明を表示しています。

jame581/GodotPrompter8122026年10月12日 更新

jame581 のスキルをすべて見る

このスキルの問題を報告する