godot-camera-systems
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCamera Systems
相机系统
Expert guidance for creating smooth, responsive cameras in 2D and 3D games.
为2D和3D游戏创建流畅、响应式相机的专业指南。
NEVER Do
绝对不要做的事
- NEVER use every frame — Instant position matching causes jittery movement. Use
global_position = target.global_positionorlerp().position_smoothing_enabled = true - NEVER forget for Camera2D — Hard limits cause sudden stops at edges. Smoothing prevents jarring halts.
limit_smoothed = true - NEVER use offset for permanent camera positioning — is for shake/sway effects only. Use
offsetfor permanent camera placement.position - NEVER enable multiple Camera2D nodes simultaneously — Only one camera can be active. Others must have .
enabled = false - NEVER use SpringArm3D without collision mask — SpringArm clips through walls if is empty. Set to world layer
collision_mask
- 绝对不要每帧都使用——即时位置匹配会导致抖动。请使用
global_position = target.global_position或设置lerp()。position_smoothing_enabled = true - 绝对不要忘记为Camera2D设置——硬边界限制会导致相机在边缘突然停止。平滑处理可避免这种突兀的停顿。
limit_smoothed = true - 绝对不要用offset做永久相机定位——仅用于震动/摇摆效果。永久定位请使用
offset属性。position - 绝对不要同时启用多个Camera2D节点——同一时间只能有一个相机处于激活状态,其他相机必须设置。
enabled = false - 绝对不要在没有碰撞掩码的情况下使用SpringArm3D——如果为空,SpringArm会穿墙。请将其设置为对应世界层。
collision_mask
Available Scripts
可用脚本
MANDATORY: Read before implementing camera behaviors.
强制要求:在实现相机行为前请先阅读。
camera_follow_2d.gd
camera_follow_2d.gd
Smooth camera following with look-ahead prediction, deadzones, and boundary limits.
具备前瞻预测、死区和边界限制功能的平滑相机跟随脚本。
camera_shake_trauma.gd
camera_shake_trauma.gd
Trauma-based camera shake using Perlin noise - industry-standard screenshake implementation. Uses FastNoiseLite for smooth camera shake (squared trauma for feel) with automatic decay.
基于创伤系统的相机震动脚本,使用Perlin噪声——行业标准的屏幕震动实现方案。通过FastNoiseLite实现流畅的相机震动(采用平方创伤值提升手感),并支持自动衰减。
phantom_decoupling.gd
phantom_decoupling.gd
Phantom camera pattern: separates "where we look" from "what we follow". Camera follows phantom node, enabling cinematic offsets and area bounds.
虚拟相机模式:将“观察位置”与“跟随目标”分离。相机跟随虚拟节点,支持实现过场动画偏移和区域边界限制。
Camera2D Basics
Camera2D基础
gdscript
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
)gdscript
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
)Position Smoothing
位置平滑
gdscript
extends Camera2D
func _ready() -> void:
# Built-in smoothing
position_smoothing_enabled = true
position_smoothing_speed = 5.0gdscript
extends Camera2D
func _ready() -> void:
# 内置平滑功能
position_smoothing_enabled = true
position_smoothing_speed = 5.0Camera Limits
相机边界限制
gdscript
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 = truegdscript
extends Camera2D
func _ready() -> void:
# 将相机限制在关卡范围内
limit_left = 0
limit_top = 0
limit_right = 1920
limit_bottom = 1080
# 平滑触达边界
limit_smoothed = trueCamera Shake
相机震动
gdscript
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 = intensitygdscript
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 = intensityUsage:
使用示例:
$Camera2D.shake(10.0) # Screen shake on explosion
undefined$Camera2D.shake(10.0) # 爆炸时触发屏幕震动
undefinedZoom Controls
缩放控制
gdscript
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
)gdscript
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
)Look-Ahead Camera
前瞻相机
gdscript
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_aheadgdscript
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_aheadSplit-Screen (Multiple Cameras)
分屏(多相机)
gdscript
undefinedgdscript
undefinedPlayer 1 Camera
玩家1相机
@onready var cam1: Camera2D = $Player1/Camera2D
@onready var cam1: Camera2D = $Player1/Camera2D
Player 2 Camera
玩家2相机
@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
undefined@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
undefinedCamera3D Patterns
Camera3D模式
Third-Person Camera
第三人称相机
gdscript
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)gdscript
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)First-Person Camera
第一人称相机
gdscript
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 = pitchgdscript
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 = pitchCamera Transitions
相机过渡
gdscript
undefinedgdscript
undefinedSmooth 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)
undefinedfunc 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)
undefinedCinematic Cameras
过场动画相机
gdscript
undefinedgdscript
undefinedCamera 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
undefinedextends 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
undefinedBest Practices
最佳实践
1. One Active Camera
1. 单一激活相机
gdscript
undefinedgdscript
undefinedOnly one Camera2D should be enabled at a time
同一时间只能有一个Camera2D处于启用状态
Others should have enabled = false
其他相机需设置enabled = false
undefinedundefined2. Parent Camera to Player
2. 将相机作为玩家的子节点
gdscript
undefinedgdscript
undefinedScene structure:
场景结构:
Player (CharacterBody2D)
Player (CharacterBody2D)
└─ Camera2D
└─ Camera2D
undefinedundefined3. Use Anchors for UI
3. 为UI使用锚点
gdscript
undefinedgdscript
undefinedCamera doesn't affect UI positioned with anchors
使用锚点定位的UI不会受相机影响
UI stays in screen space
UI将保持在屏幕空间内
undefinedundefined4. Deadzone for Platformers
4. 平台游戏的死区设置
gdscript
extends Camera2D
func _ready() -> void:
drag_horizontal_enabled = true
drag_vertical_enabled = true
drag_left_margin = 0.3
drag_right_margin = 0.3gdscript
extends Camera2D
func _ready() -> void:
drag_horizontal_enabled = true
drag_vertical_enabled = true
drag_left_margin = 0.3
drag_right_margin = 0.3Reference
参考资料
Related
相关内容
- Master Skill: godot-master
- 核心技能:godot-master