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 = Transform

dt = 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 package
        return;
    }

    TArray<UPackage*> TopLevelPackages;
    TopLevelPackages.Add(outer);
    if (!UPackageTools::HandleFullyLoadingPackages(TopLevelPackages, FText::FromString("Create a new object")))
    {
        // Handle error: unable to fully load package
        return;
    }

    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 package
        return;
    }

    if (u_class->IsChildOf<UUserDefinedStruct>() && FindObject<UUserDefinedStruct>(outer, *name))
    {
        // Handle error: a structure with this name already exists in the package
        return;
    }

    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 factory
        return;
    }
}

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 uobject
        return;
    }

    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 asset
        return;
    }

    // Return foliage_type if needed
}

遍历植被表

import unreal_engine as ue

foliage_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 = 8

fixed_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 UWorld
        return nullptr;
    }

    if (!FPackageName::DoesPackageExist(*name, nullptr))
    {
        // Handle error: package does not exist
        return 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);
#else
    level_streaming = EditorLevelUtils::AddLevelToWorld(u_world, *name, streaming_mode_class);
#endif

    if (!level_streaming)
    {
        // Handle error: unable to add level to the world
        return nullptr;
    }

#if ENGINE_MAJOR_VERSION == 5 || (ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 16)
    FEditorDelegates::RefreshLevelBrowser.Broadcast();
#endif

    return 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)

Assets

  • UPackage 可以存若干 UObject(一般一个)
  • 路径 API,和 os 类似
  • asset import 时会记录本地 source path,用来支持 reimport
  • 每种 asset 对应 factory 类来进行 load
  • UObject 编辑器下 Outer 即 UPackage,SavePackage 写文件
  • AssetRegistryModule.Get().GetReferencersGetDependencies,editor 下全局缓存了引用关系图
  • // UE 的这套资源系统封的非常深度,相当于有一套傻瓜的自动模式,本质是 UE 存了引用关系配合 GC 来自动加载释放,(只有加载 UWorld,Spawn Actor & Component),做 Demo 完全够了;正式项目还是要对资源使用定下规范,使用"专家模式“,否则内存占用,以及 IO 加载造成游戏卡顿是后期优化很难偿还的技术债务
# get the Mat001.Foobar asset
material = ue.get_asset('/Game/Materials/Mat001.Foobar')
# print material repr
ue.log(material)
# print material properties
ue.log(material.properties())

############################
materials = ue.get_assets('/Game/Materials')
materials = ue.get_assets('/Game/Materials', True)
materials = ue.get_assets_by_class('Material')

ue.duplicate_asset('/Game/Materials/Mat001.Foobar', '/Game/NewMaterials/Mat001', 'FooBarUpdated')
ue.delete_asset('/Game/NewMaterials/Mat001.FooBarUpdated')

asset = ue.import_asset('/Users/FooBar/Desktop/texture001.png', '/Game/Textures')

if asset002.asset_can_reimport():
   asset002.asset_reimport()

mesh.asset_import_data_set_sources('D:/sword.fbx')

############################
particle_system = ParticleSystem()
particle_system.set_name('ParticleSystemForDummies')

# special form of the constructor taking the object name
material = Material('FunnyMaterial')

# this will save particle_system into /Game/Particles.ParticleSystemForDummies
particle_system.save_package('/Game/Particles')

# this will save material into /Game/FunnyMaterials.FunnyMaterial
material.save_package('/Game/FunnyMaterials')

list_of_referencers = ue.get_asset_referencers('/Game/FooBar')
list_of_dependencies = ue.get_asset_dependencies('/Game/FooBar')

Material

Material 本质是 shader function,走 shader compiler 那一套

实际上还有 Material Instance 和 Material Instance Dynamic 用来 override 参数值,前者是静态(存资源),后者是动态(运行时修改)

创建 MI_,指定 parent

material_instance = MaterialInstanceConstant()
material_instance.set_name('New Funny Material Instance')
material_instance.set_material_parent(new_material)
material_instance.save_package('/Game/Materials/instanced')

遍历参数

parent_material = material_instance.Parent

for expression in parent_material.Expressions:
    parameter_name = expression.ParameterName
    parameter_group = expression.Group

# retuns a float
material_instance.get_material_scalar_parameter('Parameter name')
# returns a FVector
material_instance.get_material_vector_parameter('Parameter name')
# returns a Texture
material_instance.get_material_texture_parameter('Parameter name')

material_instance.set_material_scalar_parameter('Parameter name', float)
material_instance.set_material_vector_parameter('Parameter name', FVector)
material_instance.set_material_texture_parameter('Parameter name', Texture)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

WEB-Wordlist-Generator:为扫描后的Web应用生成相关联的字典

关于WEB-Wordlist-Generator WEB-Wordlist-Generator是一款功能强大的字典生成工具&#xff0c;该工具旨在帮助广大研究人员扫描目标Web应用程序并生成与之相关联的字典文件&#xff0c;从而允许我们对相关的网络威胁行为执行预备性应对策略。 功能介绍 当前版本的WEB-Wordli…

CAN总线学习笔记-CAN帧结构

数据帧 数据帧&#xff1a;发送设备主动发送数据&#xff08;广播式&#xff09; 标准格式的11ID不够用了&#xff0c;由此产生了扩展格式 SOF&#xff1a;帧起始&#xff0c;表示后面一段波形为传输的数据位 ID&#xff1a;标识符&#xff0c;区分功能&#xff0c;同时决定优…

旭日X3与英伟达Orin NX通过TCP传输图片

观前提醒&#xff1a;本文主要内容为使用Python在局域网内建立TCP连接并传输图片信息&#xff0c;计算机为一块旭日X3和一块英伟达Orin NX。 一、什么是TCP TCP&#xff08;传输控制协议&#xff09;是一种可靠的、面向连接的协议&#xff0c;它确保数据包的顺序传输和完整性…

工厂设备数采对接数字化平台

在数字化转型的浪潮下&#xff0c;工厂设备数据采集与数字化平台对接已成为提升生产效率、降低运维成本的关键手段。HiWoo Cloud作为工业物联网软件平台&#xff0c;以其卓越的性能和丰富的功能&#xff0c;为众多企业提供了全面、高效的设备数采对接数字化平台解决方案。本文将…

hutool工具实践-缓存

简介 依赖引入 <dependency><groupId>cn.hutool</groupId><artifactId>hutool-cache</artifactId><version>5.8.17</version></dependency> hutool工具既可以像上一章hutool工具实践-验证码-CSDN博客所说直接全部引入&#x…

用idea将java文件打成jar包

一、用idea将java文件打成jar包 1、在idea上选择file—Project Structure 2、Artifacts —点–JAR—From modules with dependencies 3、选择要打包的java文件 4、Build — Build Artifacts 5、找到刚才添加的Artifacts直接Build 6、生成jar包文件

钢轨行业的数字化转型:全生命周期管理与智能化决策支持

引言 随着时代的发展和技术的进步&#xff0c;数字化转型已经成为各行各业实现创新和提升竞争力的重要途径。在钢轨行业&#xff0c;数字化转型不仅是跟上时代潮流的必然选择&#xff0c;更是推动行业发展、提高效率和保障安全的关键举措。 钢轨作为铁路交通的基础设施之一&am…

余承东的“狂”,李想学不会

理想MEGA失利的连锁反应还在继续&#xff0c;李想第三次下调了今年的销量目标。 今年早些时候&#xff0c;李想还踌躇满志&#xff0c;提出今年销量目标为80万辆&#xff0c;这要比2023年37.6万辆的成绩翻了一倍不止,还喊出实现“中国市场豪华汽车品牌销量第一”的宣言。3月初…

ES6真题合集(一)

ES6真题合集&#xff08;一&#xff09; 1. var、let、const之间的区别2. ES6中数组新增了哪些扩展2.1 扩展运算符2.2 Array.from() 方法2.3 Array.of() 方法2.4 find() 和 findIndex() 方法2.5 箭头函数2.6 模板字符串 3. ES6中对象新增了哪些扩展3.1 属性的简写3.2 属性名表达…

ssh远程管理yum源进阶

文章目录 sshNFS 共享存储服务实验yum的进阶使用Apanche做一个网页形式的源用vsftpd做一个源混合源 ssh ssh是一种安全通道协议&#xff0c;用来实现字符界面的远程登录&#xff0c;远程复制&#xff0c;远程文本传输 ssh对通信双方的数据进行了加密 用户名和密码登录 秘钥…

GitHub最大的开源算法库

GitHub 上最大的开源算法库 The Algorithms&#xff0c;值得每位算法工程师收藏&#xff01; 该库收录了 Python、Java、C、JavaScript、Go 等多种主流编程语言的算法实现代码。 其中包含二分查找、快速排序、斐波那契数列等众多热门算法&#xff0c;可以说是应用尽有。 此外&a…

关于vue2 antd 碰到的问题总结下

1.关于vue2 antd 视图更新问题 1.一种强制更新 Vue2是通过用Object…defineProperty来设置数据的getter和setter实现对数据和以及视图改变的监听的。对于数组和对象这种引用类型来说&#xff0c;getter和setter无法检测到它们内部的变化。用这种 this.$set(this.form, "…

【问题复盘】第三方接口变慢导致服务崩溃

一、事件经过 -1、一个不在公司的下午&#xff0c;接到客户投诉&#xff0c;说平台不能访问了。 0、介入调查&#xff0c;发现服务器http请求无法访问&#xff0c;https请求却可以正常访问&#xff0c;一时有些无法理解&#xff1b;&#xff08;后来发现&#xff0c;http和htt…

CMMI软件能力成熟度评估标准

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl CMMI概述 CMMI&#xff0c;全称为Capability Maturity Model Integration&#xff0c;即能力成熟度模型集成&#xff0c;是在原有的CMM&#xff08;Capability Maturity Mo…

C语言小例程8/100

题目&#xff1a;输出特殊图案&#xff0c;请在c环境中运行&#xff0c;看一看 程序分析&#xff1a;字符共有256个。不同字符&#xff0c;图形不一样。 #include<stdio.h> int main() {char a176,b219;printf("%c%c%c%c%c\n",b,a,a,a,b);printf("%c%c%c…

【Python报错】已解决ModuleNotFoundError: No module named ‘packaging’

成功解决“ModuleNotFoundError: No module named ‘packaging’”错误的全面指南 在Python编程中&#xff0c;遇到ModuleNotFoundError: No module named packaging这样的错误&#xff0c;通常意味着你的Python环境中缺少名为packaging的模块&#xff0c;或者该模块没有被正确…

达梦 执行查询语句时报[-544]:Out of sort buf space

达梦数据库有时执行SQL中有时报[-544]:Out of sort buf space, try to adjust SORT_BUF_GLOBAL_SIZE, SORT_BUF_SIZE, SORT_BLK_SIZE. 第一反应是这条语句占用排序区太大。但真实原因是前面执行的语句耗光了全局排序区&#xff0c;后面SQL任何小的排序操作都会报这个错误从而执…

『哈哥赠书 - 54期』-『架构思维:从程序员到CTO』

文章目录 ⭐️ 架构思维&#xff1a;从程序员到CTO⭐️ 本书简介⭐️ 作者简介⭐️ 编辑推荐⭐️ 不想成为架构师的程序员不是好CTO 在程序员的职业规划中&#xff0c;成为软件架构师是一个非常有吸引力的选择。但是对于如何才能成为一名架构师&#xff0c;不少同学认为只要代码…

用langchain搭配最新模型ollama打造属于自己的gpt

langchain 前段时间去玩了一下langchain,熟悉了一下大模型的基本概念&#xff0c;使用等。前段时间meta的ollama模型发布了3.0,感觉还是比较强大的&#xff0c;在了解过后&#xff0c;自己去用前后端代码&#xff0c;调用ollama模型搭建了一个本地的gpt应用。 核心逻辑 开始搭…

智谱 GLM4 模型开源,意料之中的尺寸,意料之外的效果

最近智谱开了GLM-4-9B的模型&#xff0c;不是6B&#xff0c;是9B。 一共开源了四个模型&#xff0c;Base版本模型&#xff08;GLM-4-9B&#xff09;、Chat版本模型&#xff08;GLM-4-9B-Chat和GLM-4-9B-Chat-1M&#xff09;和多模态模型&#xff08;GLM-4V-9B-Chat&#xff09…