虚幻UnrealEngine5 C++ 汽车基础交互

虚幻引擎5的抢先体验

UE5开启了抢先体验版,我也入了坑。
在开启汽车之旅之前,我先唠叨些,使用UE5相关的问题,做以记录。

虚幻引擎5 新功能

先说说,UE5最为亮眼的两大新特性:LumenNanite

Lumen:崭新的实时全局光照。

Lumen是一套动态全局光照技术,Lumen可以实现实时光线反弹,在Lumen帮助下可以包含多次反弹的全局光照,没有光照贴图并无需烘焙,启用Lumen之后,只要移动光源,光线反弹效果会跟着实时变化。

Lumen能够对场景和光照变化做出实时反应,且无需专门的光线追踪硬件。Lumen能在任何场景中渲染间接镜面反射,也可以无限反弹的漫反射。

这就意味着我们可以使用Lumen创建出更动态的场景,我们可以随意改变白天或者晚上的光照角度,系统会根据情况调整间接光照。Lumen的出现将为美术与设计行业的工作人员节省了大量时间。

Lumen很牛逼,但是在使用时候也是存在一些问题的。
我在做草地,双面渲染的时候,背面特别暗,导致在UE4中效果,在UE5中显示不出来。究其原因,Lumen不支持植物透光…

在项目中,使用了Lumen,自己加环境光和自发光板,不会补光,导致光线反射处出现噪点,简单的补光都搞不定,真是当不上技美了…

Nanite:多边形虚拟化的技术。

一个虚拟场景是由很多模型构成的,而这些模型的基础是三角形,所以三角形的数量通常都非常的多。这造成了一个问题:由于三角形的数量非常多,但是我们所能看到的三角形数量非常的少。这使得我们需要把很多不必要的三角形剔除,增加渲染的效率并节省时间。

而Nanite技术可以虚拟化几何体,Nanite能极快的渲染超多的三角面,并且能够将很多的三角面无损压缩成很少。Nanite能够展示像素级别的细节,这使得几何体中的三角形也常是像素大小的,这个级别的几何体细节也要求阴影能够精确到像素。

做过测试,将百万级三角面的模型,导入到工程,将模型拖到场景中进行显示,三角面骤降几千面,不得不承认,UE的强大,这一技术真实工业级模型的福音~但是讲百万级的三角面的模型导入工程时,UE5崩溃了3、4次,这个代价还是完全可以接受的。

还有相关的其他的更新。

  • UI更为现代化,使用起来更为舒服。
  • 超大世界协作提供方案:世界分区
  • 动画系统增强
  • 程序式音频 MetaSounds
  • 随身携带的Bridge Megascans云资产库

有些功能,我还没使用过,这里不做过多评论了。

虚幻引擎5 填坑

UE修改缓存存储位置

无论是安装版的还是源码版的UE5,默认引擎会把生成的缓存文件寄存在该目录下,该目录默认在系统盘中,久而久之会积累巨大从而严重影响运行速度。所以我们将这个目录指向项目目录下。

UE默认的项目缓存文件夹

C:\Users\你的用户名\AppData\Local\UnrealEngine

修改缓存文件夹路径

在UE安装目录下找到BaseEngine.ini文件
搜索
%ENGINEVERSIONAGNOSTICUSERDIR%DerivedDataCache
替换
%GAMEDIR%DerivedDataCache

UE在VS编译时乱码

这个没啥好办法解决,我是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的源码编译

编译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 一下就可以了。

虚幻引擎5 C++ 工程搭建

汽车基础交互-工程场景 展示:
虚幻UnrealEngine5 C++ 汽车基础交互_第1张图片

创建工程,这里我们创建一个C++移动端空工程,命名为UEVehicleProject
虚幻UnrealEngine5 C++ 汽车基础交互_第2张图片

添加"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"模块。
虚幻UnrealEngine5 C++ 汽车基础交互_第3张图片

虚幻引擎5 C++ 工程基础

先说一下,该篇博客提交的汽车基础交互都要做什么。都是为以后驾驶交互和人车交互打基础。

  • 灯光交互:
    车辆前后灯控制,夜间行驶时照明功能。
    刹车灯控制,在刹车时亮起,停车后熄灭。
    倒车灯控制,汽车倒车时亮起。
  • 车门交互:
    对左前门控制开关,人员进入驾驶位时使用。
    对右前门控制开关,防止有右驾驶座位的车辆使用。
  • 换色交互:
    汽车车体更换不同颜色。

先创建几个汽车相关的类。
虚幻UnrealEngine5 C++ 汽车基础交互_第4张图片

  • ABaseVehicle 类:基础车辆类,继承 AWheeledVehicle。
  • UFrontWheel 类:车前轮类,继承 UVehicleWheel。
  • URearWheel 类:车后轮类,继承 UVehicleWheel。

编写代码之前,需要先讲一下汽车的骨骼和动画蓝图。
汽车的骨骼,汽车前后车轮的骨骼节点是必须的,因为行驶时,以完成车轮的转动、转向需要,轱辘内的刹车片和左右车门,是根据需求添加的,要看都想完成什么样的操作。

虚幻UnrealEngine5 C++ 汽车基础交互_第5张图片

再看一下汽车的蓝图动画,来控制车门的开关。

虚幻UnrealEngine5 C++ 汽车基础交互_第6张图片

了解了要实现的功能和规则,我们来逐渐完善ABaseVehicle、UFrontWheel、URearWheel类,本篇博客只涉及到ABaseVehicle类的编写。
最终,我们会搞一个蓝图类BP_SUV,来继承C++类ABaseVehicle。指定车体模型,微调车灯、方向盘、仪表盘、尾气特效…等组件的位置。来完成不同车型(轿车、吉普、SUV…)的实现。这里以一辆SUV为例。

虚幻UnrealEngine5 C++ 汽车基础交互_第7张图片

虚幻引擎5 C++ 汽车灯光交互

汽车灯光交互-前后灯控制 展示:

汽车灯光交互-刹车灯控制 展示:

汽车灯光交互-倒车灯控制 展示:

现在我们开始正式编码。
首先看一下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)函数,车灯控制函数。

  • position_lights 前后灯开关
  • brake_lights 刹车灯开关
  • 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);
	...
}

虚幻引擎5 C++ 汽车车门交互

汽车车门交互-左右门控制 展示:

车门的交互,关注构造函数中的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即可。

  • door_type 车门类型(左门、右门)
  • door_control 车门控制(开门、关闭)
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的回调函数中调用。

虚幻UnrealEngine5 C++ 汽车基础交互_第8张图片

回调函数的实现,
由于后续我只想控制,驾驶位车门的开关,其他门并不想处理,因此我拿一个变量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);
	...
}

虚幻引擎5 C++ 汽车换色交互

汽车换色交互-车身过度换色 展示:

车门的交互,关注构造函数中的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(世界偏移)来实现。

  • target_color 目标颜色。
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);
}

你可能感兴趣的:(UE5,C++,汽车交互)