UE Editor API 整理

news2024/11/26 0:44:33

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)

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

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

相关文章

查看远程桌面端口,查看服务器的远程桌面端口的方法

如果你正在寻找一种方法来检查服务器的远程桌面端口&#xff0c;那么请务必按照以下步骤操作&#xff0c;以确保准确且安全地获取所需信息。这不仅是一个技术问题&#xff0c;更是一个关于效率和安全性的重要议题。 首先&#xff0c;你需要明确&#xff0c;远程桌面端口通常是…

leetcode第263题:丑数

丑数的因子只能是2,3,5。但是可能有多个2&#xff0c;多个3&#xff0c;多个5.因此需要循环地除以2、3、5. public class Solution {public bool IsUgly(int n) {if (n < 0) {return false;}int[] factors {2, 3, 5};for ( int i0;i<3;i) {int factorfactors[i];while …

Vue-插槽 Slots

文章目录 前言什么叫插槽简单插槽指定默认值多个插槽根据父级别名称指定区域显示(具名插槽)作用域插槽 前言 本篇文章不做过多的讲解与说明&#xff0c;只记录个人实验测试案例。 详见&#xff1a;vue 官方文档 插槽 slots 什么叫插槽 之前的博客中&#xff0c;父级组件可以…

深入理解feign远程调用的各种超时参数

1. 引言 在spring cloud微服中&#xff0c;feign远程调用可能是大家每天都接触到东西&#xff0c;但很多同学却没咋搞清楚这里边的各种超时问题&#xff0c;生产环境可能会蹦出各种奇怪的问题。 首先说下结论&#xff1a; 1)只使用feign组件&#xff0c;不使用ribbion组件&…

[Vulfocus解题系列]spring 命令执行(CVE-2022-22947)

环境部署 使用docker部署环境 漏洞等级&#xff1a;高危 3 月 1 日&#xff0c;VMware 官方发布安全公告&#xff0c;声明对 Spring Cloud Gateway 中的一处命令注入漏洞进行了修复&#xff0c;漏洞编号为CVE-2022-22947 Spring官方发布 漏洞描述 使用 Spring Cloud Gate…

mysql启动出现Error: 2 (No such file or directory)

查看mydql状态 systemctl status mysqlThe designated data directory /var/lib/mysql/ is unusable 查看mysql日志 tail -f /var/log/mysql/error.logtail: cannot open ‘/var/log/mysql/error.log’ for reading: No such file or directory tail: no files remaining 第…

File类操作文件方法详解及其简单应用

一、File 类介绍 Java 中的 File 类是 java.io 包的一部分&#xff0c;它提供了操作文件和目录的能力。File 类可以用来表示文件系统中的文件或目录。 二、路径 在讲File用法之前咱们先介绍一下路径是什么&#xff1f; 在计算机中&#xff0c;路径&#xff08;Path&#xff0…

6.更复杂的光照

一、Unity的渲染路径 渲染路径决定了光照是如何应用到Unity Shader中的。我们需要为每个Pass指定它使用的渲染路径 如何设置渲染路径&#xff1f; Edit>Project Settings>Player>Other Settinigs>Rendering 如何使用多个渲染路径&#xff1f;如&#xff1a;摄像…

Linux系统使用Docker安装Drupal结合内网穿透实现远程访问管理后台

目录 前言 1. Docker安装Drupal 2. 本地局域网访问 3 . Linux 安装cpolar 4. 配置Drupal公网访问地址 5. 公网远程访问Drupal 6. 固定Drupal 公网地址 前言 作者简介&#xff1a; 懒大王敲代码&#xff0c;计算机专业应届生 今天给大家聊聊Linux系统使用Docker安装Drupal…

三十四篇:办公效率革命:深入探索办公自动化系统的全面策略

办公效率革命&#xff1a;深入探索办公自动化系统的全面策略 1. 引言 1.1 办公自动化系统&#xff08;OAS&#xff09;的定义与关键作用 在当前的企业环境中&#xff0c;办公自动化系统&#xff08;Office Automation System, OAS&#xff09;已成为提高效率和执行力的关键技…

沃通CA参与《证书透明规范》及《自动化证书管理规范》两项商密标准制定

沃通CA加入由零信技术牵头的两项商密标准《证书透明规范》及《自动化证书管理规范》编制工作。沃通CA作为国内依法设立的电子认证服务机构与领先的SSL证书服务商&#xff0c;很荣幸参与到两项商密标准的编制工作中&#xff0c;不仅提供多年SSL证书领域的应用经验&#xff0c;还…

智能管理,无忧报修——高校校园报事报修系统小程序全解析

随着数字化、智能化的发展&#xff0c;高校生活也迎来了前所未有的变革。你是否还在为宿舍的水龙头漏水、图书馆的灯光闪烁而烦恼&#xff1f;你是否还在为报修流程繁琐、等待时间长而焦虑&#xff1f;今天&#xff0c;这一切都将成为过去式&#xff01;因为一款震撼高校圈的新…

通过 SFP 接口实现千兆光纤以太网通信4

Tri Mode Ethernet MAC 与 1G/2.5G Ethernet PCS/PMA or SGMII 的连接 在设计中&#xff0c;需要将 Tri Mode Ethernet MAC 与 1G/2.5G Ethernet PCS/PMA or SGMII 之间通过 GMII 接口互联。Tri Mode Ethernet MAC IP 核的工作时钟源为 1G/2.5G Ethernet PCS/PMA or SGMII …

【教程】从0开始搭建大语言模型:文本预处理

从0开始搭建大语言模型&#xff1a;文本预处理 参考仓库&#xff1a;LLMs-from-scratch 理解Word embedding 深度神经网络模型&#xff0c;包括LLM&#xff0c;不能直接处理原始文本&#xff0c;因此需要一种方法将它转换为连续值的向量&#xff0c;也就是embedding。如下图…

Sql注入-报错注入

报错注入&#xff08;Error-Based Injection&#xff09;是一种通过引起数据库报错并从错误信息中提取有用信息的SQL注入攻击手法&#xff1b;攻击者利用数据库在处理异常情况时返回的错误消息&#xff0c;来推断出数据库结构、字段名甚至数据内容&#xff1b;这种攻击方法依赖…

【html】如何用html+css写出一个漂亮的“众成教育”页面

先来看看效果图&#xff1a; 源码&#xff1a; <!DOCTYPE html> <html><head><meta charset"utf-8" /><title></title><style>* {margin: 0;padding: 0;/* border: 2px solid #000; */}.con {width: 1000px;height: 840px…

redis之发布与订阅

华子目录 什么是发布与订阅&#xff1f;常用命令psubscribe pattern1 [pattern2...]subscribe channel1 [channel2...]publish channel messagepunsubscribe pattern1 [pattern2...]unsubscribe [channel1 [channel2...]]pubsub subcommand argument1 [argument2...] 示例1示例…

JAVAEE之网络初识_协议、TCP/IP网络模型、封装、分用

前言 在这一节我们简单介绍一下网络的发展 一、通信网络基础 网络互连的目的是进行网络通信&#xff0c;也即是网络数据传输&#xff0c;更具体一点&#xff0c;是网络主机中的不同进程间&#xff0c;基于网络传输数据。那么&#xff0c;在组建的网络中&#xff0c;如何判断到…

MicroPython教程:ESP8266 快速参考

ESP8266 快速参考 Adafruit Feather HUZZAH 板&#xff08;图片来源&#xff1a;Adafruit&#xff09;。 以下是基于 ESP8266 的开发板的快速参考。如果这是您第一次使用该板&#xff0c;请考虑先阅读以下部分&#xff1a; 关于 ESP8266 端口的一般信息ESP8266 的 MicroPytho…

大中小面积紫外光老化加速试验机装置

高低温试验箱,振动试验台,紫外老化试验箱,氙灯老化试验箱,沙尘试验箱,箱式淋雨试验箱,臭氧老化试验箱,换气老化试验箱,电热鼓风干燥箱,真空干燥箱&#xff0c;超声波清洗机&#xff0c;盐雾试验箱 一、产品用途 紫外光加速老化试验装置采用荧光紫外灯为光源,通过模拟自然阳光中…