尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Unity集成Python开发指南:自动化工具与数据处理的实战应用

Unity集成Python开发指南:自动化工具与数据处理的实战应用 1. 项目概述为什么要在Unity里用Python如果你是一个Unity开发者或者对游戏开发、交互式内容创作感兴趣那你对C#一定不陌生。Unity的核心脚本语言就是C#它强大、高效是构建复杂游戏逻辑的不二之选。但最近几年我发现在一些特定场景下把Python引入Unity工作流能带来意想不到的效率和便利。这听起来可能有点“跨界”毕竟一个是游戏引擎一个是数据科学和自动化的宠儿但它们俩结合能擦出不少火花。简单来说Unity官方从某个版本开始通过一个名为com.unity.scripting.python的包正式支持了Python脚本。这可不是让你用Python去写游戏的核心战斗循环或者物理模拟——那种高性能需求的任务还是得交给C#。Python在Unity里的角色更像是一个强大的“自动化助手”和“数据桥梁”。想象一下你需要批量处理上百个模型资产的导入设置或者想用一段复杂的数学公式生成地形高度图又或者想快速连接一个外部API获取实时数据并驱动游戏内的UI。用C#写这些工具或胶水代码当然可以但Python凭借其简洁的语法、丰富的库如NumPy, Pandas, Requests和强大的脚本能力往往能让你几行代码就搞定大大提升了开发管线Pipeline的构建效率和原型验证速度。对于零基础的朋友你可能会担心“我既要学Unity又要学C#现在还要学Python” 别慌。这篇指南的目的就是帮你绕过那些复杂的配置坑手把手带你从零开始在Unity中搭建起Python环境并完成几个有代表性的实用案例。你会发现即使你之前没写过Python也能跟着一步步做出能实际运行的东西。我们的目标是“实用”和“轻松上手”让你快速感受到这种混合编程的魔力。2. 环境准备与核心原理拆解在开始写第一行代码之前我们必须把地基打好。Unity中使用Python并不是一个“开箱即用”的功能它依赖于一个独立的包并且需要正确的Python环境。理解背后的原理能帮你更好地排查未来可能遇到的问题。2.1 Unity版本与Python Scripting包首先最关键的一步是确认你的Unity版本。官方Python Scripting包是一个“预发布”Pre-release包这意味着它还在积极开发中可能某些版本存在不稳定。根据官方手册它从Unity 2021.2版本开始提供较完善的支持。我个人推荐使用Unity 2022.3 LTS长期支持版或更新版本LTS版本更稳定社区资源也更丰富。安装包的步骤很简单打开Unity进入Window - Package Manager。在Package Manager窗口左上角点击“Packages”下拉菜单选择“Unity Registry”。在搜索框中输入“Python”。你应该能看到一个名为“Python Scripting”的包注意它的版本状态可能是“Pre-release”。点击它然后点击右下角的“Install”按钮。注意安装这个包可能会花费一些时间因为它需要下载并集成 Python for .NETPython.NET的后端。安装过程中请保持网络通畅如果失败可以尝试重启Unity或检查网络代理设置。安装成功后你会在Unity编辑器的顶部菜单栏看到一个新的菜单项Python。这就说明环境的基础部分已经就位了。2.2 Python环境配置系统Python vs 嵌入式Python这是最容易混淆和出错的地方。Unity的Python Scripting包运行Python代码需要一个本地的Python解释器。它给你两种选择使用系统已安装的Python如果你电脑上已经装了Python比如通过python.org或Anaconda安装Unity可以尝试去找到并使用它。使用Unity内置的Python安装包时它会自动下载一个特定版本的、精简的Python环境通常是Python 3.9或3.10这个环境是便携式的只供Unity内部使用。我强烈建议初学者和大多数项目使用“Unity内置的Python”。理由如下环境隔离不会与你系统上其他Python项目所需的库版本冲突。一致性团队协作时所有人的Unity内部Python环境完全一致避免了“在我机器上好好的”这类问题。简单无需你额外配置系统环境变量。配置路径在Edit - PreferencesMac是Unity - Preferences然后找到Python选项卡。在这里你可以设置“Python Interpreter”路径。如果使用内置环境通常它会自动填好一个类似[YourProjectPath]/Library/PythonInstall的路径。确认这个路径存在即可。如果你想使用系统Python比如因为你需要用到一些已经用pip安装好的复杂库就在这里指定你的python.exe的完整路径例如C:\Users\YourName\AppData\Local\Programs\Python\Python39\python.exe。2.3 核心原理Python.NET 是如何工作的简单了解一下原理能让你更清楚Python脚本在Unity里能做什么、不能做什么。Unity的Python支持底层是基于Python for .NETPython.NET这个开源项目。你可以把它想象成一个“翻译官”。当你在Unity里执行Python代码时发生了以下事情Unity的C#核心.NET运行时启动了一个Python解释器进程。Python.NET在中间架起桥梁让Python代码可以调用C#.NET的类和方法这是最重要的能力。意味着你的Python脚本可以创建GameObject、修改组件属性、调用你写的C#方法等。本质上你是在用Python语法操作Unity引擎的C# API。被C#调用你也可以在C#脚本里启动和执行一段Python脚本并获取其结果。但是这个“翻译”是有成本的。每一次跨越Python和.NET边界的调用我们称之为“互操作”都会带来微小的性能开销。因此切忌在性能敏感的每帧更新如Update函数中执行大量的Python代码。Python的定位应该是编辑器工具脚本、数据预处理、批量操作等“一次性”或“低频次”的任务。3. 第一个Python脚本从编辑器工具开始理论说再多不如动手试一下。让我们创建一个最简单的Python脚本感受一下它在Unity编辑器里的工作方式。3.1 创建与执行Python脚本文件在Unity项目中Python脚本就是普通的.py文本文件。你可以在Project窗口的任何位置右键 -Create - Python Script来创建一个模板文件。不过直接在你喜欢的位置比如Assets/Editor/Python文件夹下新建一个文本文件然后把后缀改成.py也一样例如my_first_tool.py。用任何文本编辑器推荐VSCode、Sublime Text甚至Unity自带的编辑器打开这个文件输入以下代码import UnityEngine def say_hello(): # 访问UnityEngine的Debug类就像在C#里一样 UnityEngine.Debug.Log(Hello from Python!) # 我们甚至可以用Python的字符串格式化 editor_version UnityEngine.Application.unityVersion UnityEngine.Debug.Log(fUnity Editor Version: {editor_version}) # 当这个脚本在Unity编辑器中被直接运行时会执行下面的代码 if __name__ __main__: say_hello()保存文件后回到Unity编辑器。你有两种方式运行这个脚本菜单栏运行点击顶部Python菜单选择Run Python Script然后在弹出的文件选择框中找到你的my_first_tool.py文件并打开。右键菜单运行在Project窗口中右键点击你的.py文件选择Run Python Script。运行后你应该能在Unity的Console窗口看到输出的两行日志。恭喜你的第一个Unity Python脚本成功运行了。它通过import UnityEngine导入了核心的Unity引擎API然后像在C#里一样使用Debug.Log。3.2 构建一个实用的编辑器工具批量重命名光是打印日志显然不够实用。让我们做一个真正能提升效率的小工具批量重命名选中的游戏对象GameObject。在Assets/Editor文件夹下创建一个新文件batch_rename.py。Assets/Editor是个特殊文件夹里面的脚本只在Unity编辑器环境下运行不会被打包到游戏里非常适合放工具脚本。import UnityEngine import re def batch_rename_selected(): 批量重命名当前在Hierarchy中选中的所有GameObject。 规则在原名基础上添加前缀 Renamed_ # 获取当前选中的所有GameObject selected_objects UnityEngine.Selection.gameObjects if not selected_objects or len(selected_objects) 0: UnityEngine.Debug.LogWarning(没有选中任何GameObject。) return # 开启一个编辑器操作组这样所有的重命名操作可以被一次撤销 UnityEngine.Undo.RecordObjects(selected_objects, Batch Rename) rename_count 0 for obj in selected_objects: original_name obj.name # 简单的重命名逻辑添加前缀 new_name fRenamed_{original_name} obj.name new_name UnityEngine.Debug.Log(fRenamed: {original_name} - {new_name}) rename_count 1 UnityEngine.Debug.Log(f批量重命名完成共处理了 {rename_count} 个对象。) # 刷新编辑器界面让更改立即显示在Hierarchy中 UnityEngine.EditorApplication.RepaintHierarchyWindow() # 创建编辑器菜单项 # 这行代码会在Unity的菜单栏“Python”下添加一个子菜单项 UnityEngine.MenuItem(Python Tools/Batch Rename Selected Objects) def execute_batch_rename(): batch_rename_selected() if __name__ __main__: # 如果直接运行脚本也执行一次可选 batch_rename_selected()这段代码做了几件事UnityEngine.Selection.gameObjects获取了你在Hierarchy窗口里选中的物体列表。UnityEngine.Undo.RecordObjects是关键一步它告诉Unity的撤销系统接下来要对这些对象做的修改是一个整体操作。这样你按CtrlZ时可以一次性撤销所有重命名非常专业。遍历列表修改每个对象的name属性。UnityEngine.MenuItem是一个装饰器decorator它把这个Python函数注册成了一个Unity编辑器菜单项。保存脚本后你会在顶部菜单栏看到Python Tools - Batch Rename Selected Objects点击它就能运行。实操心得工具脚本放在Assets/Editor下是个好习惯。涉及修改场景或资源数据的操作务必使用Undo.RecordObject或Undo.RecordObjects来支持撤销这是编写可靠编辑器工具的基本素养。UnityEngine.EditorApplication.RepaintHierarchyWindow()可以强制刷新界面有时修改了数据但界面没更新调用这个就对了。你可以基于这个模板扩展出更复杂的功能比如用正则表达式re模块进行模式匹配和替换或者添加一个编辑器窗口EditorWindow来提供图形化参数设置界面。4. 深入交互Python与C#脚本的双向通信Python在Unity里真正的威力在于它能和你的C#代码“对话”。这意味着你可以用Python快速编写算法原型或数据处理逻辑然后让C#脚本来驱动游戏运行时的行为。4.1 在Python中调用自定义C#方法假设你有一个C#脚本DataProcessor.cs它挂载在一个GameObject上里面有一个处理数据的方法。// DataProcessor.cs using UnityEngine; public class DataProcessor : MonoBehaviour { // 一个可以被Python调用的静态方法 public static int AddNumbers(int a, int b) { Debug.Log($C#: Adding {a} and {b}); return a b; } // 一个实例方法需要先获取到对象实例 public string Greet(string name) { return $Hello, {name} from C# instance!; } }现在我们写一个Python脚本call_csharp.py来调用它import UnityEngine import clr # Python.NET 的核心模块用于加载.NET程序集 # 1. 调用静态方法非常简单就像调用UnityEngine的API一样 result_static UnityEngine.DataProcessor.AddNumbers(5, 3) UnityEngine.Debug.Log(fPython got result from static method: {result_static}) # 2. 调用实例方法需要先找到场景中的GameObject和组件 # 假设场景中有一个GameObject名字叫“Processor”上面挂了DataProcessor脚本 game_object UnityEngine.GameObject.Find(Processor) if game_object: # 获取组件 data_processor_component game_object.GetComponent[UnityEngine.DataProcessor]() if data_processor_component: result_instance data_processor_component.Greet(Python Developer) UnityEngine.Debug.Log(fPython got result from instance method: {result_instance}) else: UnityEngine.Debug.LogError(DataProcessor component not found on the GameObject.) else: UnityEngine.Debug.LogWarning(GameObject Processor not found in the scene.)关键点在于GetComponent[UnityEngine.DataProcessor]()这里的方括号语法这是Python.NET调用泛型方法的方式。它等同于C#中的GetComponentDataProcessor()。4.2 在C#中启动并获取Python脚本结果反过来我们也可以在C#里把一些计算密集型或需要特定Python库的任务交给Python脚本去完成。首先我们需要在Unity中启用对UnityEditor.Scripting.Python命名空间的访问这仅用于编辑器脚本。然后我们可以这样写一个C#编辑器工具// PythonRunner.cs - 放在Assets/Editor文件夹下 using UnityEditor; using UnityEditor.Scripting.Python; // 需要引用这个命名空间 using UnityEngine; public class PythonRunner : EditorWindow { [MenuItem(Python Tools/Run External Python Script)] static void RunPythonScript() { // 打开文件面板让用户选择一个.py文件 string scriptPath EditorUtility.OpenFilePanel(Select Python Script, Application.dataPath, py); if (string.IsNullOrEmpty(scriptPath)) { Debug.Log(No script selected.); return; } // 使用PythonRunner来执行脚本 // PythonRunner.RunFile 是同步执行的会阻塞编辑器直到脚本完成 try { PythonRunner.RunFile(scriptPath); Debug.Log($Python script executed: {scriptPath}); } catch (System.Exception e) { Debug.LogError($Failed to run Python script: {e.Message}); } } }但是PythonRunner.RunFile是“执行”脚本我们如何获取Python脚本计算后的返回值呢一个更常见的模式是让Python脚本将结果保存到一个双方约定的地方比如一个临时文件或者一个静态的C#类属性中然后C#再去读取。更优雅的双向通信模式 我们可以定义一个静态的C#类作为“数据交换站”。// PythonBridge.cs - 可以放在Assets/Scripts下不需要挂载 public static class PythonBridge { public static string LastPythonResult { get; set; } public static int CalculationResult { get; set; } }然后在Python脚本中我们直接设置这个静态类的属性# calculate_and_store.py import UnityEngine # 假设我们进行了一些复杂的计算这里用简单计算模拟 complex_result 42 * 100 # 将结果存放到C#的静态类中 UnityEngine.PythonBridge.CalculationResult complex_result UnityEngine.PythonBridge.LastPythonResult fCalculation done at {UnityEngine.Time.time} UnityEngine.Debug.Log(Python calculation completed and result stored in bridge.)最后在C#的任何地方比如一个按钮的点击事件里你都可以读取PythonBridge.CalculationResult来使用这个值。这种方式实现了松耦合的异步通信。5. 利用Python生态数据处理与自动化实战Python最大的优势是其庞大的第三方库生态系统。在Unity中我们可以利用这些库来处理游戏开发中繁琐的数据任务。5.1 使用Pandas读取和配置游戏数据假设你有一个平衡设计师用Excel维护的游戏角色属性表CharacterStats.xlsx你需要将这些数据导入到Unity中并生成对应的ScriptableObject或直接配置到Prefab上。步骤准备Python环境Unity内置的Python是精简版默认没有Pandas。我们需要为它安装库。打开系统命令行终端关键一步需要先激活Unity项目所用的Python环境。找到你项目下的Python解释器路径在Preferences - Python里可以看到例如YourProject/Library/PythonInstall/python.exe。使用这个python的pip进行安装。命令类似YourProject/Library/PythonInstall/python.exe -m pip install pandas openpyxlopenpyxl是用于读取Excel文件的引擎。编写数据处理脚本excel_to_unity.py:import UnityEngine import pandas as pd import os import json def import_character_stats(excel_path): 从Excel文件读取角色数据并打印或转换为Unity可用的格式。 # 使用pandas读取Excel df pd.read_excel(excel_path, sheet_nameSheet1) # 假设数据在Sheet1 UnityEngine.Debug.Log(f成功读取Excel文件共 {len(df)} 行数据。) UnityEngine.Debug.Log(f列名: {list(df.columns)}) # 示例将DataFrame转换为字典列表方便后续处理 characters_data df.to_dict(records) # 在这里你可以遍历 characters_data然后 # 1. 动态创建GameObject和组件并赋值。 # 2. 或者更常见的做法是生成一个JSON文件由C#去读取。 # 3. 或者直接调用你写好的C#方法将数据注入到现有的管理器中。 # 示例保存为JSON到Assets目录下 output_path UnityEngine.Application.dataPath /Resources/CharacterData.json with open(output_path, w, encodingutf-8) as f: json.dump(characters_data, f, ensure_asciiFalse, indent2) UnityEngine.Debug.Log(f角色数据已导出到: {output_path}) # 刷新Asset Database让Unity立刻识别新生成的JSON文件 UnityEngine.AssetDatabase.Refresh() # 使用示例你需要将下面的路径替换成你Excel文件的实际路径 if __name__ __main__: # 假设Excel文件放在Assets同级目录的Data文件夹里 project_path os.path.dirname(UnityEngine.Application.dataPath) excel_file os.path.join(project_path, Data, CharacterStats.xlsx) if os.path.exists(excel_file): import_character_stats(excel_file) else: UnityEngine.Debug.LogError(fExcel文件未找到: {excel_file})这个脚本展示了如何利用Pandas轻松处理结构化数据并输出为Unity容易加载的JSON格式。你可以将这个脚本设置为一个定期执行的编辑器工具每当Excel表格更新运行一下脚本就能同步游戏数据。5.2 使用Requests库获取网络数据驱动游戏内容想象一个场景你的游戏需要显示实时天气或者从服务器获取一些动态配置。你可以用Python快速写一个原型。# fetch_weather.py import UnityEngine import requests import json def fetch_weather_data(api_url): 从公开的天气API获取数据。请替换为真实可用的API 这是一个示例演示网络请求。 try: # 发送GET请求 response requests.get(api_url, timeout10) response.raise_for_status() # 如果状态码不是200抛出异常 weather_info response.json() # 解析数据这里根据实际API结构来写 city weather_info.get(name, Unknown) temp weather_info.get(main, {}).get(temp, 0) description weather_info.get(weather, [{}])[0].get(description, clear sky) result_text fWeather in {city}: {temp}°C, {description} UnityEngine.Debug.Log(result_text) # 将结果存储到我们之前定义的桥接类中供C#使用 UnityEngine.PythonBridge.LastPythonResult result_text return weather_info except requests.exceptions.RequestException as e: UnityEngine.Debug.LogError(f网络请求失败: {e}) return None except json.JSONDecodeError as e: UnityEngine.Debug.LogError(f解析JSON失败: {e}) return None # 示例API这是一个免费的示例API可能不稳定请自行寻找可靠的测试API # 例如https://api.openweathermap.org/data/2.5/weather?qLondonappidYOUR_API_KEYunitsmetric if __name__ __main__: # 重要使用真实API时请勿将密钥硬编码在脚本中 # 应该从环境变量或Unity的PlayerPrefs/配置文件中读取。 sample_url https://api.openweathermap.org/data/2.5/weather?qLondonappidyour_api_key_hereunitsmetric UnityEngine.Debug.LogWarning(请替换sample_url中的API密钥和城市参数。) # fetch_weather_data(sample_url) # 取消注释并配置后运行注意事项网络权限在Unity编辑器内运行网络请求通常没问题。但如果你的最终游戏需要联网并且使用了从Python脚本生成的代码或数据你需要确保目标平台如iOS、Android有正确的网络权限设置并且考虑使用Unity原生的UnityWebRequest进行最终实现以获得更好的兼容性和控制力。Python脚本在这里更适合做编辑器下的数据抓取和预处理。依赖管理像requests这样的库也需要用pip安装到Unity的Python环境中。安全绝对不要将API密钥等敏感信息直接写在脚本里。对于编辑器工具可以将其保存在系统的环境变量中Python通过os.environ读取。6. 常见问题与排查技巧实录在实际操作中你肯定会遇到各种报错。这里我整理了几个最常见的问题和解决方法都是我踩过坑后总结出来的。6.1 Python环境与包管理问题问题1ModuleNotFoundError: No module named ‘xxx’这是最常见的问题意味着Python解释器找不到你import的模块。原因A你用的是Unity内置Python但没安装那个包。解决用Unity内置Python的pip安装。找到解释器路径Preferences - Python在命令行中用完整路径执行安装例如项目路径/Library/PythonInstall/python.exe -m pip install pandas。原因B你指定了系统Python但包安装在另一个Python版本或虚拟环境中。解决在Unity的Python偏好设置中确认解释器路径是否正确指向了你安装包的那个Python环境。或者在正确的环境中重新安装包。问题2安装包时速度慢或超时解决使用国内镜像源。在pip安装命令后添加-i参数例如项目路径/Library/PythonInstall/python.exe -m pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple6.2 脚本执行与语法错误问题3运行脚本时无任何反应Console也没有报错可能原因脚本中有语法错误导致Python解释器在解析阶段就失败了有时Unity不会捕获这种错误并打印到Console。排查尝试在命令行中直接用Unity的Python解释器运行你的脚本可以看到更详细的错误信息。项目路径/Library/PythonInstall/python.exe 你的脚本路径.py问题4AttributeError: type object ‘XXX’ has no attribute ‘YYY’可能原因在Python中调用C#的API时方法名或属性名拼写错误或者该API在你当前使用的Unity版本中不存在。排查仔细检查拼写注意C#的公共方法/属性在Python中通常是同名的。去Unity官方脚本API文档https://docs.unity3d.com/ScriptReference/ 确认该API是否存在于你的Unity版本中。Python能调用的基本都是C# API的映射。6.3 性能与使用场景误区问题5在游戏运行时Play Mode使用Python脚本导致卡顿原因与建议这是最重要的一个坑。Python和.NET的互操作调用Marshalling是有开销的。绝对不要在Update()、FixedUpdate()或任何每帧执行的游戏逻辑循环中调用复杂的Python代码。正确姿势编辑器扩展Python的主场。用于制作资源导入处理工具、批量修改器、数据转换脚本等。这些只在编辑时运行。离线数据处理在游戏启动前如打包流程中用Python脚本预处理数据生成Unity可直接读取的资产如JSON, AssetBundles。低频后台任务如果必须在运行时使用应将其放在单独的线程或协程中并且仅用于低频、非实时性的任务例如解析一份从网上下载的配置数据。6.4 调试技巧打印日志UnityEngine.Debug.Log()是你最好的朋友。在Python脚本中大量使用它来输出变量状态和执行路径。使用Python原生打印print()语句的输出有时会重定向到Unity的Console但不如Debug.Log可靠。建议统一使用Debug.Log。在外部IDE调试高级可以配置VSCode或PyCharm来调试附加到Unity进程的Python脚本但这设置起来较为复杂。对于大多数工具脚本充分的日志输出已经足够定位问题。7. 项目实战构建一个自动化资源检查器让我们综合运用以上知识创建一个有点实际用处的工具一个自动扫描项目指定文件夹如Assets/Art/Textures检查纹理尺寸是否为2的幂PoT许多图形API的要求、格式是否正确并生成报告的工具。这个工具将涉及遍历文件系统、使用Unity的AssetDatabase API获取纹理信息、使用Python进行逻辑判断和报告生成。# texture_audit_tool.py import UnityEngine import os import sys from datetime import datetime def check_texture_settings(texture_path): 检查单个纹理的设置 # 通过AssetDatabase加载纹理为TextureImporter对象这样才能获取导入设置 importer UnityEngine.AssetImporter.GetAtPath(texture_path) as UnityEngine.TextureImporter if not importer: return None asset UnityEngine.AssetDatabase.LoadAssetAtPath(texture_path, UnityEngine.Texture2D) if not asset: return None issues [] # 检查尺寸是否为2的幂 width asset.width height asset.height def is_power_of_two(n): return (n (n - 1)) 0 and n ! 0 if not is_power_of_two(width) or not is_power_of_two(height): issues.append(f非2的幂尺寸: {width}x{height}) # 检查纹理类型设置例如普通纹理还是法线贴图 # 这里只是示例你可以根据项目规范检查更多设置如sRGB、压缩格式等 if importer.textureType ! UnityEngine.TextureImporterType.Default: # 假设我们要求所有漫射贴图都是Default类型 issues.append(f纹理类型为 {importer.textureType}非Default) # 检查Max Size是否合适假设我们要求不超过2048 if importer.maxTextureSize 2048: issues.append(fMax Size设置过大: {importer.maxTextureSize}) return { path: texture_path, width: width, height: height, importer_type: str(importer.textureType), max_size: importer.maxTextureSize, issues: issues, has_issues: len(issues) 0 } def audit_textures_in_folder(folder_pathAssets/Art/Textures): 审计指定文件夹下的所有纹理 UnityEngine.Debug.Log(f开始审计纹理文件夹: {folder_path}) if not UnityEngine.AssetDatabase.IsValidFolder(folder_path): UnityEngine.Debug.LogError(f文件夹无效: {folder_path}) return # 获取文件夹下所有资产GUID guids UnityEngine.AssetDatabase.FindAssets(t:Texture2D, [folder_path]) audit_results [] problematic_textures [] for guid in guids: asset_path UnityEngine.AssetDatabase.GUIDToAssetPath(guid) # 只处理特定后缀排除.meta文件等 if asset_path.lower().endswith((.png, .jpg, .jpeg, .tga, .psd, .bmp)): result check_texture_settings(asset_path) if result: audit_results.append(result) if result[has_issues]: problematic_textures.append(result) # 生成报告 report_time datetime.now().strftime(%Y-%m-%d %H:%M:%S) report_lines [] report_lines.append(f 纹理审计报告 ) report_lines.append(f时间: {report_time}) report_lines.append(f扫描文件夹: {folder_path}) report_lines.append(f扫描纹理数量: {len(audit_results)}) report_lines.append(f存在问题纹理: {len(problematic_textures)}) report_lines.append() report_lines.append(--- 问题详情 ---) for tex in problematic_textures: report_lines.append(f纹理: {tex[path]}) report_lines.append(f 尺寸: {tex[width]}x{tex[height]}) for issue in tex[issues]: report_lines.append(f - {issue}) report_lines.append() # 空行分隔 report_text \n.join(report_lines) # 输出到Unity Console for line in report_lines: if 问题详情 in line or in line or --- in line: UnityEngine.Debug.LogWarning(line) # 用Warning级别高亮标题 else: UnityEngine.Debug.Log(line) # 将报告保存为文本文件 report_file_path UnityEngine.Application.dataPath /../TextureAuditReport.txt try: with open(report_file_path, w, encodingutf-8) as f: f.write(report_text) UnityEngine.Debug.Log(f完整报告已保存至: {report_file_path}) except Exception as e: UnityEngine.Debug.LogError(f保存报告文件失败: {e}) # 在Project窗口高亮显示有问题的纹理可选 if problematic_textures: selection_guids [] for tex in problematic_textures: guid UnityEngine.AssetDatabase.AssetPathToGUID(tex[path]) if guid: selection_guids.append(guid) # 这里需要调用一个内部API来高亮选择操作较复杂通常输出路径就够了。 UnityEngine.Debug.Log(请在Console中查看问题纹理路径。) return problematic_textures # 创建菜单项 UnityEngine.MenuItem(Assets/Audit Selected Textures, priority100) def audit_selected_textures_menu(): 审计当前在Project窗口选中的纹理 selected_objects UnityEngine.Selection.objects texture_paths [] for obj in selected_objects: if isinstance(obj, UnityEngine.Texture2D): path UnityEngine.AssetDatabase.GetAssetPath(obj) if path: texture_paths.append(path) if not texture_paths: # 如果没有选中纹理或者选中的是文件夹则审计整个选中文件夹 selected_paths [UnityEngine.AssetDatabase.GetAssetPath(obj) for obj in selected_objects if obj] for path in selected_paths: if UnityEngine.AssetDatabase.IsValidFolder(path): audit_textures_in_folder(path) return UnityEngine.Debug.LogWarning(请选中一个文件夹或若干纹理文件。) return # 审计选中的纹理 for path in texture_paths: result check_texture_settings(path) if result and result[has_issues]: for issue in result[issues]: UnityEngine.Debug.LogWarning(f{path}: {issue}) UnityEngine.MenuItem(Tools/Python/Audit All Textures in Project) def audit_all_textures_menu(): 审计整个项目中的所有纹理谨慎使用项目大时可能很慢 if UnityEngine.EditorUtility.DisplayDialog(纹理审计, 这将扫描项目中所有纹理可能耗时较长。确认继续, 是, 否): # 可以递归扫描所有文件夹这里简单起见扫描Assets根目录 guids UnityEngine.AssetDatabase.FindAssets(t:Texture2D) UnityEngine.Debug.Log(f开始审计全项目纹理共找到 {len(guids)} 个纹理。) # 实际处理... 为避免卡死编辑器对于大量资产应考虑分帧处理或进度条。 UnityEngine.Debug.LogWarning(全项目审计功能较耗时建议使用文件夹审计。) if __name__ __main__: # 默认运行审计常用的纹理文件夹 audit_textures_in_folder(Assets/Textures)这个实战项目涵盖了多个关键点资产路径操作使用AssetDatabase相关API。类型检查与转换Python中使用as进行类型转换。业务逻辑封装将检查逻辑单独写成函数。报告生成结合Python的字符串和文件操作生成易于阅读的文本报告。编辑器集成通过MenuItem创建了右键菜单和Tools菜单方便使用。你可以根据需要扩展检查规则例如检查MipMap是否开启、压缩格式是否为ASTC等。这个工具能帮助美术和开发人员在项目早期发现资源规范问题避免在打包或运行时出现意外。
返回列表