[UE4]使用C++重写蓝图,SpawnObject根据类型动态创建UObject
先大量使用蓝图制作项目,后续再用C++把复杂的蓝图重写一遍,用C++代码按照蓝图依葫芦画瓢就可以了,很简单,但需要遵守一些原则:
第一种方法:使用继承
一、创建一个C++类作为蓝图的父类(C++类继承蓝图一样的父类),在UE4中修改蓝图的父类。
二、C++类中的方法、成员变量与蓝图一一对应,并且方法和成员变量名称不能与蓝图的重复。
三、A蓝图不能直接使用B蓝图的变量,A蓝图把要公开的变量封装在函数内返回,并且只返回UE4自带的基础变量类型,不能返回自定义类型,以方便C++重写时返回C++中的成员变量。
四、用C++中实现好的方法逐个替换蓝图中方法,每次替换一个方法就必须要运行游戏进行详细测试,防止修改太多万一出错无法定位问题所在。如下图所示:保留原蓝图的实现,方便C++代码查错。
五、任意一个用C++方法替换蓝图相应的方法 ,都能保证游戏能正常运行,尽量避免出现要同时替换2个以上蓝图方法才能正常运行游戏。这一点非常重要。同样也是防止修改太多万一出错无法定位问题所在。
六、蓝图方法给变量赋值,也可以直接调用C++对应方法赋值,以保证C++其它函数方法能正常运行。
第二种使用C++重写蓝图的方法:使用组合
一、创建一个继承自UObject的C++类,一般加后缀Helper,并且加上BlueprintType标签,共蓝图作为变量类型使用。
头文件:
// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "UObject/NoExportTypes.h" #include "Components/CanvasPanel.h" #include "Blueprint/UserWidget.h" #include "MiniMap/MiniMapFlagData.h" #include "Components/CanvasPanelSlot.h" #include "StaticMiniMapHelper.generated.h" /** * */ UCLASS(BlueprintType) class PROJ10_0121_API UStaticMiniMapHelper : public UObject { GENERATED_BODY() private: UUserWidget* self; //自身引用 public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = myMethods, meta = (ToolTip = "小图标容器面板")) UCanvasPanel * FlagPanel; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = myMethods, meta = (ToolTip = "存放小图标数据机构数组")) TArray<FUMiniMapFlagDataC> FlagArray; //TArray<TSubclassOf<class UMiniMapFlagData>> FlagArray; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = myVariables, meta = (ToolTip = "小地图比例尺")) float MapRatio; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = myVariables, meta = (ToolTip = "小地图缩放比例")) float UIScale; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = myVariables, meta = (ToolTip = "中心对位点")) FVector CenterPosition; public: UFUNCTION(BlueprintCallable, Category = myMethods, meta = (ToolTip = "初始化")) void Ini(UUserWidget* me,UCanvasPanel * Panelflag, FVector PositionCenter, float ScaleUI, float RatioMap); UFUNCTION(BlueprintCallable, Category = myMethods, meta = (ToolTip = "添加图标到小地图")) void AddFlag(UUserWidget* flag, AActor* actor); UFUNCTION(BlueprintCallable, Category = myMethods, meta = (ToolTip = "更新所有图标在小地图上的位置")) virtual void UpdateFlags(); UFUNCTION(BlueprintCallable, Category = myMethods, meta = (ToolTip = "更新某个图标在小地图上的位置")) virtual void UpdateFlag(FUMiniMapFlagDataC data); UFUNCTION(BlueprintCallable, Category = myMethods, meta = (ToolTip = "设置小地图缩放比例")) void SetUIScale(float scale); UFUNCTION(BlueprintCallable, Category = myMethods, meta = (ToolTip = "图标的像素坐标转换成Pivot坐标,小地图是以Pivot为中心点旋转的")) void SetFlagAsPivot(float x, float y); };
CPP文件:
// Fill out your copyright notice in the Description page of Project Settings. #include "StaticMiniMapHelper.h" /** * 功能描述:初始化迷你地图类参数 * @self 自身引用 * @FlagPanel 小图标父级容器对象 * @CenterPosition 中心对位点 * @UIScale 地图缩放比例 * @MapRatio 地图比例尺 * * @return */ void UStaticMiniMapHelper::Ini(UUserWidget * me, UCanvasPanel * Panelflag, FVector PositionCenter, float ScaleUI, float RatioMap) { this->self = me; this->FlagPanel = Panelflag; this->CenterPosition = PositionCenter; this->UIScale = ScaleUI; this->MapRatio = RatioMap; } void UStaticMiniMapHelper::AddFlag(UUserWidget* flag, AActor* actor) { UCanvasPanelSlot* slot = FlagPanel->AddChildToCanvas(flag); FAnchors InAnchors(0.5f, 0.5f); slot->SetAnchors(InAnchors); //设置锚点:中心对齐 slot->SetAlignment(FVector2D(0.5f, 0.5f)); //设置对齐:中心对齐 slot->SetPosition(FVector2D(0, 0)); //设置原点不偏移 slot->SetAutoSize(true); //设置自动尺寸:为图片原始尺寸 //FUMiniMapFlagDataC data; //data.flag = flag; //data.Actor = actor; //data.Slot = slot; //FUMiniMapFlagDataC(flag,actor,slot) FUMiniMapFlagDataC data(flag, actor, slot); FlagArray.Add(data); } void UStaticMiniMapHelper::UpdateFlags() { for (int i = 0; i < FlagArray.Num(); i++) { UpdateFlag(FlagArray[i]); } } void UStaticMiniMapHelper::UpdateFlag(FUMiniMapFlagDataC data) { FVector ActorLocation = data.Actor->GetActorLocation(); FVector temp = (ActorLocation - CenterPosition)*(MapRatio*UIScale); data.Slot->SetPosition(FVector2D(temp.Y, temp.X*-1)); //设置小图标位置 data.flag->SetRenderAngle(data.Actor->GetActorRotation().Yaw); //旋转小图标 } void UStaticMiniMapHelper::SetUIScale(float scale) { UIScale = scale; } //更新所有图标在小地图上的位置 void UStaticMiniMapHelper::SetFlagAsPivot(float x, float y) { FVector2D 地图实际尺寸 = self->GetCachedGeometry().GetLocalSize(); //获得地图实际尺寸 float 地图长度 = 地图实际尺寸.X; float 地图宽度 = 地图实际尺寸.Y; float 图标X坐标 = x; float 图标Y坐标 = y; //小地图旋转X坐标 = (图标X坐标 + 地图长度/2)/地图长度 float 小地图旋转X坐标 = (图标X坐标 + 地图长度 / 2) / 地图长度; float 小地图旋转Y坐标 = (图标Y坐标 + 地图长度 / 2) / 地图长度; //设置小地图旋转原点 self->SetRenderTransformPivot(FVector2D(小地图旋转X坐标, 小地图旋转Y坐标)); }
二、在蓝图中添加一个名为MyHelper的变量,类型是第一步C++创建的类型。
三、在使用helper对象之前,必须先实例化。“Spawn Object”是自己用C++写的一个蓝图库中的一个方法。
四、接着就是要初始化helper的成员变量值。其中当前蓝图对象的引用(也就是self)要传递给me参数,这是关键,用helper的成员对象保存起来。
五、最终用C++相同的方法替换原来用蓝图写的功能。
使用继承和组合都可以实现C++重写蓝图,但是组合比继承要更好,耦合度更低!
迁移遇到的问题:
1、如果蓝图继承一个C++类,则迁移的时候会出现问题,这个蓝图在新项目里面打不开的。
2、使用C++组合时可以迁移到其他项目,可以打开蓝图,但是helper所有的相关方法调用变成无效,需要手动重新照着做一次。
附注用C++ 实现蓝图函数库,根据类型动态创建UObject,并返回引用给蓝图使用:
头文件
#include "Kismet/BlueprintFunctionLibrary.h" #include "TimyLibrary.generated.h" /** * */ UCLASS() class PROJ10_0121_API UTimyLibrary : public UBlueprintFunctionLibrary { GENERATED_BODY() public: UFUNCTION(BlueprintCallable, Category = "TimyLibrary|Object", meta = (ToolTip = "创建UObject实例")) static UObject* SpawnObject(UObject* owner, UClass* ObjClass); };
CPP文件
// Fill out your copyright notice in the Description page of Project Settings. #include "TimyLibrary.h" #include "Runtime/Engine/Classes/Engine/Engine.h" //创建根据类型创建UObject UObject* UTimyLibrary::SpawnObject(UObject* owner, UClass* ObjClass) { UWorld* World = GEngine->GetWorldFromContextObject(owner); UObject* tempObject = NewObject<UObject>(World, ObjClass); //UObject* tempObject = NewObject<UObject>(ObjClass); //创建对象会失败 return tempObject; }