天气
调用流程
1.Python中的 World.set_weather()
调用 client/World.cpp
中的World::SetWeather()
,PythonAPI具体天气参数定义在 PythonAPI/carla/source/libcarla/Weather.cpp 。
2.client/detail/Client.cpp 的 Client::SetWeatherParameters()
,调用 _pimpl->AsyncCall("set_weather_parameters", weather)
。
然后通过class Client::Pimpl
类的AsyncCall()
方法发起远程异步调用rpc::Client.async_call(function, std::forward<Args>(args) ...)
。
3.通过rpc/Client.h 的 async_call()
,最后使用rpc::client
库中的async_call()
进行远程调用服务端。具体天气类别位于 rpc/WeatherParameter.cpp 。
4.在Server/CarlaServer.cpp
中的BIND_SYNC(set_weather_parameters)
开始服务端调用。
5.在 Weather.cpp 中的 AWeather::ApplyWeather()
调用 void RefreshWeather(const FWeatherParameters &WeatherParameters)
调用真正改变天气的蓝图,方法实现位于Weather.h
中的
UFUNCTION(BlueprintImplementableEvent)
void RefreshWeather(const FWeatherParameters &WeatherParameters);
UFUNCTION(BlueprintImplementableEvent)
。
蓝图
BlueprintImplementableEvent 是 Unreal Engine 中的一种特殊的修饰符,用于在 C++ 代码中声明一个可以在 Blueprint 中实现的事件。这意味着你可以在 C++ 类中定义一个事件,并允许开发者在 Blueprint 中提供该事件的具体实现,而不需要编写 C++ 代码来实现该逻辑。这样就能够将游戏逻辑的实现与 C++ 代码解耦,使得逻辑的实现更加灵活,特别是在面向设计师的开发流程中非常有用。
笔记
解耦(Decoupling)是软件设计中的一个重要概念,指的是通过设计使得系统的不同模块或组件之间的依赖关系最小化,从而提高系统的灵活性、可扩展性和可维护性。解耦的目的是使得系统中的各个部分能够相对独立地变化、替换或扩展,而不需要修改其他部分的代码。在游戏开发和其他软件开发中,解耦通常是通过接口、抽象、事件和依赖注入等技术实现的。通过解耦,开发人员可调用。
BlueprintNativeEvent和BlueprintImplementableEvent区别在于: BlueprintNativeEvent多了个c++实现,可以同时调用c++和蓝图的函数; BlueprintImplementableEvent不能c++实现,只能调用蓝图函数。
代码概述
这段 C++ 代码定义了一个名为 AWeather 的类,它主要用于管理和应用天气效果。这个类提供了天气参数设置、后处理效果检查和应用、天气变化通知等功能,并且支持日夜循环状态的设置。以下是详细的说明文档。
类定义与成员变量
AWeather类
功能:管理和应用天气效果,包括后处理效果的检查和应用,以及天气参数的设置和通知。
构造函数:AWeather(const FObjectInitializer& ObjectInitializer)
功能:初始化类成员,查找并设置降水和沙尘暴的后处理材质,设置根组件和 Tick 函数的调用状态。
参数:ObjectInitializer - 用于初始化类成员的FObjectInitializer对象。 成员变量:
- PrecipitationPostProcessMaterial - 降水效果的后处理材质。
- DustStormPostProcessMaterial - 沙尘暴效果的后处理材质。
- ActiveBlendables - 存储当前激活的后处理材质和强度的元组列表。
- Weather - 当前的天气参数。
- DayNightCycle - 日夜循环状态。
成员函数
CheckWeatherPostProcessEffects() 功能:检查当前天气参数,根据降水和沙尘暴的强度,添加或移除相应的后处理材质,并将激活的后处理材质应用到场景中的所有传感器。
步骤:
检查降水强度,如果大于 0.0f,则将降水后处理材质和强度添加到ActiveBlendables列表中;否则,从列表中移除该材质。
检查沙尘暴强度,如果大于 0.0f,则将沙尘暴后处理材质和强度添加到ActiveBlendables列表中;否则,从列表中移除该材质。
获取场景中所有的ASceneCaptureCamera类型的传感器,并将激活的后处理材质和强度应用到每个传感器的后处理设置中。
ApplyWeather(const FWeatherParameters& InWeather) 功能:应用指定的天气参数,包括设置当前天气、检查并应用后处理效果、记录日志信息(如果定义了CARLA_WEATHER_EXTRA_LOG宏),并调用蓝图函数来刷新天气。
参数:InWeather - 要应用的天气参数。
步骤:
调用SetWeather函数设置当前天气参数。
调用CheckWeatherPostProcessEffects函数检查并应用后处理效果。
如果定义了CARLA_WEATHER_EXTRA_LOG宏,则记录当前天气参数的日志信息。
调用RefreshWeather函数刷新天气。
NotifyWeather(ASensor* Sensor) 功能:通知传感器天气相关的变化,包括检查并应用后处理效果和调用蓝图函数来刷新天气。
参数:Sensor - 要通知的传感器。
步骤:
调用CheckWeatherPostProcessEffects函数检查并应用后处理效果。
调用RefreshWeather函数刷新天气。
SetWeather(const FWeatherParameters& InWeather)
功能:设置当前的天气参数。
参数:InWeather - 要设置的天气参数。
SetDayNightCycle(const bool& active)
功能:设置日夜循环状态。
参数:active - 日夜循环的激活状态。
宏定义
CARLA_WEATHER_EXTRA_LOG - 如果定义了该宏,则在应用天气参数时会记录详细的日志信息。
使用示例
```cpp
// 创建AWeather对象
AWeather* WeatherActor = NewObject
// 创建天气参数对象 FWeatherParameters NewWeather; NewWeather.Precipitation = 50.0f; NewWeather.DustStorm = 20.0f;
// 应用天气参数 WeatherActor->ApplyWeather(NewWeather);
// 设置日夜循环状态 WeatherActor->SetDayNightCycle(true);
核心功能 1. 天气参数管理 数据结构:使用 FWeatherParameters 结构体存储当前天气状态(如降水、湿度、风速等)。 参数更新方法: ApplyWeather():更新参数并触发蓝图事件 RefreshWeather,通知场景元素响应变化。 SetWeather():静默更新参数,适用于无需立即触发效果的场景。 GetCurrentWeather():获取当前天气参数副本,用于读取状态。 2. 昼夜循环控制 启用/禁用:通过 SetDayNightCycle(bool) 动态切换昼夜循环逻辑,影响光照和天空盒表现。 状态查询:GetDayNightCycle() 返回当前昼夜循环是否激活。 3. 后期处理效果 材质管理:使用 PrecipitationPostProcessMaterial(降水)和 DustStormPostProcessMaterial(沙尘暴)实现天气视觉特效。 动态混合:ActiveBlendables 映射表跟踪活动材质及其混合强度,通过 CheckWeatherPostProcessEffects() 调整效果的显示强度,实现平滑过渡。 蓝图交互机制 1. 事件驱动更新 RefreshWeather 事件:在蓝图中实现该事件,响应天气变化(如调整粒子系统、修改光照参数)。当调用 ApplyWeather() 或 NotifyWeather() 时自动触发。 传感器通知:NotifyWeather(ASensor*) 允许特定传感器(如摄像头)订阅天气变化,用于触发截图或数据收集。 2. 参数暴露 UPROPERTY 标记:DayNightCycle 和 Weather 参数在编辑器中可见,支持设计师直接调整默认值或进行实时调试。 实现细节 1. 后期处理逻辑 材质混合:根据天气参数(如降水强度)计算材质的透明度,通过 ActiveBlendables 管理多个效果叠加。例如,大雨时增加降水材质的混合权重。 性能优化:仅在参数变化时调用 CheckWeatherPostProcessEffects(),避免每帧计算。 2. 抽象基类设计 不可实例化:标记为 Abstract 强制子类化,确保平台特定的天气效果(如 Vulkan 的着色器差异)可通过派生类实现。 扩展性:子类可覆盖 CheckWeatherPostProcessEffects() 实现自定义效果逻辑,同时复用基类的参数管理。
资源管理优化建议:
1.改为配置文件驱动(如INI文件或JSON)
2.使用UPROPERTY(EditAnywhere)暴露给编辑器
3.增加资源加载失败的安全检查
线程安全建议
对于多线程环境: 1.使用FThreadSafeBool保护天气参数
2.使用异步加载资源时添加锁机制 FCriticalSection WeatherCriticalSection;
- 避免硬编码材质路径,提升可维护性 问题: AWeather 类在构造函数中直接硬编码了两个后处理材质的路径(如 M_screenDrops 和 M_screenDust_wind)。这种硬编码方式存在以下缺陷:
路径依赖性强:若材质资源被移动、重命名或模块化拆分,需手动修改代码并重新编译。 扩展性差:新增天气效果时需修改源码,不符合开放-封闭原则。 解决方案: 通过 配置文件或动态资源加载 替代硬编码。例如:
在配置文件中定义材质路径(如 DefaultWeather.ini),运行时读取并加载资源。
使用 TSubclassOf
- 优化传感器后处理更新逻辑,减少冗余操作
问题:
1.CheckWeatherPostProcessEffects 函数在每次调用时会: 重复获取所有传感器:通过 UGameplayStatics::GetAllActorsOfClass 每次遍历场景查找 2.ASceneCaptureCamera,可能造成性能开销。 全量更新后处理材质:即使天气参数未变化,也会遍历所有传感器并重新添加/移除材质。
解决方案:
- 缓存传感器列表:在初始化时获取传感器并缓存,避免重复查找。
-
增量更新材质:记录当前生效的后处理材质,仅在天气参数变化时更新差异部分。
-
后处理材质管理优化
问题:后处理材质的添加/移除逻辑分散,且未考虑材质生命周期管理(如动态实例化)。
优化建议: 1. 材质实例化:使用CreateDynamicMaterialInstance()创建动态材质实例,独立控制不同传感器的参数。 2. 强度归一化封装:将/100.0f的归一化操作封装为函数(如GetNormalizedIntensity()),避免重复计算。 3. 错误处理:添加对材质加载失败的检查(如IsValid()),防止空指针访问。
- 日志与调试信息优化
问题:CARLA_WEATHER_EXTRA_LOG宏的日志输出格式固定,缺乏灵活性。
优化建议: 1. 结构化日志:使用FString::Printf生成带时间戳和上下文的日志,便于分析。 2. 动态日志级别:通过UE_LOG分类(如LogTemp、LogCarla)和Verbosity级别控制输出量。 3. 蓝图调试:暴露DebugWeatherParameters函数,允许在编辑器中实时查看天气状态。
- 线程安全与蓝图交互 问题:RefreshWeather()直接调用蓝图逻辑,未处理多线程场景下的竞态条件。
优化建议:
- 线程安全调用:对蓝图调用使用AsyncTask(ENamedThreads::GameThread)确保在主线程执行。
-
事件驱动通知:通过DECLARE_DYNAMIC_MULTICAST_DELEGATE定义天气变化事件,减少直接依赖。
-
性能敏感参数优化
问题:ActiveBlendables的频繁添加/移除可能引发内存碎片化。
优化建议: 1. 预分配内存:使用TArray::Reserve()预分配足够容量。 2. 对象池模式:对后处理材质对象池化,减少运行时动态分配。
- 扩展性改进 问题:天气类型(如降水、沙尘暴)硬编码,新增效果需修改源码。
优化建议:
- 数据驱动设计:定义FWeatherEffect结构体,通过数据表(如UDataTable)配置不同天气的材质和参数。
- 插件化架构:将天气效果拆分为独立插件(如WeatherEffectsModule),支持动态加载。
代码规范建议
头文件优化: 1. 使用#pragma once替代#include guards。 2. 移除未使用的头文件(如Kismet/GameplayStatics.h仅用于传感器查找,可替换为更轻量的查询方式)。 3. API注释:补充UFUNCTION和UCLASS元数据,说明蓝图可调用性和网络同步行为。
注意
确保在使用前正确初始化AWeather对象。 后处理材质的路径需要根据实际情况进行调整。 如果需要详细的日志信息,请定义CARLA_WEATHER_EXTRA_LOG宏。