UE Editor API 整理

UE Editor API 整理

过一下 https://github.com/20tab/UnrealEnginePython/blob/master/docs/,熟悉一下编辑器 API,方便后续编辑器脚本开发

后续的目标是所有编辑器操作应该都可以脚本化(自动化),这样把 GPT 接进 UE 里就可以 Chat UE 操作了

  • SM_ = static mesh, HISM = …
  • http 略
  • runtime 开发就用到的略

ICollectionManager

集合 是一种将资产集整理成组的方式。与文件夹不同,集合不包含资产本身,而仅包含对这些资产的引用。实际上,这意味着一个资产可以属于多个集合。

在这里插入图片描述

#include “Developer/CollectionManager/Public/ICollectionManager.h”

DataTable

#include “Runtime/Engine/Classes/Engine/DataTable.h”
#include “Editor/UnrealEd/Public/DataTableEditorUtils.h”

DT 内部是 FName => UScriptStruct 的映射
TMap<FName, uint8*> RowMap;

DT 转 json string

FString GetTableAsJSON(const EDataTableExportFlags InDTExportFlags = EDataTableExportFlags::None) const;

创建新 DT

dt_factory = DataTableFactory()
dt_factory.Struct = Transformdt = dt_factory.factory_create_new('/Game/TransformDataTable')

工厂模式,每个资源类型都有一个

检查下路径有没有冲突

class UDataTableFactory : public UFactory
{UPROPERTY(BlueprintReadWrite, Category = "Data Table Factory")TObjectPtr<const class UScriptStruct> Struct;
...void ue_factory_create_new(UFactory* factory, const FString& name)
{FString PackageName = UPackageTools::SanitizePackageName(name);UPackage* outer = CreatePackage(*PackageName);if (!outer){// Handle error: unable to create packagereturn;}TArray<UPackage*> TopLevelPackages;TopLevelPackages.Add(outer);if (!UPackageTools::HandleFullyLoadingPackages(TopLevelPackages, FText::FromString("Create a new object"))){// Handle error: unable to fully load packagereturn;}UClass* u_class = factory->GetSupportedClass();if (u_class->IsChildOf<UBlueprint>() && FindObject<UBlueprint>(outer, *name)){// Handle error: a blueprint with this name already exists in the packagereturn;}if (u_class->IsChildOf<UUserDefinedStruct>() && FindObject<UUserDefinedStruct>(outer, *name)){// Handle error: a structure with this name already exists in the packagereturn;}UObject* u_object =nullptr;u_object = factory->FactoryCreateNew(u_class, outer, FName(*name), RF_Public | RF_Standalone, nullptr, GWarn);if (u_object){FAssetRegistryModule::AssetCreated(u_object);outer->MarkPackageDirty();}else{// Handle error: unable to create new object from factoryreturn;}
}

Foliage

UE 自带的植被系统,专门有个刷植被的编辑模式,刷的结果存在 UWorld 的 AInstancedFoliageActor 里(单例,自动创建)

存储用了一个 Map,每一项是一个 SM_,渲染用 HISM

TMap<TObjectPtr<UFoliageType>, TUniqueObj<FFoliageInfo>> FoliageInfos;

? 某种资源 todo

factory = FoliageTypeFactory()
foliage_type = factory.factory_create_new('/Game/Foliage/FirstFoliageType')
foliage_type.Mesh = ue.load_object(StaticMesh, '/Game/Mesh/StaticMesh001')
foliage_type.save_package()

这个 Map 存 UWorld.AInstancedFoliageActor 里,即每个 .umap 都单独有一套植被表

foliage_actor = world.get_instanced_foliage_actor_for_current_level()
world.add_foliage_asset(foliage_type)
world.add_foliage_asset(ue.load_object(StaticMesh, '/Game/Mesh/StaticMesh001'))
void ue_add_foliage_asset(UObject* self, UObject* u_object)
{UWorld* world = ue_get_uworld(self);if (!world){// Handle error: unable to retrieve UWorld from uobjectreturn;}UFoliageType* foliage_type = nullptr;AInstancedFoliageActor* ifa = AInstancedFoliageActor::GetInstancedFoliageActorForCurrentLevel(world, true);if (u_object->IsA<UStaticMesh>()){foliage_type = ifa->GetLocalFoliageTypeForSource(u_object);if (!foliage_type){ifa->AddMesh(static_cast<UStaticMesh*>(u_object), &foliage_type);}}else if (u_object->IsA<UFoliageType>()){foliage_type = static_cast<UFoliageType*>(u_object);ifa->AddFoliageType(foliage_type);}if (!foliage_type){// Handle error: unable to add foliage assetreturn;}// Return foliage_type if needed
}

遍历植被表

import unreal_engine as uefoliage_actor = ue.get_editor_world().get_instanced_foliage_actor_for_current_level()for foliage_type in foliage_actor.get_foliage_types():print('Foliage Type: {0}'.format(foliage_type.get_name()))for foliage_instance in foliage_actor.get_foliage_instances(foliage_type):print(foliage_instance.location)print(foliage_instance.draw_scale3d)print(foliage_instance.pre_align_rotation)print(foliage_instance.rotation)print(foliage_instance.flags)print(foliage_instance.zoffset)print('*' * 20)

Add a ForEachLoop Macro node

蓝图编辑器的 API,非常恶心,添加节点,添加 pin 脚连线,保存

# for_each_loop = ue.load_object(EdGraph, '/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:ForEachLoop')
for_each_loop = ue.find_object('ForEachLoop')# get a reference to your blueprint
blueprint = ...# add the node
node = blueprint.UberGraphPages[0].graph_add_node(K2Node_MacroInstance)
# assign the macro graph to the node
node.MacroGraphReference = GraphReference(MacroGraph=for_each_loop)
# allocate pins
node.node_allocate_default_pins()# update the blueprint
ue.blueprint_mark_as_structurally_modified(bp)

Landscape/Terrain

Landscape 也是特殊单例对象(和植被一样),用 heightmap 渲染

地形支持分块,实际以 ULandscapeComponent 为单位渲染,每个 ULandscapeComponent 存一小块 heightmap texture 引用

python 里 heightmap 对应到 bytearray 来操作

创建 heightmap,创建 landscape 并指定 heightmap,设置地形大小,分块粒度(存在 ULandscapeInfo)

其他导出 SM_ 等略

width = 1024
height = 1024
heightmap = []for y in range(0, height):for x in range(0, width):heightmap.append(random.randint(0, 65535))data = struct.pack('{0}H'.format(width * height), *heightmap)quads_per_section = 63
number_of_sections = 1
components_x = 8
components_y = 8fixed_data = ue.heightmap_expand(data, width, height, quads_per_section * number_of_sections * components_x + 1, quads_per_section * number_of_sections * components_y + 1)landscape = ue.get_editor_world().actor_spawn(Landscape)
landscape.landscape_import(quads_per_section, number_of_sections, components_x, components_y, fixed_data)
landscape.set_actor_scale(1,1,1)

Level & World

编辑器下理解 level 很重要

  • level 和 world 的区别,level 是静态容器,world 是动态容器,两个都是存 actor 列表

创建 .umap

factory = WorldFactory()
new_world = factory.factory_create_new('/Game/Maps/FooLevel')

spawn 一些 actor 到 world 里

# create a world (it will be the main one, the one you load into the editor by double clicking it)
main_world = factory.factory_create_new('/Game/MapsTest/MainWorld001')
# spawn actors in the world
actor0 = main_world.actor_spawn(PlayerStart)# create another world
child_world1 = factory.factory_create_new('/Game/MapsTest/ChildWorld001')
# spawn actors in the world
actor1 = child_world1.actor_spawn(Actor)
actor2 = child_world1.actor_spawn(Actor)
actor3 = child_world1.actor_spawn(Actor)# create another world
child_world2 = factory.factory_create_new('/Game/MapsTest/ChildWorld002')
# spawn actors in the world
actor4 = child_world2.actor_spawn(Actor)# now the important part, each UWorld, has a ULevel mapped to it (the PersistentLevel):
main_level = main_world.PersistentLevel
child_level1 = child_world1.PersistentLevel
child_level2 = child_world2.PersistentLevel# open main world in the editor
ue.open_editor_for_asset(main_world)

level 面板里,sub-level 和 level-streaming 的概念

added_streaming_level = ue.add_level_to_world(main_world, child_world1.get_path_name()[, always_loaded])

添加一个 sub-level(用 path),指定 ULevelStreaming StreamingMode

这个存在 main-level 的 TArray<TObjectPtr<ULevelStreaming>> StreamingLevels // todo double check

ULevelStreaming* ue_add_level_to_world(UWorld* u_world, const FString& name, bool isAlwaysLoaded)
{if (!u_world){// Handle error: argument is not a UWorldreturn nullptr;}if (!FPackageName::DoesPackageExist(*name, nullptr)){// Handle error: package does not existreturn nullptr;}UClass* streaming_mode_class = ULevelStreamingDynamic::StaticClass();if (isAlwaysLoaded){streaming_mode_class = ULevelStreamingAlwaysLoaded::StaticClass();}ULevelStreaming* level_streaming = nullptr;
#if ENGINE_MAJOR_VERSION == 5 || (ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 17)level_streaming = EditorLevelUtils::AddLevelToWorld(u_world, *name, streaming_mode_class);
#elselevel_streaming = EditorLevelUtils::AddLevelToWorld(u_world, *name, streaming_mode_class);
#endifif (!level_streaming){// Handle error: unable to add level to the worldreturn nullptr;}#if ENGINE_MAJOR_VERSION == 5 || (ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 16)FEditorDelegates::RefreshLevelBrowser.Broadcast();
#endifreturn level_streaming;
}

CurrentLevel 是编辑器下当前正在编辑的 level(focus),可以切 level,编辑器所有 actor 操作都是在这个 level 进行

# get a reference to the current level
current_level = ue.get_editor_world().get_current_level()# change the current level
ue.get_editor_world().set_current_level(child_level1)# spawn an actor in editor world, but effectively it will be spawned
# in a child level
actor001 = ue.get_editor_world().actor_spawn(actor001)# back to the original level
ue.get_editor_world().set_current_level(current_level)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/23573.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

了解Kubernetes-RKE2的PKI以及证书存放位置

一、什么是PKI&#xff1f; 简称&#xff1a;证书基础设施。 可以方便理解为当你的集群有Server,Client架构&#xff0c;那么为了安全加密之间的通信&#xff0c;则需要使用证书进行交互&#xff0c;那么利用PKI架构可以安全加密组件之间的通信。 二、Kubernetes的PKI架构什…

HLA高层体系结构1.0.0版本

名&#xff1a;高层体系结构&#xff08;High Level Architecture&#xff0c;HLA&#xff09; 高层体系结构&#xff08;High Level Architecture&#xff0c;HLA&#xff09;是从体系结构上建立这样一个框架&#xff0c;它能尽量涵盖M&S领域中所涉及的各种不同类型的仿真…

代码随想录算法训练营第十四天| 104.二叉树的最大深度 、 111.二叉树的最小深度、 222.完全二叉树的节点个数

104.二叉树的最大深度 题目链接&#xff1a;二叉树的最大深度 文档讲解&#xff1a;代码随想录 状态&#xff1a;so easy 思路&#xff1a;左子树和右子树中取最大深度&#xff0c;依次往下递归 递归解法&#xff1a; public int maxDepth(TreeNode root) {if (root null) {r…

【高校科研前沿】新疆生地所陈亚宁研究员团队在GeoSus发文:在1.5°C和2°C全球升温情景下,中亚地区暴露于极端降水的人口增加

目录 文章简介 1.研究内容 2.相关图件 3.文章引用 文章简介 论文名称&#xff1a;Increased population exposures to extreme precipitation in Central Asia under 1.5 ◦C and 2 ◦C global warming scenarios&#xff08;在1.5C和2C全球变暖情景下&#xff0c;中亚地区…

伽马校正技术在AI绘画中的作用

随着人工智能技术的飞速发展&#xff0c;AI绘画已经成为了艺术创作领域的一股新兴力量。在这个数字化时代&#xff0c;计算机图形学和机器学习的结合为我们带来了前所未有的创作工具。然而&#xff0c;为了实现更加真实和自然的色彩表现&#xff0c;伽马校正技术在其中扮演着至…

DP读书:如何使用badge?(开源项目下的标咋用)

最近在冲论坛&#xff0c;很少更一些内容了。但遇到了一个真的有趣的&#xff1a; 开源项目下&#xff0c;蓝蓝绿绿的标是怎么用的呢&#xff1f; 这是我的主页Readme&#xff0c;在看一些NXP的主仓时&#xff0c;突然发现没有这个玩&#xff0c;就自己整了个 再比如我的CSDN专…

Vivado 设置关联使用第三方仿真软件 Modelsim

目录 1.前言2.Vivado 设置关联使用第三方仿真软件 Modelsim 微信公众号获取更多FPGA相关源码&#xff1a; 1.前言 Vivado 软件自带有仿真功能,该功能使用还是比较方便的,初学者可以直接使用自带的仿真功能。 Modelsim仿真工具是Model公司开发的。它支持Verilog、VHDL以及他…

springboot+vue+mybatis房屋租贷系统+PPT+论文+讲解+售后

本论文系统地描绘了整个网上房屋租赁系统的设计与实现&#xff0c;主要实现的功能有以下几点&#xff1a;管理员&#xff1b;首页、个人中心、房屋类型管理、房屋租赁管理、会员管理、订单信息管理、合同信息管理、退房评价管理、管理员管理&#xff0c;系统管理&#xff0c;前…

分布式ID生成方式

1.UUID uuid方式存在问题&#xff1a;占用字节数比较大&#xff1b;ID比较随机&#xff0c;作为MySQL主键写入库时&#xff0c;为了保证顺序性将导致BTree节点分裂比较频繁&#xff0c;影响IO性能。 2.数据库方式 步长step 3&#xff0c;即为机器的数量。 第一台机器&#x…

2024 Q1企业级SSD市场暴涨,国产努力追赶!

在2024年第一季度&#xff0c;由于对高容量存储需求的激增&#xff0c;企业级固态硬盘&#xff08;SSD&#xff09;市场的收入实现了显著增长&#xff0c;达到了37.58亿美元&#xff0c;与上一季度相比增长了62.9%。这一增长主要得益于供应商减产导致的高容量订单需求未得到满足…

大模型高级 RAG 检索策略之混合检索

古人云&#xff1a;兼听则明&#xff0c;偏信则暗&#xff0c;意思是要同时听取各方面的意见&#xff0c;才能正确认识事物&#xff0c;只相信单方面的话&#xff0c;必然会犯片面性的错误。 在 RAG&#xff08;Retrieval Augmented Generation&#xff09;应用中也是如此&…

LeetCode 算法:合并区间c++

原题链接&#x1f517;&#xff1a;合并区间 难度&#xff1a;中等⭐️⭐️ 题目 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰…

Nginx编译安装+nginx模块

一、I/O模型 处理高并发的时候用 1.1I/O模型简介 同步/异步&#xff08;消息反馈机制&#xff09;&#xff1a;关注的是消息通信机制&#xff0c;即调用者在等待一件事情的处理结果时&#xff0c;被调用者是否提供完成状态的通知。 同步&#xff1a;synchronous&#xff0c;…

java第二十课 —— 面向对象习题

类与对象练习题 编写类 A01&#xff0c;定义方法 max&#xff0c;实现求某个 double 数组的最大值&#xff0c;并返回。 public class Chapter7{public static void main(String[] args){A01 m new A01();double[] doubleArray null;Double res m.max(doubleArray);if(res !…

Mysql8安装教程与配置(超详细图文)

MySQL 8.0 是 MySQL 数据库的一个重大更新版本&#xff0c;它引入了许多新特性和改进&#xff0c;旨在提高性能、安全性和易用性。 1.下载MySQL 安装包 注&#xff1a;本文使用的是压缩版进行安装。 &#xff08;1&#xff09;从网盘下载安装文件 点击此处直接下载 &#…

JavaWeb3 Ajax+Axios+Element+Nginx部署

Ajax 异步JS和XML 1.数据交换&#xff1a;给服务器发送请求&#xff0c;并获取服务器相应的数据 2.异步交互&#xff1a;在不重新加载整个页面的情况下&#xff0c;与服务器交换数据并更新部分网页 同步与异步 原生Ajax <!DOCTYPE html> <html> <body><…

Android——热点开关演讲稿

SoftAP打开与关闭 目录 1.三个名词的解释以及关系 Tethering——网络共享&#xff0c;WiFi热点、蓝牙、USB SoftAp——热点(无线接入点)&#xff0c;临时接入点 Hostapd——Hostapd是用于Linux系统的软件&#xff0c;&#xff0c;支持多种无线认证和加密协议&#xff0c;将任…

Java集合的迭代操作,Set Map接口以及工具类方法

1、集合元素迭代 1.1 集合元素遍历 集合的遍历&#xff1a;把集合中的每一个元素获取出来 使用for遍历 使用迭代器遍历 Iterator表示迭代器对象&#xff0c;迭代器中拥有一个指针&#xff0c;默认指向第一个元素之前&#xff0c; . boolean hasNext()&#xff1a;判断指针后是…

任务3.7 开发名片管理系统

本实战项目以Java语言为基础&#xff0c;精心打造了一个功能全面的名片管理系统。系统采用面向对象的设计原则&#xff0c;通过Card类来封装每张名片的详细信息&#xff0c;如姓名、单位、职位和联系电话等&#xff0c;并提供了标准的访问器和修改器方法以确保数据的安全访问。…

ROS学习记录:自定义消息类型

前言 当我们需要传输一些特殊的数据时&#xff0c;且官方的消息包无法满足需求&#xff0c;我们便可以自己定义一个消息类型。 实验步骤 一、在终端输入cd ~/catkin_ws1/src进入工作空间中src目录 二、输入catkin_create_pkg qq_msgs roscpp rospy std_msgs message_generati…