5. 天气系统
2026/4/14大约 4 分钟
天气系统
天气是飞行模拟器中最重要的挑战来源。顺风、逆风、侧风、湍流、低能见度降落——每一种天气都带来不同的飞行挑战。本章实现一个影响飞行物理的动态天气系统。
本章你将学到
- 风系统:风速风向如何影响飞机
- 湍流:随机颠簸的实现
- 云层与能见度:视觉和飞行的双重影响
- 天气动态变化:晴天→阴天→雨天的过渡
- 结冰:特定条件下飞机性能下降
风对飞行的影响
风是飞行中最基础也最重要的天气因素:
举例:飞机地速100km/h,正对20km/h顺风飞行,则空速=100-20=80km/h,升力减少;顶风飞行则空速=120km/h,升力增加,但地速减慢。
天气系统核心
GDScript
extends Node
## 天气系统 - 管理所有气象条件
class_name WeatherSystem
## 天气状态枚举
enum WeatherCondition { CLEAR, PARTLY_CLOUDY, OVERCAST, RAIN, STORM, FOG }
## 当前天气参数
var condition: WeatherCondition = WeatherCondition.CLEAR
var wind_direction: float = 270.0 ## 风从哪里来(度,270=从西来)
var wind_speed: float = 5.0 ## 风速(米/秒)
var turbulence_intensity: float = 0.0 ## 湍流强度(0~1)
var visibility: float = 10000.0 ## 能见度(米)
var cloud_base: float = 2000.0 ## 云底高度(米)
signal weather_changed(new_condition: WeatherCondition)
## 获取当前风速向量(3D空间中的风)
func get_wind_vector() -> Vector3:
var wind_rad := deg_to_rad(wind_direction)
## 风向:气象上说的"北风"是从北吹来,所以向量朝南
return Vector3(-sin(wind_rad), 0, -cos(wind_rad)) * wind_speed
## 获取随机湍流力(每帧调用,随机颠簸)
func get_turbulence_force(aircraft_mass: float) -> Vector3:
if turbulence_intensity <= 0:
return Vector3.ZERO
var t := Time.get_ticks_msec() / 1000.0
## 用多个不同频率的正弦波叠加,产生不规则颠簸感
var turbulence := Vector3(
sin(t * 3.7) * cos(t * 2.3),
sin(t * 2.1) * cos(t * 4.5) + sin(t * 7.3),
sin(t * 4.1) * cos(t * 1.7)
)
return turbulence * turbulence_intensity * aircraft_mass * 2.0
## 随时间推移改变天气
func change_weather(new_condition: WeatherCondition, transition_time: float = 300.0) -> void:
## TODO: 用Tween平滑过渡天气参数
condition = new_condition
_apply_condition_parameters(new_condition)
emit_signal("weather_changed", new_condition)
func _apply_condition_parameters(cond: WeatherCondition) -> void:
match cond:
WeatherCondition.CLEAR:
visibility = 10000.0
turbulence_intensity = 0.0
cloud_base = 3000.0
WeatherCondition.PARTLY_CLOUDY:
visibility = 8000.0
turbulence_intensity = 0.1
cloud_base = 2000.0
WeatherCondition.OVERCAST:
visibility = 5000.0
turbulence_intensity = 0.2
cloud_base = 1000.0
WeatherCondition.RAIN:
visibility = 3000.0
turbulence_intensity = 0.4
cloud_base = 800.0
wind_speed = maxf(wind_speed, 8.0)
WeatherCondition.STORM:
visibility = 500.0
turbulence_intensity = 0.9
cloud_base = 400.0
wind_speed = maxf(wind_speed, 20.0)
WeatherCondition.FOG:
visibility = 200.0
turbulence_intensity = 0.05
cloud_base = 50.0C
using Godot;
public partial class WeatherSystem : Node
{
public enum WeatherCondition { Clear, PartlyCloudy, Overcast, Rain, Storm, Fog }
public WeatherCondition Condition { get; private set; } = WeatherCondition.Clear;
public float WindDirection { get; set; } = 270f;
public float WindSpeed { get; set; } = 5f;
public float TurbulenceIntensity { get; private set; } = 0f;
public float Visibility { get; private set; } = 10000f;
public float CloudBase { get; private set; } = 2000f;
[Signal] public delegate void WeatherChangedEventHandler(int newCondition);
public Vector3 GetWindVector()
{
float windRad = Mathf.DegToRad(WindDirection);
return new Vector3(-Mathf.Sin(windRad), 0, -Mathf.Cos(windRad)) * WindSpeed;
}
public Vector3 GetTurbulenceForce(float aircraftMass)
{
if (TurbulenceIntensity <= 0) return Vector3.Zero;
float t = Time.GetTicksMsec() / 1000f;
var turbulence = new Vector3(
Mathf.Sin(t * 3.7f) * Mathf.Cos(t * 2.3f),
Mathf.Sin(t * 2.1f) * Mathf.Cos(t * 4.5f) + Mathf.Sin(t * 7.3f),
Mathf.Sin(t * 4.1f) * Mathf.Cos(t * 1.7f)
);
return turbulence * TurbulenceIntensity * aircraftMass * 2f;
}
public void ChangeWeather(WeatherCondition newCondition)
{
Condition = newCondition;
ApplyConditionParameters(newCondition);
EmitSignal(SignalName.WeatherChanged, (int)newCondition);
}
private void ApplyConditionParameters(WeatherCondition cond)
{
switch (cond)
{
case WeatherCondition.Clear:
Visibility = 10000f; TurbulenceIntensity = 0f; CloudBase = 3000f; break;
case WeatherCondition.Rain:
Visibility = 3000f; TurbulenceIntensity = 0.4f; CloudBase = 800f;
WindSpeed = Mathf.Max(WindSpeed, 8f); break;
case WeatherCondition.Storm:
Visibility = 500f; TurbulenceIntensity = 0.9f; CloudBase = 400f;
WindSpeed = Mathf.Max(WindSpeed, 20f); break;
case WeatherCondition.Fog:
Visibility = 200f; TurbulenceIntensity = 0.05f; CloudBase = 50f; break;
}
}
}将风和湍流应用到飞机
在飞行物理控制器中接入天气系统:
GDScript
## 在 RealisticFlightController 的 _physics_process 中添加:
@onready var weather: WeatherSystem = get_node("/root/WeatherSystem")
func _apply_weather_effects() -> void:
## 风:把风速向量加到飞机受到的外力中
var wind := weather.get_wind_vector()
## 注意:风影响的是"表观空速",而非直接施加力
## 简化处理:把风看作在飞机逆着飞行方向施加的额外气动力
var wind_effect := (wind - linear_velocity) * 0.01 * mass
apply_central_force(wind_effect)
## 湍流:直接施加随机力
var turbulence := weather.get_turbulence_force(mass)
apply_central_force(turbulence)C
// 在 RealisticFlightController._PhysicsProcess 中调用:
private WeatherSystem _weather;
public override void _Ready()
{
_weather = GetNode<WeatherSystem>("/root/WeatherSystem");
}
private void ApplyWeatherEffects()
{
var wind = _weather.GetWindVector();
var windEffect = (wind - LinearVelocity) * 0.01f * Mass;
ApplyCentralForce(windEffect);
var turbulence = _weather.GetTurbulenceForce(Mass);
ApplyCentralForce(turbulence);
}云层视觉效果
云层既是视觉元素,也是影响飞行的功能性元素——进入云中能见度骤降:
| 飞行位置 | 视觉效果 | 操作影响 |
|---|---|---|
| 云层以下 | 正常视野,可见云底 | 无影响 |
| 云层中 | 白雾笼罩,能见度<100m | 必须依靠仪表飞行 |
| 云层以上 | 晴朗蓝天,脚下云海 | 无法看到地面 |
下一步
天气系统完成后,实现 飞行建设系统——这是游戏的长线玩法核心。
