UE5开启了抢先体验版,我也入了坑。
在开启汽车之旅之前,我先唠叨些,使用UE5相关的问题,做以记录。
先说说,UE5最为亮眼的两大新特性:Lumen 和 Nanite。
Lumen:崭新的实时全局光照。
Lumen是一套动态全局光照技术,Lumen可以实现实时光线反弹,在Lumen帮助下可以包含多次反弹的全局光照,没有光照贴图并无需烘焙,启用Lumen之后,只要移动光源,光线反弹效果会跟着实时变化。
Lumen能够对场景和光照变化做出实时反应,且无需专门的光线追踪硬件。Lumen能在任何场景中渲染间接镜面反射,也可以无限反弹的漫反射。
这就意味着我们可以使用Lumen创建出更动态的场景,我们可以随意改变白天或者晚上的光照角度,系统会根据情况调整间接光照。Lumen的出现将为美术与设计行业的工作人员节省了大量时间。
Lumen很牛逼,但是在使用时候也是存在一些问题的。
我在做草地,双面渲染的时候,背面特别暗,导致在UE4中效果,在UE5中显示不出来。究其原因,Lumen不支持植物透光…
在项目中,使用了Lumen,自己加环境光和自发光板,不会补光,导致光线反射处出现噪点,简单的补光都搞不定,真是当不上技美了…
Nanite:多边形虚拟化的技术。
一个虚拟场景是由很多模型构成的,而这些模型的基础是三角形,所以三角形的数量通常都非常的多。这造成了一个问题:由于三角形的数量非常多,但是我们所能看到的三角形数量非常的少。这使得我们需要把很多不必要的三角形剔除,增加渲染的效率并节省时间。
而Nanite技术可以虚拟化几何体,Nanite能极快的渲染超多的三角面,并且能够将很多的三角面无损压缩成很少。Nanite能够展示像素级别的细节,这使得几何体中的三角形也常是像素大小的,这个级别的几何体细节也要求阴影能够精确到像素。
做过测试,将百万级三角面的模型,导入到工程,将模型拖到场景中进行显示,三角面骤降几千面,不得不承认,UE的强大,这一技术真实工业级模型的福音~但是讲百万级的三角面的模型导入工程时,UE5崩溃了3、4次,这个代价还是完全可以接受的。
还有相关的其他的更新。
有些功能,我还没使用过,这里不做过多评论了。
无论是安装版的还是源码版的UE5,默认引擎会把生成的缓存文件寄存在该目录下,该目录默认在系统盘中,久而久之会积累巨大从而严重影响运行速度。所以我们将这个目录指向项目目录下。
UE默认的项目缓存文件夹
C:\Users\你的用户名\AppData\Local\UnrealEngine
修改缓存文件夹路径
在UE安装目录下找到BaseEngine.ini文件
搜索
%ENGINEVERSIONAGNOSTICUSERDIR%DerivedDataCache
替换
%GAMEDIR%DerivedDataCache
这个没啥好办法解决,我是UE和VS都是使用的英文版的…
如果你使用的是源码版的,可以修改源码。
在工程中查找文件 Engine\Source\Developer\DesktopPlatform\Private\Windows\DesktopPlatformWindows.cpp
查找函数
FString FWindowsPlatformProcess::ReadPipe( void* ReadPipe )
将
Output += FUTF8ToTCHAR((const ANSICHAR*)Buffer).Get();
修改为
Output += FString(string2wstring(std::string((const ANSICHAR*)Buffer), "Chinese_China.936").c_str());
编译UE5,会使用你120多G的硬盘空间。并且几个小时的时间。
因为要编写UE服务端,所以需要UE5的源码版开发。
我在编译后,修改过UE5的路径,导致再次编译又花费了几个小时,并且编译最后出现了下面的错误。
Severity Code Description Project File Line Suppression State
Error MSB3073 The command "..\..\Build\BatchFiles\Build.bat -Target="UnrealEditor Win64 DebugGame" -Target="ShaderCompileWorker Win64 Development -Quiet" -WaitMutex -FromMsBuild" exited with code 6. UE5 C:\Develop\Microsoft Visual Studio\2019\Enterprise\MSBuild\Microsoft\VC\v160\Microsoft.MakeFile.Targets 44
解决办法,删除目录
Engine\Intermediate\Build\Win64\UE4Editor\Development\VisualStudioDTE
重新 Build 一下就可以了。
创建工程,这里我们创建一个C++移动端空工程,命名为UEVehicleProject。
添加"PhysXVehicles"模块,这里UE5是必须的,在UE4是可以不用添加的。
使用VS2019打开UEVehicleProject.sln。
打开UEVehicleProject.Build.cs文件。添加"PhysXVehicles"模块。
...
public UEVehicleProject(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "PhysXVehicles" });
...
}
...
打开UEVehicleProject.uproject文件。添加"PhysXVehicles"模块。
{
"FileVersion": 3,
"EngineAssociation": "5.0",
"Category": "",
"Description": "",
"Modules": [
{
"Name": "UEVehicleProject",
"Type": "Runtime",
"LoadingPhase": "Default"
}
],
"Plugins": [
{
"Name": "PhysXVehicles",
"Enabled": true
},
{
"Name": "Bridge",
"Enabled": true,
"SupportedTargetPlatforms": [
"Win64",
"Mac",
"Linux"
]
}
]
}
当然,也可以在UE5工程中添加"PhysXVehicles"模块。
先说一下,该篇博客提交的汽车基础交互都要做什么。都是为以后驾驶交互和人车交互打基础。
编写代码之前,需要先讲一下汽车的骨骼和动画蓝图。
汽车的骨骼,汽车前后车轮的骨骼节点是必须的,因为行驶时,以完成车轮的转动、转向需要,轱辘内的刹车片和左右车门,是根据需求添加的,要看都想完成什么样的操作。
再看一下汽车的蓝图动画,来控制车门的开关。
了解了要实现的功能和规则,我们来逐渐完善ABaseVehicle、UFrontWheel、URearWheel类,本篇博客只涉及到ABaseVehicle类的编写。
最终,我们会搞一个蓝图类BP_SUV,来继承C++类ABaseVehicle。指定车体模型,微调车灯、方向盘、仪表盘、尾气特效…等组件的位置。来完成不同车型(轿车、吉普、SUV…)的实现。这里以一辆SUV为例。
汽车灯光交互-前后灯控制 展示:
汽车灯光交互-刹车灯控制 展示:
汽车灯光交互-倒车灯控制 展示:
现在我们开始正式编码。
首先看一下ABaseVehicle 的构造函数。
...
ABaseVehicle::ABaseVehicle()
{
InitBaseValue(); // 初始化 基础 数值
InitBaseMesh(); // 初始化 基础 模型
InitCameraComponent(); // 初始化 摄像机 组件
InitLightComponent(); // 初始化 灯光 组件
InitBrakeSystemComponent(); // 初始化 刹车系统 组件
InitSteeringWheelComponent(); // 初始化 方向盘 组件
InitExhaustComponent(); // 初始化 尾气 组件
InitDashboardComponent(); // 初始化 仪表盘 组件
InitSoundComponent(); // 初始化 声音 组件
InitVehicleMovementComponent(); // 初始化 轮胎系统 组件
InitDoorComponent(); // 初始化 车门 组件
InitColorComponent(); // 初始化 颜色 组件
}
...
这里我们灯光的交互,所以我们只关注InitBaseMesh()和InitLightComponent()函数,后续只要是涉及到的部分都会进行讲解,稍安勿躁。
BaseVehicle.h 中相关模型、车灯的定义。
...
UCLASS()
class UEVEHICLEPROJECT_API ABaseVehicle : public AWheeledVehicle
{
GENERATED_BODY()
public:
ABaseVehicle();
...
// 车灯控制
UFUNCTION(BlueprintCallable, Category = "VehicleFunc")
void VehicleLights(bool position_lights, bool brake_lights, bool reverse_lights);
...
protected:
virtual void BeginPlay() override;
...
void InitBaseMesh(); // 初始化 基础 模型
...
void InitLightComponent(); // 初始化 灯光 组件
...
protected:
// 骨骼模型
UPROPERTY(VisibleAnywhere, Category = "BaseMesh", meta = (AllowPrivateAccess = "true"))
USkeletalMeshComponent* VehicleMesh;
...
// 刹车灯
UPROPERTY(VisibleAnywhere, Category = "Light", meta = (AllowPrivateAccess = "true"))
USpotLightComponent* BrakeLightL;
UPROPERTY(VisibleAnywhere, Category = "Light", meta = (AllowPrivateAccess = "true"))
USpotLightComponent* BrakeLightR;
// 倒车灯
UPROPERTY(VisibleAnywhere, Category = "Light", meta = (AllowPrivateAccess = "true"))
USpotLightComponent* ReverseLightL;
UPROPERTY(VisibleAnywhere, Category = "Light", meta = (AllowPrivateAccess = "true"))
USpotLightComponent* ReverseLightR;
// 前灯
UPROPERTY(VisibleAnywhere, Category = "Light", meta = (AllowPrivateAccess = "true"))
USpotLightComponent* FrontLightL;
UPROPERTY(VisibleAnywhere, Category = "Light", meta = (AllowPrivateAccess = "true"))
USpotLightComponent* FrontLightR;
// 尾灯
UPROPERTY(VisibleAnywhere, Category = "Light", meta = (AllowPrivateAccess = "true"))
USpotLightComponent* RearLightL;
UPROPERTY(VisibleAnywhere, Category = "Light", meta = (AllowPrivateAccess = "true"))
USpotLightComponent* RearLightR;
...
// 刹车灯材质
int BrakeLightMaterialID;
UMaterialInstance* BrakeLightMaterialOff;
UMaterialInstance* BrakeLightMaterialOn;
// 倒车灯材质
int ReverseLightMaterialID;
UMaterialInstance* ReverseLightMaterialOff;
UMaterialInstance* ReverseLightMaterialOn;
// 前灯材质
int FrontLightMaterialID;
UMaterialInstance* FrontLightMaterialOff;
UMaterialInstance* FrontLightMaterialOn;
// 尾灯材质
int RearLightMaterialID;
UMaterialInstance* RearLightMaterialOff;
UMaterialInstance* RearLightMaterialOn;
bool PositionLights;
...
// 动画蓝图
UObject* VehicleAnimBP;
...
};
BaseVehicle.cpp 相关的实现。
InitBaseMesh()函数,模型和动画蓝图初始化。
void ABaseVehicle::InitBaseMesh()
{
VehicleMesh = GetMesh();
static ConstructorHelpers::FObjectFinder<USkeletalMesh> sm_vehicle(TEXT("/Game/Meshs/Hatchback/Hatchback.Hatchback"));
VehicleMesh->SetSkeletalMesh(sm_vehicle.Object);
// 设置动画蓝图
static ConstructorHelpers::FObjectFinder<UBlueprint> vehicleAminBP(TEXT("/Game/Animations/AnimBlueprint_vehicle.AnimBlueprint_vehicle"));
FString strVehicleAnimBP = "Class'/Game/Animations/AnimBlueprint_vehicle.AnimBlueprint_vehicle'";
VehicleAnimBP = vehicleAminBP.Object->GeneratedClass;
VehicleMesh->SetAnimInstanceClass((UClass*)VehicleAnimBP);
}
InitLightComponent()函数,灯光组件的初始化。
void ABaseVehicle::InitLightComponent()
{
// 记录车灯是否打开,使其不受刹车、倒车影响
// 本再InitBaseValue() 函数中初始化,简化讲解写在这里。
PositionLights = false;
///
// 刹车灯
BrakeLightL = CreateDefaultSubobject<USpotLightComponent>(TEXT("BrakeLightL"));
// 大体位置、角度,不同类型车 蓝图调整位置
BrakeLightL->SetRelativeLocation(FVector(-199.0f, 72.0f, 95.0f));
BrakeLightL->SetRelativeRotation(FRotator(0.0f, 160.0f, 0.0f));
BrakeLightL->Mobility = EComponentMobility::Movable; // 可移动
// 光的设置
BrakeLightL->Intensity = 25.0f; // 强调
BrakeLightL->LightColor = FColor(255, 0, 0); // 红色
BrakeLightL->AttenuationRadius = 495.0f; // 影响半径
BrakeLightL->InnerConeAngle = 0.f; // 聚光源的内锥角
BrakeLightL->OuterConeAngle = 90.f; // 聚光源的外锥角
BrakeLightL->SetupAttachment(VehicleMesh);
BrakeLightR = CreateDefaultSubobject<USpotLightComponent>(TEXT("BrakeLightR"));
// 大体位置、角度,不同类型车 蓝图调整位置
BrakeLightR->SetRelativeLocation(FVector(-199.0f, -72.0f, 95.0f));
BrakeLightR->SetRelativeRotation(FRotator(0.0f, -160.0f, 0.0f));
BrakeLightR->Mobility = EComponentMobility::Movable; // 可移动
// 光的设置
BrakeLightR->Intensity = 25.0f; // 强调
BrakeLightR->LightColor = FColor(255, 0, 0); // 红色
BrakeLightR->AttenuationRadius = 495.0f; // 影响半径
BrakeLightR->InnerConeAngle = 0.f; // 聚光源的内锥角
BrakeLightR->OuterConeAngle = 90.f; // 聚光源的外锥角
BrakeLightR->SetupAttachment(VehicleMesh);
///
// 倒车灯
ReverseLightL = CreateDefaultSubobject<USpotLightComponent>(TEXT("ReverseLightL"));
// 大体位置、角度,不同类型车 蓝图调整位置
ReverseLightL->SetRelativeLocation(FVector(-203.0f, -60.0f, 93.0f));
ReverseLightL->SetRelativeRotation(FRotator(0.0f, 180.0f, 0.0f));
ReverseLightL->Mobility = EComponentMobility::Movable; // 可移动
// 光的设置
ReverseLightL->Intensity = 25.0f; // 强调
ReverseLightL->LightColor = FColor(255, 255, 255); // 白色
ReverseLightL->AttenuationRadius = 495.0f; // 影响半径
ReverseLightL->InnerConeAngle = 0.f; // 聚光源的内锥角
ReverseLightL->OuterConeAngle = 90.f; // 聚光源的外锥角
ReverseLightL->SetupAttachment(VehicleMesh);
ReverseLightR = CreateDefaultSubobject<USpotLightComponent>(TEXT("ReverseLightR"));
// 大体位置、角度,不同类型车 蓝图调整位置
ReverseLightR->SetRelativeLocation(FVector(-203.0f, 60.0f, 93.0f));
ReverseLightR->SetRelativeRotation(FRotator(0.0f, 180.0f, 0.0f));
ReverseLightR->Mobility = EComponentMobility::Movable; // 可移动
// 光的设置
ReverseLightR->Intensity = 25.0f; // 强调
ReverseLightR->LightColor = FColor(255, 255, 255); // 白色
ReverseLightR->AttenuationRadius = 495.0f; // 影响半径
ReverseLightR->InnerConeAngle = 0.f; // 聚光源的内锥角
ReverseLightR->OuterConeAngle = 90.f; // 聚光源的外锥角
ReverseLightR->SetupAttachment(VehicleMesh);
///
// 前灯
FrontLightL = CreateDefaultSubobject<USpotLightComponent>(TEXT("FrontLightL"));
// 大体位置、角度,不同类型车 蓝图调整位置
FrontLightL->SetRelativeLocation(FVector(197.0f, -70.0f, 69.0f));
FrontLightL->SetRelativeRotation(FRotator(0.0f, 0.0f, 0.0f));
FrontLightL->Mobility = EComponentMobility::Movable; // 可移动
// 光的设置
FrontLightL->Intensity = 100000.0f; // 强调
FrontLightL->LightColor = FColor(255, 255, 255); // 白色
FrontLightL->AttenuationRadius = 5000.0f; // 影响半径
FrontLightL->InnerConeAngle = 0.f; // 聚光源的内锥角
FrontLightL->OuterConeAngle = 50.f; // 聚光源的外锥角
FrontLightL->SetupAttachment(VehicleMesh);
FrontLightR = CreateDefaultSubobject<USpotLightComponent>(TEXT("FrontLightR"));
// 大体位置、角度,不同类型车 蓝图调整位置
FrontLightR->SetRelativeLocation(FVector(197.0f, 70.0f, 69.0f));
FrontLightR->SetRelativeRotation(FRotator(0.0f, 0.0f, 0.0f));
FrontLightR->Mobility = EComponentMobility::Movable; // 可移动
// 光的设置
FrontLightR->Intensity = 100000.0f; // 强调
FrontLightR->LightColor = FColor(255, 255, 255); // 白色
FrontLightR->AttenuationRadius = 5000.0f; // 影响半径
FrontLightR->InnerConeAngle = 0.f; // 聚光源的内锥角
FrontLightR->OuterConeAngle = 50.f; // 聚光源的外锥角
FrontLightR->SetupAttachment(VehicleMesh);
///
// 尾灯
RearLightL = CreateDefaultSubobject<USpotLightComponent>(TEXT("RearLightL"));
// 大体位置、角度,不同类型车 蓝图调整位置
RearLightL->SetRelativeLocation(FVector(-203.0f, -70.0f, 90.0f));
RearLightL->SetRelativeRotation(FRotator(0.0f, 180.0f, 0.0f));
RearLightL->Mobility = EComponentMobility::Movable; // 可移动
// 光的设置
RearLightL->Intensity = 15.0f; // 强调
RearLightL->LightColor = FColor(255, 0, 0); // 红色
RearLightL->AttenuationRadius = 495.0f; // 影响半径
RearLightL->InnerConeAngle = 0.f; // 聚光源的内锥角
RearLightL->OuterConeAngle = 90.f; // 聚光源的外锥角
RearLightL->SetupAttachment(VehicleMesh);
RearLightR = CreateDefaultSubobject<USpotLightComponent>(TEXT("RearLightR"));
// 大体位置、角度,不同类型车 蓝图调整位置
RearLightR->SetRelativeLocation(FVector(-203.0f, 70.0f, 90.0f));
RearLightR->SetRelativeRotation(FRotator(0.0f, 180.0f, 0.0f));
RearLightR->Mobility = EComponentMobility::Movable; // 可移动
// 光的设置
RearLightR->Intensity = 15.0f; // 强调
RearLightR->LightColor = FColor(255, 0, 0); // 红色
RearLightR->AttenuationRadius = 495.0f; // 影响半径
RearLightR->InnerConeAngle = 0.f; // 聚光源的内锥角
RearLightR->OuterConeAngle = 90.f; // 聚光源的外锥角
RearLightR->SetupAttachment(VehicleMesh);
/* ---------------------------------------------------------------------------------------------------------------------- */
// 灯光材质设置
///
// 刹车灯材质
static ConstructorHelpers::FObjectFinder<UMaterialInstance> temp_red(TEXT("/Game/Materials/Vehicle/Material_Instances/MI_Reflector_RED.MI_Reflector_RED"));
static ConstructorHelpers::FObjectFinder<UMaterialInstance> temp_red_emmisive(TEXT("/Game/Materials/Vehicle/Material_Instances/MI_Reflector_RED_Emmisive.MI_Reflector_RED_Emmisive"));
static ConstructorHelpers::FObjectFinder<UMaterialInstance> temp_grey(TEXT("/Game/Materials/Vehicle/Material_Instances/MI_Reflector_Grey.MI_Reflector_Grey"));
static ConstructorHelpers::FObjectFinder<UMaterialInstance> temp_grey_emmisive(TEXT("/Game/Materials/Vehicle/Material_Instances/MI_Reflector_Grey_Emmisive.MI_Reflector_Grey_Emmisive"));
BrakeLightMaterialID = 12;
if (temp_red.Succeeded() == false)
BrakeLightMaterialOff = nullptr;
else
BrakeLightMaterialOff = temp_red.Object;
//BrakeLightMaterialOff = UMaterialInstanceDynamic::Create(temp_red.Object, nullptr); //创建动态材质实例
if (temp_red_emmisive.Succeeded() == false)
BrakeLightMaterialOn = nullptr;
else
BrakeLightMaterialOn = temp_red_emmisive.Object;
//BrakeLightMaterialOn = UMaterialInstanceDynamic::Create(temp_red_emmisive.Object, nullptr); //创建动态材质实例
///
// 倒车灯材质
ReverseLightMaterialID = 10;
if (temp_grey.Succeeded() == false)
ReverseLightMaterialOff = nullptr;
else
ReverseLightMaterialOff = temp_grey.Object;
//ReverseLightMaterialOff = UMaterialInstanceDynamic::Create(temp_grey.Object, nullptr); //创建动态材质实例
if (temp_grey_emmisive.Succeeded() == false)
ReverseLightMaterialOn = nullptr;
else
ReverseLightMaterialOn = temp_grey_emmisive.Object;
//ReverseLightMaterialOn = UMaterialInstanceDynamic::Create(temp_grey_emmisive.Object, nullptr); //创建动态材质实例
///
// 前灯材质
FrontLightMaterialID = 9;
if (temp_grey.Succeeded() == false)
FrontLightMaterialOff = nullptr;
else
FrontLightMaterialOff = temp_grey.Object;
//FrontLightMaterialOff = UMaterialInstanceDynamic::Create(temp_grey.Object, nullptr); //创建动态材质实例
if (temp_grey_emmisive.Succeeded() == false)
FrontLightMaterialOn = nullptr;
else
FrontLightMaterialOn = temp_grey_emmisive.Object;
//FrontLightMaterialOn = UMaterialInstanceDynamic::Create(temp_grey_emmisive.Object, nullptr); //创建动态材质实例
///
// 尾灯材质
RearLightMaterialID = 11;
if (temp_red.Succeeded() == false)
RearLightMaterialOff = nullptr;
else
RearLightMaterialOff = temp_red.Object;
//RearLightMaterialOff = UMaterialInstanceDynamic::Create(temp_red.Object, nullptr); //创建动态材质实例
//static UMaterialInterface* temp_rearmaterial_on = LoadObject(nullptr, TEXT("/Game/Materials/Material_Instances/MI_Reflector_RED_Emmisive_low.MI_Reflector_RED_Emmisive_low"));
//if (temp_rearmaterial_on == nullptr)
if (temp_grey_emmisive.Succeeded() == false)
RearLightMaterialOn = nullptr;
else
RearLightMaterialOn = temp_grey_emmisive.Object;
//RearLightMaterialOn = UMaterialInstanceDynamic::Create(temp_grey_emmisive.Object, nullptr); //创建动态材质实例
}
VehicleLights(bool position_lights, bool brake_lights, bool reverse_lights)函数,车灯控制函数。
void ABaseVehicle::VehicleLights(bool position_lights, bool brake_lights, bool reverse_lights)
{
PositionLights = position_lights;
UMaterialInstance* brakeLightMaterial = nullptr;
UMaterialInstance* reverseLightMaterial = nullptr;
UMaterialInstance* frontLightMaterial = nullptr;
UMaterialInstance* rearLightMaterial = nullptr;
if (position_lights)
{
rearLightMaterial = RearLightMaterialOn;
frontLightMaterial = FrontLightMaterialOn;
}
else
{
rearLightMaterial = RearLightMaterialOff;
frontLightMaterial = FrontLightMaterialOff;
}
if (reverse_lights)
{
reverseLightMaterial = ReverseLightMaterialOn;
}
else
{
reverseLightMaterial = ReverseLightMaterialOff;
}
if (brake_lights)
{
brakeLightMaterial = BrakeLightMaterialOn;
}
else
{
brakeLightMaterial = BrakeLightMaterialOff;
}
VehicleMesh->SetMaterial(RearLightMaterialID, rearLightMaterial);
VehicleMesh->SetMaterial(FrontLightMaterialID, frontLightMaterial);
VehicleMesh->SetMaterial(BrakeLightMaterialID, brakeLightMaterial);
VehicleMesh->SetMaterial(ReverseLightMaterialID, reverseLightMaterial);
FrontLightL->SetVisibility(position_lights);
FrontLightR->SetVisibility(position_lights);
RearLightL->SetVisibility(position_lights);
RearLightR->SetVisibility(position_lights);
BrakeLightL->SetVisibility(brake_lights);
BrakeLightR->SetVisibility(brake_lights);
RearLightL->SetVisibility(reverse_lights);
RearLightR->SetVisibility(reverse_lights);
}
BeginPlay()函数中,调用一下 VehicleLights()函数,关闭所有车灯。
void ABaseVehicle::BeginPlay()
{
Super::BeginPlay();
...
VehicleLights(false, false, false);
...
}
汽车车门交互-左右门控制 展示:
车门的交互,关注构造函数中的InitDoorComponent()函数。
BaseVehicle.h 中相关车门的定义。
...
UENUM(BlueprintType)
enum class EVehicleDoorType : uint8
{
EVDT_LeftDoor, // 左门
EVDT_RightDoor, // 右门
EVDT_Count
};
UENUM(BlueprintType)
enum class EControlDoorType : uint8
{
ECDT_OpenDoor, // 开门
ECDT_CloseDoor, // 关门
ECDT_Count
};
UCLASS()
class UEVEHICLEPROJECT_API ABaseVehicle : public AWheeledVehicle
{
GENERATED_BODY()
public:
ABaseVehicle();
...
// 车门控制
UFUNCTION(BlueprintCallable, Category = "VehicleFunc")
void VehicleDoorControl(EVehicleDoorType door_type, EControlDoorType door_control);
// 车门过程控制 需蓝图实现
UFUNCTION(BlueprintImplementableEvent)
void VehicleDoorProcess(EVehicleDoorType door_type, float door_alpha, UObject* anim_bp);
...
protected:
...
virtual void Tick(float DeltaTime) override;
...
UFUNCTION()
void OpenDoorTimeLineCallBack(float interpolatedVal);
UFUNCTION()
void OpenDoorTimelineFinishedCallback();
UFUNCTION()
void CloseDoorTimeLineCallBack(float interpolatedVal);
UFUNCTION()
void CloseDoorTimelineFinishedCallback();
UFUNCTION()
...
void InitDoorComponent(); // 初始化 车门 组件
...
protected:
...
// 开关门控制
// 声明TimeLine的CurveFloat(也即函数曲线)
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Door", meta = (AllowPrivateAccess = "true"))
UCurveFloat* OpenDoorTimeLineCurve;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Door", meta = (AllowPrivateAccess = "true"))
...
UCurveFloat* CloseDoorTimeLineCurve;
// 车门控制
FTimeline OpenDoorTimeLine;
FTimeline CloseDoorTimeLine;
EVehicleDoorType VehicleDoorType;
...
};
InitDoorComponent()函数,车门组件的初始化。
控制车门开关的实现,是用TimeLine来控制动画蓝图实现的,而开关门的动画曲线可以控制开关门的平滑过渡,只是功能的实现,对曲线也没做处理。
void ABaseVehicle::InitDoorComponent()
{
FOnTimelineFloat OpenDoorTimeLineCallBack;
FOnTimelineEventStatic OpenDoorTimelineFinishedCallback;
OpenDoorTimeLineCallBack.BindUFunction(this, FName{ TEXT("OpenDoorTimeLineCallBack") });
OpenDoorTimelineFinishedCallback.BindUFunction(this, FName{ TEXT("OpenDoorTimelineFinishedCallback") });
static ConstructorHelpers::FObjectFinder<UCurveFloat> opendoor_curve(TEXT("/Game/Blueprints/Vehicles/Door/OpenDoorTimeLineCurve.OpenDoorTimeLineCurve"));
check(opendoor_curve.Succeeded());
OpenDoorTimeLineCurve = opendoor_curve.Object;
OpenDoorTimeLine.SetLooping(false);
OpenDoorTimeLine.SetTimelineLength(1.0f);
//OpenDoorTimeLine.SetTimelineLengthMode(ETimelineLengthMode::TL_LastKeyFrame);
//OpenDoorTimeLine.SetPlaybackPosition(0.0f, false, false);
OpenDoorTimeLine.AddInterpFloat(OpenDoorTimeLineCurve, OpenDoorTimeLineCallBack);
OpenDoorTimeLine.SetTimelineFinishedFunc(OpenDoorTimelineFinishedCallback);
FOnTimelineFloat CloseDoorTimeLineCallBack;
FOnTimelineEventStatic CloseDoorTimelineFinishedCallback;
CloseDoorTimeLineCallBack.BindUFunction(this, FName{ TEXT("CloseDoorTimeLineCallBack") });
CloseDoorTimelineFinishedCallback.BindUFunction(this, FName{ TEXT("CloseDoorTimelineFinishedCallback") });
static ConstructorHelpers::FObjectFinder<UCurveFloat> closedoor_curve(TEXT("/Game/Blueprints/Vehicles/Door/CloseDoorTimeLineCurve.CloseDoorTimeLineCurve"));
check(closedoor_curve.Succeeded());
CloseDoorTimeLineCurve = closedoor_curve.Object;
CloseDoorTimeLine.SetLooping(false);
CloseDoorTimeLine.SetTimelineLength(1.0f);
//CloseDoorTimeLine.SetTimelineLengthMode(ETimelineLengthMode::TL_LastKeyFrame);
//CloseDoorTimeLine.SetPlaybackPosition(0.0f, false, false);
CloseDoorTimeLine.AddInterpFloat(CloseDoorTimeLineCurve, CloseDoorTimeLineCallBack);
CloseDoorTimeLine.SetTimelineFinishedFunc(CloseDoorTimelineFinishedCallback);
}
VehicleDoorControl(EVehicleDoorType door_type, EControlDoorType door_control)函数,控制车门的开关。只要启动TimeLine即可。
void ABaseVehicle::VehicleDoorControl(EVehicleDoorType door_type, EControlDoorType door_control)
{
VehicleDoorType = door_type;
switch (door_control)
{
case EControlDoorType::ECDT_OpenDoor:
OpenDoorTimeLine.PlayFromStart();
break;
case EControlDoorType::ECDT_CloseDoor:
CloseDoorTimeLine.PlayFromStart();
break;
case EControlDoorType::ECDT_Count:
break;
default:
break;
}
}
VehicleDoorProcess(EVehicleDoorType door_type, float door_alpha, UObject* anim_bp)函数,蓝图中实现的。TimeLine的回调函数中调用。
回调函数的实现,
由于后续我只想控制,驾驶位车门的开关,其他门并不想处理,因此我拿一个变量VehicleDoorType记录了当前是那个门的开关,如果想控制多个门还是将回调分开的好一些。
void ABaseVehicle::OpenDoorTimeLineCallBack(float interpolatedVal)
{
float alpha = OpenDoorTimeLineCurve->GetFloatValue(interpolatedVal);
VehicleDoorProcess(VehicleDoorType, alpha, VehicleMesh->GetAnimInstance());
}
void ABaseVehicle::OpenDoorTimelineFinishedCallback()
{
}
void ABaseVehicle::CloseDoorTimeLineCallBack(float interpolatedVal)
{
float alpha = CloseDoorTimeLineCurve->GetFloatValue(interpolatedVal);
VehicleDoorProcess(VehicleDoorType, 1-alpha, VehicleMesh->GetAnimInstance());
}
void ABaseVehicle::CloseDoorTimelineFinishedCallback()
{
}
Tick(float DeltaTime)函数,需要调用开关门的TimeLine的TikeTimeLine()函数。
void ABaseVehicle::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
OpenDoorTimeLine.TickTimeline(DeltaTime);
CloseDoorTimeLine.TickTimeline(DeltaTime);
...
}
汽车换色交互-车身过度换色 展示:
车门的交互,关注构造函数中的InitColorComponent()函数。
BaseVehicle.h 中相关换色的定义。
...
UCLASS()
class UEVEHICLEPROJECT_API ABaseVehicle : public AWheeledVehicle
{
GENERATED_BODY()
public:
ABaseVehicle();
...
// 车身过度换色
UFUNCTION(BlueprintCallable, Category = "VehicleFunc")
void VehicleTransitionColor(const FLinearColor& target_color);
protected:
...
virtual void Tick(float DeltaTime) override;
...
UFUNCTION()
void TransitionColorTimeLineCallback(float interpolatedVal);
UFUNCTION()
void TransitionColorTimelineFinishedCallback();
...
void InitColorComponent(); // 初始化 颜色 组件
protected:
...
// 车身换色
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VehicleColor", meta = (AllowPrivateAccess = "true"))
UCurveFloat* TransitionColorTimeLineCurve;
UPROPERTY(VisibleAnywhere, Category = "VehicleColor", meta = (AllowPrivateAccess = "true"))
UChildActorComponent* WaveStartPoint;
UPROPERTY(VisibleAnywhere, Category = "VehicleColor", meta = (AllowPrivateAccess = "true"))
float WaveSpeed;
UPROPERTY(VisibleAnywhere, Category = "VehicleColor", meta = (AllowPrivateAccess = "true"))
float WaveOffsetEndValue;
...
// 车身换色
FTimeline TransitionColorTimeLine;
};
InitColorComponent()函数,换色组件的初始化。
控制车身换色的实现,也是用TimeLine来实现的,只不过控制的是材质实例。
void ABaseVehicle::InitColorComponent()
{
WaveStartPoint = CreateDefaultSubobject<UChildActorComponent>(TEXT("WaveStartPoint"));
WaveStartPoint->SetRelativeLocation(FVector(127.f, 0.0f, 113.f));
WaveStartPoint->SetupAttachment(VehicleMesh);
WaveSpeed = 0.5f;
WaveOffsetEndValue = 1.0f;
FOnTimelineFloat TransitionColorTimeLineCallBack;
FOnTimelineEventStatic TransitionColorTimelineFinishedCallback;
TransitionColorTimeLineCallBack.BindUFunction(this, FName{ TEXT("TransitionColorTimeLineCallBack") });
TransitionColorTimelineFinishedCallback.BindUFunction(this, FName{ TEXT("TransitionColorTimelineFinishedCallback") });
static ConstructorHelpers::FObjectFinder<UCurveFloat> transitioncolor_curve(TEXT("/Game/Blueprints/Vehicles/Color/TransitionColorTimeLineCurve.TransitionColorTimeLineCurve"));
check(transitioncolor_curve.Succeeded());
TransitionColorTimeLineCurve = transitioncolor_curve.Object;
TransitionColorTimeLine.SetLooping(false);
TransitionColorTimeLine.SetTimelineLength(1.0f);
//TransitionColorTimeLine.SetTimelineLengthMode(ETimelineLengthMode::TL_TimelineLength);
//TransitionColorTimeLine.SetPlaybackPosition(0.0f, false, false);
TransitionColorTimeLine.AddInterpFloat(TransitionColorTimeLineCurve, TransitionColorTimeLineCallBack);
TransitionColorTimeLine.SetTimelineFinishedFunc(TransitionColorTimelineFinishedCallback);
}
VehicleTransitionColor(const FLinearColor& target_color)函数,车身过度换色。
实现方式,该函数获得材质实例中当前车身颜色,然后设置材质实例相关参数,并通过TimeLine修改材质实例参数实现的。
材质的实现这里就不多说了,如果后续有相关UE材质的博客,会考虑写篇文章。原理获取模型顶点然后通过修改材质中的World Positon Offset(世界偏移)来实现。
void ABaseVehicle::VehicleTransitionColor(const FLinearColor& target_color)
{
FVector v_wave_start_point = WaveStartPoint->GetComponentToWorld().GetLocation();
float WaveOffset = 0.0f;
bool StartWave = true;
TArray<class UMaterialInterface*> arrMs = VehicleMesh->GetMaterials();
UMaterialInstanceDynamic* mid_body = VehicleMesh->CreateAndSetMaterialInstanceDynamic(0);
FName bodyname = mid_body->GetFName();
FLinearColor lc_current_color{0.0f, 0.0f, 0.0f};
if (mid_body->GetVectorParameterValue(FHashedMaterialParameterInfo(FName{ TEXT("Color2") }), lc_current_color))
{
//UMaterialInstanceDynamic* mid_body = (UMaterialInstanceDynamic*)(mi_body);
mid_body->SetVectorParameterValue(TEXT("WaveStartPoint"), v_wave_start_point);
mid_body->SetVectorParameterValue(TEXT("Color1"), lc_current_color);
mid_body->SetVectorParameterValue(TEXT("Color2"), target_color);
mid_body->SetScalarParameterValue(TEXT("WaveOffset"), WaveOffset);
TransitionColorTimeLine.PlayFromStart();
}
}
回调函数的实现,通过TimeLine修改材质实例"WaveOffset"参数实现的。
void ABaseVehicle::TransitionColorTimeLineCallback(float interpolatedVal)
{
float WaveOffset = TransitionColorTimeLineCurve->GetFloatValue(interpolatedVal);
UMaterialInstanceDynamic* mid_body = VehicleMesh->CreateAndSetMaterialInstanceDynamic(0);
if (mid_body != nullptr)
{
mid_body->SetScalarParameterValue(TEXT("WaveOffset"), WaveOffset);
}
}
void ABaseVehicle::TransitionColorTimelineFinishedCallback()
{
}
Tick(float DeltaTime)函数,需要调用车身换色的TimeLine的TikeTimeLine()函数。
void ABaseVehicle::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
...
TransitionColorTimeLine.TickTimeline(DeltaTime);
}