godot-camera-systems by thedivergentai/gd-agentic-skills
npx skills add https://github.com/thedivergentai/gd-agentic-skills --skill godot-camera-systems为创建流畅、响应灵敏的 2D 和 3D 游戏相机提供专家指导。
global_position = target.global_position — 瞬间位置匹配会导致抖动运动。请使用 lerp() 或 position_smoothing_enabled = true [12]。offset 进行永久性相机定位 — offset 仅用于抖动、摇晃或临时后坐力效果。对于永久性构图,请使用 position 以避免逻辑冲突 [14]。Camera2D,绝对不要忘记 limit_smoothed = true — 硬边界会导致突兀的视觉停顿。对边界进行平滑处理能确保专业感 [13]。广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
Camera2D 节点 — 只有最后启用的相机具有优先权。请明确禁用非活动相机 [15]。SpringArm3D — 它会穿透地形和墙壁。请将其设置为世界/环境层 [16]。position 来实现屏幕抖动 — 这会覆盖跟随逻辑。请使用 offset 或专用的创伤/噪声系统,将抖动效果叠加在跟随位置上 [27, 28]。RemoteTransform2D/3D 来获得稳定视图 [30]。look_at() 时,绝对不要没有针对 'Up' 向量的备用方案 — 如果目标正上方/正下方,相机会疯狂翻转。对于垂直跟踪,请使用防护措施或 Quaternion 数学。SubViewport 的默认设置来制作小地图 — 视口开销很大;请明确将 render_target_update_mode 设置为 UPDATE_WHEN_VISIBLE 或固定的较低帧率以节省 GPU [156]。TRANS_CUBIC 的 Tween 来获得更自然的战术感觉。必读:在实现相机行为前阅读。
基于噪声的高级屏幕抖动(创伤系统),用于实现有机、非抖动的爆炸和冲击效果。
代码中的三分法则和引导空间管理,确保高质量的影视构图。
使用 Tween 管理“跟随”、“静态”和“影视”相机状态之间的过渡。
针对小地图和 UI 叠加层的 SubViewport 优化。减少渲染更新以提高 FPS。
用于本地多人游戏的动态分屏架构,处理视口拉伸和音频监听器。
使用 RemoteTransform2D 将相机位置与玩家旋转/缩放解耦,以实现高速稳定性。
非线性、平滑的缩放逻辑,带有战术概览边界和鼠标滚轮支持。
使用弹簧质量插值的物理稳定 3D 跟随相机,以减少跟随延迟抖动。
程序化的 8 字形头部晃动和武器摇摆逻辑,用于沉浸式第一人称系统。
使用代码控制跟随边距和拖拽中心行为的平台游戏专用死区管理。
extends Camera2D
@export var target: Node2D
@export var follow_speed := 5.0
func _process(delta: float) -> void:
if target:
global_position = global_position.lerp(
target.global_position,
follow_speed * delta
)
extends Camera2D
func _ready() -> void:
# 内置平滑
position_smoothing_enabled = true
position_smoothing_speed = 5.0
extends Camera2D
func _ready() -> void:
# 将相机限制在关卡边界内
limit_left = 0
limit_top = 0
limit_right = 1920
limit_bottom = 1080
# 对边界进行平滑处理
limit_smoothed = true
extends Camera2D
var shake_amount := 0.0
var shake_decay := 5.0
func _process(delta: float) -> void:
if shake_amount > 0:
shake_amount = max(shake_amount - shake_decay * delta, 0)
offset = Vector2(
randf_range(-shake_amount, shake_amount),
randf_range(-shake_amount, shake_amount)
)
else:
offset = Vector2.ZERO
func shake(intensity: float) -> void:
shake_amount = intensity
# 用法:
$Camera2D.shake(10.0) # 爆炸时的屏幕抖动
extends Camera2D
@export var zoom_speed := 0.1
@export var min_zoom := 0.5
@export var max_zoom := 2.0
func _unhandled_input(event: InputEvent) -> void:
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_WHEEL_UP:
zoom_in()
elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
zoom_out()
func zoom_in() -> void:
zoom = zoom.move_toward(
Vector2.ONE * max_zoom,
zoom_speed
)
func zoom_out() -> void:
zoom = zoom.move_toward(
Vector2.ONE * min_zoom,
zoom_speed
)
extends Camera2D
@export var look_ahead_distance := 50.0
@export var target: CharacterBody2D
func _process(delta: float) -> void:
if target:
var look_ahead := target.velocity.normalized() * look_ahead_distance
global_position = target.global_position + look_ahead
# 玩家 1 相机
@onready var cam1: Camera2D = $Player1/Camera2D
# 玩家 2 相机
@onready var cam2: Camera2D = $Player2/Camera2D
func _ready() -> void:
# 分割视口
cam1.anchor_mode = Camera2D.ANCHOR_MODE_DRAG_CENTER
cam2.anchor_mode = Camera2D.ANCHOR_MODE_DRAG_CENTER
extends Camera3D
@export var target: Node3D
@export var distance := 5.0
@export var height := 2.0
@export var rotation_speed := 3.0
var rotation_angle := 0.0
func _process(delta: float) -> void:
if not target:
return
# 围绕目标旋转
rotation_angle += Input.get_axis("camera_left", "camera_right") * rotation_speed * delta
# 计算位置
var offset := Vector3(
sin(rotation_angle) * distance,
height,
cos(rotation_angle) * distance
)
global_position = target.global_position + offset
look_at(target.global_position, Vector3.UP)
extends Camera3D
@export var mouse_sensitivity := 0.002
@export var max_pitch := deg_to_rad(80)
var pitch := 0.0
func _ready() -> void:
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
func _input(event: InputEvent) -> void:
if event is InputEventMouseMotion:
# 偏航(水平)
get_parent().rotate_y(-event.relative.x * mouse_sensitivity)
# 俯仰(垂直)
pitch -= event.relative.y * mouse_sensitivity
pitch = clamp(pitch, -max_pitch, max_pitch)
rotation.x = pitch
# 平滑的相机位置变化
func move_to_position(target_pos: Vector2, duration: float = 1.0) -> void:
var tween := create_tween()
tween.tween_property(self, "global_position", target_pos, duration)
tween.set_ease(Tween.EASE_IN_OUT)
tween.set_trans(Tween.TRANS_CUBIC)
# 相机路径跟随
extends Path2D
@onready var path_follow: PathFollow2D = $PathFollow2D
@onready var camera: Camera2D = $PathFollow2D/Camera2D
func play_cutscene(duration: float) -> void:
var tween := create_tween()
tween.tween_property(path_follow, "progress_ratio", 1.0, duration)
await tween.finished
# 一次只应启用一个 Camera2D
# 其他相机应设置为 enabled = false
# 场景结构:
# Player (CharacterBody2D)
# └─ Camera2D
# 相机不影响使用锚点定位的 UI
# UI 保持在屏幕空间
extends Camera2D
func _ready() -> void:
drag_horizontal_enabled = true
drag_vertical_enabled = true
drag_left_margin = 0.3
drag_right_margin = 0.3
每周安装次数
86
代码仓库
GitHub 星标数
59
首次出现
2026年2月10日
安全审计
安装于
opencode84
codex83
gemini-cli83
kimi-cli81
github-copilot81
amp81
Expert guidance for creating smooth, responsive cameras in 2D and 3D games.
global_position = target.global_position every frame — Instant position matching causes jittery movement. Use lerp() or position_smoothing_enabled = true [12].offset for permanent camera positioning — offset is for shake, sway, or temporary recoil effects only. Use position for permanent framing to avoid logic conflicts [14].limit_smoothed = true for Camera2D — Hard boundaries cause jarring visual stops. Smoothing against limits ensures a professional feel [13].Camera2D nodes in the same viewport simultaneously — Only the last enabled camera takes precedence. Explicitly disable inactive cameras [15].SpringArm3D without a collision mask — It will clip through terrain and walls. Set it to the world/environment layer [16].position directly — This overwrites follow-logic. Use offset or a dedicated Trauma/Noise system to Layer shake over the follow-position [27, 28].RemoteTransform2D/3D with rotation sync disabled for a stable view [30].look_at() in 3D without a fallback for the 'Up' vector — If the target is directly above/below, the camera will flip wildly. Use guards or Quaternion math for vertical tracking.SubViewport defaults for Mini-maps — Viewports are expensive; explicitly set render_target_update_mode to UPDATE_WHEN_VISIBLE or a fixed lower framerate to save GPU [156].Tween with TRANS_CUBIC for a more natural tactical feel.MANDATORY : Read before implementing camera behaviors.
Advanced noise-based screenshake (Trauma system) for organic, non-jittery explosions and impacts.
Rule of Thirds and Lead Room management in code, ensuring high-quality cinematic composition.
Managing transitions between 'Follow', 'Static', and 'Cinematic' camera states with Tweens.
SubViewport optimization for Mini-maps and UI overlays. Reduces render updates for better FPS.
Dynamic split-screen architecture for local multiplayer, handling viewport stretching and audio listeners.
Decoupling camera position from player rotation/scale using RemoteTransform2D for high-speed stability.
Non-linear, smooth zoom logic with tactical overview bounds and mouse-wheel support.
Physics-stable 3D follow camera using spring-mass interpolation to reduce follow-latency jitter.
Procedural 8-figure head bob and weapon sway logic for immersive First-Person systems.
Platformer-specific deadzone management using code to control follow-margins and drag-center behavior.
extends Camera2D
@export var target: Node2D
@export var follow_speed := 5.0
func _process(delta: float) -> void:
if target:
global_position = global_position.lerp(
target.global_position,
follow_speed * delta
)
extends Camera2D
func _ready() -> void:
# Built-in smoothing
position_smoothing_enabled = true
position_smoothing_speed = 5.0
extends Camera2D
func _ready() -> void:
# Constrain camera to level bounds
limit_left = 0
limit_top = 0
limit_right = 1920
limit_bottom = 1080
# Smooth against limits
limit_smoothed = true
extends Camera2D
var shake_amount := 0.0
var shake_decay := 5.0
func _process(delta: float) -> void:
if shake_amount > 0:
shake_amount = max(shake_amount - shake_decay * delta, 0)
offset = Vector2(
randf_range(-shake_amount, shake_amount),
randf_range(-shake_amount, shake_amount)
)
else:
offset = Vector2.ZERO
func shake(intensity: float) -> void:
shake_amount = intensity
# Usage:
$Camera2D.shake(10.0) # Screen shake on explosion
extends Camera2D
@export var zoom_speed := 0.1
@export var min_zoom := 0.5
@export var max_zoom := 2.0
func _unhandled_input(event: InputEvent) -> void:
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_WHEEL_UP:
zoom_in()
elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
zoom_out()
func zoom_in() -> void:
zoom = zoom.move_toward(
Vector2.ONE * max_zoom,
zoom_speed
)
func zoom_out() -> void:
zoom = zoom.move_toward(
Vector2.ONE * min_zoom,
zoom_speed
)
extends Camera2D
@export var look_ahead_distance := 50.0
@export var target: CharacterBody2D
func _process(delta: float) -> void:
if target:
var look_ahead := target.velocity.normalized() * look_ahead_distance
global_position = target.global_position + look_ahead
# Player 1 Camera
@onready var cam1: Camera2D = $Player1/Camera2D
# Player 2 Camera
@onready var cam2: Camera2D = $Player2/Camera2D
func _ready() -> void:
# Split viewport
cam1.anchor_mode = Camera2D.ANCHOR_MODE_DRAG_CENTER
cam2.anchor_mode = Camera2D.ANCHOR_MODE_DRAG_CENTER
extends Camera3D
@export var target: Node3D
@export var distance := 5.0
@export var height := 2.0
@export var rotation_speed := 3.0
var rotation_angle := 0.0
func _process(delta: float) -> void:
if not target:
return
# Rotate around target
rotation_angle += Input.get_axis("camera_left", "camera_right") * rotation_speed * delta
# Calculate position
var offset := Vector3(
sin(rotation_angle) * distance,
height,
cos(rotation_angle) * distance
)
global_position = target.global_position + offset
look_at(target.global_position, Vector3.UP)
extends Camera3D
@export var mouse_sensitivity := 0.002
@export var max_pitch := deg_to_rad(80)
var pitch := 0.0
func _ready() -> void:
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
func _input(event: InputEvent) -> void:
if event is InputEventMouseMotion:
# Yaw (horizontal)
get_parent().rotate_y(-event.relative.x * mouse_sensitivity)
# Pitch (vertical)
pitch -= event.relative.y * mouse_sensitivity
pitch = clamp(pitch, -max_pitch, max_pitch)
rotation.x = pitch
# Smooth camera position change
func move_to_position(target_pos: Vector2, duration: float = 1.0) -> void:
var tween := create_tween()
tween.tween_property(self, "global_position", target_pos, duration)
tween.set_ease(Tween.EASE_IN_OUT)
tween.set_trans(Tween.TRANS_CUBIC)
# Camera path following
extends Path2D
@onready var path_follow: PathFollow2D = $PathFollow2D
@onready var camera: Camera2D = $PathFollow2D/Camera2D
func play_cutscene(duration: float) -> void:
var tween := create_tween()
tween.tween_property(path_follow, "progress_ratio", 1.0, duration)
await tween.finished
# Only one Camera2D should be enabled at a time
# Others should have enabled = false
# Scene structure:
# Player (CharacterBody2D)
# └─ Camera2D
# Camera doesn't affect UI positioned with anchors
# UI stays in screen space
extends Camera2D
func _ready() -> void:
drag_horizontal_enabled = true
drag_vertical_enabled = true
drag_left_margin = 0.3
drag_right_margin = 0.3
Weekly Installs
86
Repository
GitHub Stars
59
First Seen
Feb 10, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode84
codex83
gemini-cli83
kimi-cli81
github-copilot81
amp81
Go 数据结构内部原理与最佳实践:切片、映射、容器库选择指南
712 周安装