14、游戏手感与视觉打磨

第14章 游戏手感与视觉打磨

游戏手感(Juice)要素总览

复制代码
┌─────────────────────────────────────────────────────────┐
│                    游戏手感要素                          │
├──────────┬──────────┬──────────┬──────────┬──────────────┤
│  视觉反馈  │  音效反馈  │  触觉反馈  │  动画节奏  │  屏幕效果    │
├──────────┼──────────┼──────────┼──────────┼──────────────┤
│ 受击闪白  │ 随机音高  │ 手柄震动  │ 预备动作  │ 屏幕震动      │
│ 粒子特效  │ 打击音效  │ 顿帧     │ 跟随动作  │ 相机 Zoom     │
│ 击退动画  │ 环境音效  │          │ 弹性动画  │ 屏幕闪烁      │
│ 伤害数字  │          │          │          │ 暗角变化      │
└──────────┴──────────┴──────────┴──────────┴──────────────┘

打击感综合方案(执行顺序):

  攻击命中 → 顿帧(0.05s) → 屏幕震动 → 相机Zoom脉冲
       ↓
  受击闪白 → 伤害数字弹出 → 击退 → 粒子特效 → 音效

14.1 缓动曲线

gdscript 复制代码
# ease() 内置函数
var value := ease(progress, 0.5)  # ease_out
# 参数:
# < 0: 先慢后快(ease_in)
# = 0: 线性
# > 0: 先快后慢(ease_out)
# 2.0: 二次方 ease_out
# -2.0: 二次方 ease_in

# Tween 的 Trans 类型
# TRANS_LINEAR    线性
# TRANS_SINE      正弦
# TRANS_QUINT     五次方
# TRANS_QUART     四次方
# TRANS_QUAD      二次方
# TRANS_EXPO      指数
# TRANS_ELASTIC   弹性
# TRANS_CUBIC     三次方
# TRANS_CIRC      圆形
# TRANS_BOUNCE    弹跳
# TRANS_BACK      回弹

# 使用
var tween := create_tween()
tween.tween_property(self, "position", target_pos, 0.5) \
    .set_trans(Tween.TRANS_QUAD) \
    .set_ease(Tween.EASE_OUT)

14.2 屏幕过渡效果

gdscript 复制代码
# 淡入淡出
extends CanvasLayer

@onready var color_rect: ColorRect = $ColorRect

func fade_in(duration: float = 0.5) -> void:
    color_rect.color.a = 1.0
    var tween := create_tween()
    tween.tween_property(color_rect, "color:a", 0.0, duration)

func fade_out(duration: float = 0.5) -> void:
    color_rect.color.a = 0.0
    var tween := create_tween()
    tween.tween_property(color_rect, "color:a", 1.0, duration)
    await tween.finished

# 场景切换带过渡
func change_scene_with_fade(scene_path: String) -> void:
    await fade_out(0.5)
    get_tree().change_scene_to_file(scene_path)
    await get_tree().process_frame
    fade_in(0.5)

14.3 屏幕闪烁

gdscript 复制代码
extends CanvasLayer

@onready var flash_rect: ColorRect = $FlashRect

func flash(color: Color, duration: float = 0.1) -> void:
    flash_rect.color = color
    flash_rect.color.a = 0.7
    var tween := create_tween()
    tween.tween_property(flash_rect, "color:a", 0.0, duration)

# 受伤红闪
func damage_flash() -> void:
    flash(Color(1, 0, 0, 1), 0.15)

# 治疗绿闪
func heal_flash() -> void:
    flash(Color(0, 1, 0, 1), 0.2)

14.4 全屏后处理(Shader)

gdscript 复制代码
# 暗角效果
# 场景结构:
#   CanvasLayer
#     ColorRect (全屏,Mouse Filter=Ignore)
#       Material: ShaderMaterial

# Shader 代码(在 ShaderMaterial 中):
# shader_type canvas_item;
# uniform float vignette_strength : hint_range(0, 1) = 0.5;
# uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest;
# 
# void fragment() {
#     vec4 color = texture(screen_texture, UV);
#     float dist = distance(UV, vec2(0.5));
#     float vignette = smoothstep(0.4, 0.8, dist);
#     color.rgb *= 1.0 - vignette * vignette_strength;
#     COLOR = color;
# }

# 代码控制强度
func set_vignette_strength(strength: float) -> void:
    $VignetteRect.material.set_shader_parameter("vignette_strength", strength)

# 受伤时增强暗角
func _on_hurt() -> void:
    var tween := create_tween()
    tween.tween_method(set_vignette_strength, 0.8, 0.3, 0.5)

14.5 音效与视觉节奏同步

gdscript 复制代码
# 打击感综合方案
func on_attack_hit() -> void:
    # 1. 顿帧
    hitstop(0.05)
    
    # 2. 屏幕震动
    $Camera2D.shake(8.0, 0.15)
    
    # 3. 相机 zoom 脉冲
    $Camera2D.zoom_pulse(1.1, 0.1)
    
    # 4. 屏幕白闪
    $ScreenEffects.damage_flash()
    
    # 5. 受击闪白
    target.flash_white(0.1)
    
    # 6. 伤害数字
    show_damage_popup(damage, is_critical)
    
    # 7. 击退
    target.apply_knockback(direction, force)
    
    # 8. 音效(随机音高变化)
    $HitSound.pitch_scale = randf_range(0.8, 1.2)
    $HitSound.play()
    
    # 9. 粒子特效
    spawn_hit_particles(hit_position)

14.6 环境反馈

gdscript 复制代码
# 风粒子
extends GPUParticles2D

@export var wind_direction: Vector2 = Vector2(1, 0)
@export var wind_strength: float = 100.0

func _process(delta: float) -> void:
    var material: ParticleProcessMaterial = process_material
    material.direction = Vector3(wind_direction.x, wind_direction.y, 0)
    material.initial_velocity_min = wind_strength
    material.initial_velocity_max = wind_strength * 1.5

# 物体摆动(受风影响)
extends Sprite2D

@export var sway_amount: float = 3.0
@export var sway_speed: float = 2.0

func _process(delta: float) -> void:
    rotation_degrees = sin(Time.get_ticks_msec() * 0.001 * sway_speed) * sway_amount

实操练习

练习:打击感测试场景

gdscript 复制代码
# 创建一个测试场景,包含:
# 1. 一个可以攻击的假人
# 2. 完整的打击反馈(顿帧+震动+闪白+粒子+音效)
# 3. 伤害数字弹出
# 4. 连击系统
# 5. 屏幕后处理效果
相关推荐
寂寞的船19 小时前
【Unity知识速递】第13期 Unity 内存泄漏排查实战:从 Memory Profiler 到 Lua 引用泄漏的生产级全链路诊断
unity·junit·c#·游戏引擎·lua
寂寞的船2 天前
【Unity Bug挑战】第12期 Awaitable 二次 await 死锁之谜 —— 为什么同一个异步操作等两次就卡死?
unity·c#·游戏引擎·bug
Hello_Pyhx2 天前
Pavise v3.3.0.8 配置指南:从游戏识别到会话恢复
游戏
探求虚无3 天前
用 AI 在 Unity 里做一款 RTS:最难的不是生成代码,而是证明它真的能玩
人工智能·unity·游戏引擎
johnsong4 天前
Claude Fable 5.2 登顶:65 分、单发做游戏,和一份 $26.54 一单的账单
人工智能·游戏
寂寞的船5 天前
【Unity知识速递】第10期 Unity GC 零分配实战:从 UnityEngine.Pool 到手动 GC 控制的全链路优化
jvm·unity·c#·游戏引擎·lucene
shadowcz0075 天前
屿北:站在新旧时代交接处,赌一场AI原生无限游戏#Infinite Talk
游戏·ai-native
Martina_03215 天前
AI生成3D场景的霓虹灯一开就糊成光团?用6步检查Emissive、Bloom与曝光
人工智能·3d·ai·自然语言处理·游戏引擎·材质·贴图
又过二十7 天前
结合Godot八向图实现网页角色设计器测试
ai·aigc·godot·game·qwen-image·游戏素材
寂寞的船7 天前
【Unity知识速递】第8期 IL2CPP 托管堆内存碎片与原生堆交互机制深度解析
unity·游戏引擎·交互