UE4编辑器扩展之生成新关卡并将配置添加到xlsx

UE4编辑器扩展之生成新关卡并自动相应配置添加到xls

一、创建一个关卡资源

1.1 创建UE4的资源

无论是创建要给关卡、还是材质还是其它资源,逻辑基本一致,这里会给出几种资源类型创建的例子

import unreal
import os
import sys

def createGenericAsset(asset_path = '', unique_name = True, asset_class = None, asset_factory = None):
    if unique_name:
        asset_path, asset_name = unreal.AssetToolsHelpers.get_asset_tools().create_unique_asset_name(base_package_name = asset_path, suffix = '')
    if not unreal.EditorAssetLibrary.does_asset_exist(asset_path = asset_path):
        path = asset_path.rsplit('/', 1)[0]
        name = asset_path.rsplit('/', 1)[1]
        return asset = unreal.AssetToolsHelpers.get_asset_tools().create_asset(asset_name = name, package_path = path, asset_class= asset_class, factory = asset_factory)
    
    return unreal.EditorAssetLibrary.load_asset(asset_path)


def createGenericAsset_Example():
    base_path = '/Game/GenericAssets/'
    generic_assets = [
        [base_path + 'sequence', unreal.LevelSequence, unreal.LevelSequenceFactoryNew()],
        [base_path + 'material', unreal.Material, unreal.MaterialFactoryNew()],
        [base_path + 'world', unreal.World, unreal.WorldFactory()],
        [base_path + 'texture2D', unreal.Texture2D, unreal.Texture2DFactoryNew()],
        [base_path + 'paticle_system', unreal.ParticleSystem, unreal.ParticleSystemFactoryNew()],
        # [base_path + 'data_table', unreal.DataTable, unreal.LevelSequenceFactoryNew()], # not work
    ]
    for asset in generic_assets:
        print(createGenericAsset(asset[0], True, asset[1], asset[2]))

createGenericAsset_Example()

使用如上逻辑就可以创建各种类型的资源,但是有一种资源比较特殊,就是map,在使用load_asset时,会发现加载失败;
而且各种资源创建以后,在进行直接保存时会调用unreal.EditorAssetLibrary.save_asset,如果asset是map,也会报错;
参考:UE 4.22 - How To Create Generic Assets With Python:https://www.youtube.com/watch?v=1eWt1AOdCg0

1.2 资源类型的判断

我们可能需要去判断一个资源的类型,并根据资源类型做不同的逻辑处理,针对上述问题,我们就要过滤出map类型的资源;
在UE4的UObjectBaseUtility类中,有一个IsA()方法用来判定类型,但是在python api中我没有找到这个api,不过找到了一个替代性的api,unreal.MathLibrary.class_is_child_of,至于这个为什么出现在MathLibrary,我也不知道;
使用这个方法,我们对加载创建好的资源做如下判断,然后对于关卡的保存需要使用一个新的接口unreal.EditorLoadingAndSavingUtils.save_map

if unreal.MathLibrary.class_is_child_of(asset.static_class(), unreal.World):
    unreal.EditorLoadingAndSavingUtils.save_map(asset, asset_path)
else:
    unreal.EditorAssetLibrary.save_asset(asset_path)

1.3 关卡资源的保存

使用unreal.EditorLoadingAndSavingUtils.save_map这个api,是需要提供第一个参数asset的引用的,但是如果我们只知道asset_path,想要保存一个关卡呢?
第一种方式是可以加载这个关卡,然后保存;

    asset = unreal.EditorLoadingAndSavingUtils.load_map(asset_path)
    unreal.EditorLoadingAndSavingUtils.save_map(asset, asset_path)

但是这种方式会涉及到切换关卡,而如果我们不希望切换关卡呢
第二种方式:首先可以获取所有的脏地图的package,然后拿到package的路径即脏地图的路径,最后调用
unreal.EditorLoadingAndSavingUtils.save_packages([], bool)即可;

def Save_Map():
    maparray = unreal.EditorLoadingAndSavingUtils.get_dirty_map_packages()
    for asset in maparray:
        if asset.get_path_name() == asset_path :
            unreal.EditorLoadingAndSavingUtils.save_packages([asset], True)
            return true
    return false

二、制作创建关卡的界面

有了第一节内容后,我们可以先封装一个创建关卡的接口

def CreateLevel(path, name):
    createGenericAsset(os.path.join(path, name), True, unreal.World, unreal.WorldFactory())

base_path = '/Game/GenericAssets'
CreateLevel(base_path, 'world')

path和name两个参数自然就是由使用者通过窗口传入了;

2.1 窗口的制作
如果使用蓝图制作,我不知道怎么在调用python时将参数传入(如果有人知道,评论告诉我一下哈),因此就选用了pyqt5;

class CheckWidget(QWidget):
    def __init__(self):
        self.levelPath = unreal.SystemLibrary.get_project_content_directory()
        super().__init__()
        self.initUI()
 
    def initUI(self):
        # 关卡名输入UI
        label = QLabel(u'关卡名:', self)
        label.move(40, 55)
        self.qle_name = QLineEdit(self)
        self.qle_name.move(120, 55)
        # 关卡路径选择UI
        label = QLabel(u'关卡目录:', self)
        label.move(40, 80)
        self.qle_dir = QLineEdit(self)
        self.qle_dir.move(120, 80)
        self.qle_dir.setText(os.path.abspath(self.levelPath))
        btn = QPushButton(u'选择文件夹', self)
        btn.clicked.connect(self.openFoler)
        btn.resize(btn.sizeHint())
        btn.move(400, 80)
        # 创建按钮
        btn = QPushButton(u'创建', self)
        btn.clicked.connect(self.createLevel)
        btn.resize(btn.sizeHint())
        btn.move(40, 100)

        self.setGeometry(300, 300, 800, 600)
        self.center()
        self.setWindowTitle(u'关卡创建工具')
        self.show()

三、配置写入到xlsx中

使用openpyxl库来完成这个功能

    def writeToConfig(self, levelName, levelDir):
        cfgPath = os.path.join(unreal.SystemLibrary.get_project_directory(), 'TestConfig', 'level.xlsx')
        workbook = openpyxl.load_workbook(cfgPath)
        sheet1 = workbook['Sheet']
        row = sheet1.min_row + 1
        sheet1.cell(row, 1).value = row - 1
        sheet1.cell(row, 2).value = levelName
        sheet1.cell(row, 3).value = levelDir
        workbook.save(cfgPath)

四、完整代码

import unreal
import re
import os
import sys
from PyQt5.QtWidgets import (QApplication, QWidget, QToolTip, QPushButton, QMessageBox, QDesktopWidget, QFileDialog, QLabel, QLineEdit)
from PyQt5.QtGui import (QIcon, QFont)
import openpyxl
from openpyxl import Workbook

def createGenericAsset(asset_path = '', unique_name = True, asset_class = None, asset_factory = None):
    # if unique_name:
    #     asset_path, asset_name = unreal.AssetToolsHelpers.get_asset_tools().create_unique_asset_name(base_package_name = asset_path, suffix = '')
    #     print(asset_path)
    if not unreal.EditorAssetLibrary.does_asset_exist(asset_path = asset_path):
        path = asset_path.rsplit('/', 1)[0]
        name = asset_path.rsplit('/', 1)[1]
        asset = unreal.AssetToolsHelpers.get_asset_tools().create_asset(asset_name = name, package_path = path, asset_class= asset_class, factory = asset_factory)
        if unreal.MathLibrary.class_is_child_of(asset.static_class(), unreal.World):
            unreal.EditorLoadingAndSavingUtils.save_map(asset, asset_path)
        else:
            unreal.EditorAssetLibrary.save_asset(asset_path)
        return asset
    
    # return unreal.EditorAssetLibrary.load_asset(asset_path)
    # asset = unreal.EditorLoadingAndSavingUtils.load_map(asset_path)
    # unreal.EditorLoadingAndSavingUtils.save_map(asset, asset_path)
    assetType = unreal.PrimaryAssetType(asset_path)
    print(type(unreal.World))
    print(type(assetType))
    print(assetType.name)
    maparray = unreal.EditorLoadingAndSavingUtils.get_dirty_map_packages()
    for asset in maparray:
        if asset.get_path_name() == asset_path :
            unreal.EditorLoadingAndSavingUtils.save_packages([asset], True)
            break

def Save_Map():
    maparray = unreal.EditorLoadingAndSavingUtils.get_dirty_map_packages()
    for asset in maparray:
        if asset.get_path_name() == asset_path :
            unreal.EditorLoadingAndSavingUtils.save_packages([asset], True)
            return true
    return false

def createGenericAsset_Example():
    base_path = '/Game/GenericAssets/'
    generic_assets = [
        [base_path + 'sequence', unreal.LevelSequence, unreal.LevelSequenceFactoryNew()],
        [base_path + 'material', unreal.Material, unreal.MaterialFactoryNew()],
        [base_path + 'world', unreal.World, unreal.WorldFactory()],
        [base_path + 'texture2D', unreal.Texture2D, unreal.Texture2DFactoryNew()],
        [base_path + 'paticle_system', unreal.ParticleSystem, unreal.ParticleSystemFactoryNew()],
        # [base_path + 'data_table', unreal.DataTable, unreal.LevelSequenceFactoryNew()], # not work
    ]
    for asset in generic_assets:
        print(createGenericAsset(asset[0], True, asset[1], asset[2]))

def CreateLevel(path, name):
    createGenericAsset(os.path.join(path, name), True, unreal.World, unreal.WorldFactory())

def GetPackagePath(absolutePath):
    packagePath = absolutePath.replace('\\', '/')
    pro_dir = unreal.SystemLibrary.get_project_content_directory()
    return packagePath.replace(pro_dir, '/Game/')

 
class CheckWidget(QWidget):
    def __init__(self):
        self.levelPath = unreal.SystemLibrary.get_project_content_directory()
        super().__init__()
        self.initUI()
 
    def initUI(self):
        # 关卡名输入UI
        label = QLabel(u'关卡名:', self)
        label.move(40, 55)
        self.qle_name = QLineEdit(self)
        self.qle_name.move(120, 55)
        # 关卡路径选择UI
        label = QLabel(u'关卡目录:', self)
        label.move(40, 80)
        self.qle_dir = QLineEdit(self)
        self.qle_dir.move(120, 80)
        self.qle_dir.setText(os.path.abspath(self.levelPath))
        btn = QPushButton(u'选择文件夹', self)
        btn.clicked.connect(self.openFoler)
        btn.resize(btn.sizeHint())
        btn.move(400, 80)
        # 创建按钮
        btn = QPushButton(u'创建', self)
        btn.clicked.connect(self.createLevel)
        btn.resize(btn.sizeHint())
        btn.move(40, 100)

        self.setGeometry(300, 300, 800, 600)
        self.center()
        self.setWindowTitle(u'关卡创建工具')
        self.show()
 
 
    def center(self):
        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())
 
 
    def openFoler(self):
        path = QFileDialog.getExistingDirectory(self, u'选择文件夹', self.levelPath)
        if path != '' :
            self.levelPath = path
        self.qle_dir.setText(os.path.abspath(self.levelPath))

 
    def createLevel(self):
        levelName = self.qle_name.text()
        levelDir = GetPackagePath(self.qle_dir.text())
        CreateLevel(levelDir + '/' + levelName, levelName)
        self.writeToConfig(levelName, levelDir + '/' + levelName + '/' + levelName)
        QMessageBox.information(self, u'结果',
                                     u"创建成功", QMessageBox.Yes)

    def writeToConfig(self, levelName, levelDir):
        cfgPath = os.path.join(unreal.SystemLibrary.get_project_directory(), 'TestConfig', 'level.xlsx')
        if not os.path.exists(cfgPath):
            cfgDirPath = os.path.join(unreal.SystemLibrary.get_project_directory(), 'TestConfig')
            if not os.path.exists(cfgDirPath):
                os.mkdir(cfgDirPath)
            self.createxlsx(cfgPath)
        workbook = openpyxl.load_workbook(cfgPath)
        sheet = workbook['Sheet']
        row = sheet.min_row + 1
        sheet.cell(row, 1).value = row - 1
        sheet.cell(row, 2).value = levelName
        sheet.cell(row, 3).value = levelDir
        workbook.save(cfgPath)

    def createxlsx(self, path):
        wb = Workbook()
        sheet = wb['Sheet']
        sheet.cell(1, 1).value = 'id'
        sheet.cell(1, 2).value = 'name'
        sheet.cell(1, 3).value = 'path'
        wb.save(path)


def main():
    app = QApplication(sys.argv)
    ex = CheckWidget()
    sys.exit(app.exec_())
 
if __name__ == '__main__':
    main()

你可能感兴趣的:(UE4编辑器扩展)