[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(, )); //设置原点不偏移
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 = ; 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*-)); //设置小图标位置 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坐标 + 地图长度 / ) / 地图长度;
float 小地图旋转Y坐标 = (图标Y坐标 + 地图长度 / ) / 地图长度; //设置小地图旋转原点
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;
}
[UE4]使用C++重写蓝图,SpawnObject根据类型动态创建UObject的更多相关文章
- [UE4]认识CanvasPanelSlot,Construct Object From Class动态创建UI控件
Canvas Panel Slot是UserWidget的Canvas Panel组件容器内的组件特有的属性. 只有放置在Canvas Panel容器内才会有Canvas Panel Slot属性 可 ...
- 关于Emit中动态类型TypeBuilder创建类标记的一点思考
利用TypeBuilder是可以动态创建一个类型,现在有个需求,动态生成一个dll,创建类型EmployeeEx,需要继承原dll里面的Employee类,并包含Employee类上的所有类标记. ...
- iOS回顾笔记(08) -- 自定义Cell的类型和创建步骤总结
iOS回顾笔记(08) -- 自定义Cell的类型和创建步骤总结 项目中我们常见的自定义cell主要分为两种 等高cell:如应用列表.功能列表 非等高cell:如微博列表.QQ聊天页面 下面对这 ...
- .Net 中的反射(动态创建类型实例) - Part.4
动态创建对象 在前面节中,我们先了解了反射,然后利用反射查看了类型信息,并学习了如何创建自定义特性,并利用反射来遍历它.可以说,前面三节,我们学习的都是反射是什么,在接下来的章节中,我们将学习反射可以 ...
- C# 在运行时动态创建类型
C# 在运行时动态的创建类型,这里是通过动态生成C#源代码,然后通过编译器编译成程序集的方式实现动态创建类型 public static Assembly NewAssembly() { //创建编译 ...
- [转]js动态创建json类型
废话少说:json是一个特有的键值对数组类型.既然是数组类型那么我们就可以这样定义 1.先定义数组 var Data = []; 2.理解键值对 对象名:值{ "id": i, & ...
- mysql索引总结(1)-mysql 索引类型以及创建
mysql索引总结(1)-mysql 索引类型以及创建 mysql索引总结(2)-MySQL聚簇索引和非聚簇索引 mysql索引总结(3)-MySQL聚簇索引和非聚簇索引 mysql索引总结(4)-M ...
- 封装basedao及动态创建新类型的java数组
package com.huawei.base; import java.io.Serializable;import java.lang.reflect.Array;import java.lang ...
- 静态类型&动态类型
何时使用:使用存在继承关系的类型时,必须将一个变量或其他表达式的静态类型与该表达式表示对象的动态类型区分开来 静态类型:表达式的静态类型在编译时总是已知的,它是变量声明时的类型或表达式生成的类型 动态 ...
随机推荐
- redis源码之压缩列表ziplist
压缩列表ziplist1.简介连续,无序的数据结构.压缩列表是 Redis 为了节约内存而开发的, 由一系列特殊编码的连续内存块组成的顺序型(sequential)数据结构. 2.组成 属性 类型 长 ...
- 找工作String类(重点,背诵)(本质是一个类)
一个顶层设计者眼中只有2个东西接口,类(属性,方法) 无论String 类 , HashMap实现类 , Map接口 String str = "Hello" ; // 定义 ...
- 数学沉思录:古今数学思想的发展与演变 (Mario Livio 著)
第1章 神秘的数学 (已看) 发现还是发明 第2章 神秘学:命理学家和哲学家 (已看) 毕达哥拉斯 进入柏拉图的洞穴 第3章 魔法师:大师和异端 (已看) 给我一个支点:我将撬起地球 阿基米德重写稿 ...
- C# to il 9 Properties and Indexers(属性和索引器)
A field is simply a memory location, whereas, a property is a collection of methods. Aproperty is re ...
- css3中的BFC,IFC,GFC和FFC
出处:https://www.jianshu.com/p/e75f351e11f8 表格比较: 名称 英文全称 含义 BFC Block Formatting Contexts 块级格式化上下文 IF ...
- java-常用注解
JDK自带注解 @Override 重写, 标识覆盖它的父类的方法 @Deprecated 已过期,表示方法是不被建议使用的 @Suppvisewarnings 压制警告,抑制警告 元注解 @Ta ...
- less命令详解
Linux less命令 less 与 more 类似,但使用 less 可以随意浏览文件,而 more 仅能向前移动,却不能向后移动,而且 less 在查看之前不会加载整个文件 语法: less [ ...
- STM32的优先级的理解及其使用
1 组只代表主优先级和副优先级数量的配比,组之间没有优先级的概念. 2 不同的主优先级中断之间,高优先级中断可以打断正在执行的低优先级中断,即可以形成中断嵌套. 3 主优先级相同的中断,高副优先级中断 ...
- ES Terms 聚合数据不确定性
Elasticsearch是一个分布式的搜索引擎,每个索引都可以有多个分片,用来将一份大索引的数据切分成多个小的物理索引,解决单个索引数据量过大导致的性能问题,另外每个shard还可以配置多个副本,来 ...
- golang中defer的详解 转自https://blog.csdn.net/skh2015java/article/details/77081250
Go里的defer很有用,尤其在很多执行模块化操作时,初始化时给各个需要执行的模块传入参数,但是这些参数有些事在模块执行过程中才赋值的. 这时候有了defer就不会把代码写的很凌乱. Go的defer ...