8. 音效与视觉特效
2026/4/14大约 3 分钟
音效与视觉特效
皇牌空战的沉浸感很大程度来自音效:发动机的轰鸣随着油门变化、超音速时的音爆冲击、导弹击中时的爆炸声浪。本章实现让飞行体验栩栩如生的音效和视觉特效系统。
本章你将学到
- 发动机音效:随油门和速度动态变化
- 音速墙:突破音速(340m/s)时的视觉冲击波
- 导弹尾焰粒子特效
- 爆炸特效:飞机被击落的炫酷效果
- 机翼凝结尾迹(云迹)
发动机音效系统
发动机声音必须随油门、速度实时变化,才能有真实感:
GDScript
extends AudioStreamPlayer3D
## 发动机音效控制器
class_name EngineAudio
@export var min_pitch: float = 0.6 ## 慢车时的音调
@export var max_pitch: float = 1.6 ## 全油门时的音调
@export var afterburner_pitch: float = 2.0 ## 加力时的音调
@onready var aircraft: AircraftController = get_parent()
func _process(_delta: float) -> void:
if not aircraft:
return
var target_pitch: float
if aircraft.is_afterburner:
target_pitch = afterburner_pitch
else:
# 根据油门线性插值音调
target_pitch = lerp(min_pitch, max_pitch, aircraft.throttle)
# 平滑过渡,避免音调跳变
pitch_scale = lerp(pitch_scale, target_pitch, 0.1)
# 音量也随油门变化
volume_db = lerp(-10.0, 0.0, aircraft.throttle)C
using Godot;
public partial class EngineAudio : AudioStreamPlayer3D
{
[Export] public float MinPitch = 0.6f;
[Export] public float MaxPitch = 1.6f;
[Export] public float AfterburnerPitch = 2.0f;
private AircraftController _aircraft;
public override void _Ready()
{
_aircraft = GetParent<AircraftController>();
}
public override void _Process(double delta)
{
if (_aircraft == null) return;
float targetPitch = _aircraft.IsAfterburner
? AfterburnerPitch
: Mathf.Lerp(MinPitch, MaxPitch, _aircraft.Throttle);
PitchScale = Mathf.Lerp(PitchScale, targetPitch, 0.1f);
VolumeDb = Mathf.Lerp(-10f, 0f, _aircraft.Throttle);
}
}音速冲击波特效
当飞机速度超过340m/s(音速)时,产生锥形冲击波视觉效果:
GDScript
extends Node3D
## 音速冲击波特效
class_name SonicBoomEffect
## 音速(米/秒),实际游戏中可以调低让效果更频繁出现
const SOUND_SPEED := 340.0
@onready var cone_mesh: MeshInstance3D = $SonicConeMesh
@onready var aircraft: AircraftController = get_parent()
var is_supersonic: bool = false
func _process(delta: float) -> void:
var speed := aircraft.airspeed
var new_supersonic := speed > SOUND_SPEED
if new_supersonic != is_supersonic:
is_supersonic = new_supersonic
cone_mesh.visible = is_supersonic
if is_supersonic:
_play_sonic_boom_sound()
if is_supersonic:
# 锥角随马赫数变化(速度越快锥角越小)
var mach := speed / SOUND_SPEED
var half_angle := asin(1.0 / mach) ## 马赫锥半角
cone_mesh.scale = Vector3(1.0, tan(half_angle) * 10.0, tan(half_angle) * 10.0)
func _play_sonic_boom_sound() -> void:
pass ## TODO: 播放音爆音效C
using Godot;
public partial class SonicBoomEffect : Node3D
{
private const float SoundSpeed = 340f;
private MeshInstance3D _coneMesh;
private AircraftController _aircraft;
private bool _isSupersonic = false;
public override void _Ready()
{
_coneMesh = GetNode<MeshInstance3D>("SonicConeMesh");
_aircraft = GetParent<AircraftController>();
}
public override void _Process(double delta)
{
float speed = _aircraft.Airspeed;
bool newSupersonic = speed > SoundSpeed;
if (newSupersonic != _isSupersonic)
{
_isSupersonic = newSupersonic;
_coneMesh.Visible = _isSupersonic;
if (_isSupersonic) PlaySonicBoom();
}
if (_isSupersonic)
{
float mach = speed / SoundSpeed;
float halfAngle = Mathf.Asin(1f / mach);
float coneRadius = Mathf.Tan(halfAngle) * 10f;
_coneMesh.Scale = new Vector3(1f, coneRadius, coneRadius);
}
}
private void PlaySonicBoom() { /* TODO */ }
}爆炸特效
被击落时的爆炸效果是玩家最后看到的画面,要足够震撼:
节点结构:
ExplosionEffect (Node3D)
├── Fireball (GPUParticles3D) ← 火球(橙色/红色粒子)
├── Smoke (GPUParticles3D) ← 浓烟(黑色粒子)
├── Debris (GPUParticles3D) ← 碎片(灰色、棕色粒子)
├── ShockWave (MeshInstance3D) ← 冲击波环(快速扩大的圆环)
└── ExplosionSound (AudioStreamPlayer3D)机翼凝结尾迹
高速飞行时机翼尖端产生的白色水蒸气尾迹,是战斗机最标志性的视觉元素:
extends GPUParticles3D
## 凝结尾迹控制
class_name ContrailEffect
@export var min_altitude: float = 3000.0 ## 低于此高度不产生尾迹
@export var min_speed: float = 200.0 ## 低于此速度不产生尾迹
@onready var aircraft: AircraftController = get_parent().get_parent()
func _process(_delta: float) -> void:
## 只在高空高速时显示尾迹
var show := aircraft.altitude > min_altitude and aircraft.airspeed > min_speed
emitting = show下一步
音效和特效完成后,进入 打磨与发布。
