├── Scripts.meta ├── Scripts ├── Editor.meta ├── Editor │ ├── BatchEditor.meta │ ├── BatchEditor │ │ ├── Helper.meta │ │ ├── BatchEditor.cs.meta │ │ ├── BatchEditHelperBase.cs.meta │ │ ├── Helper │ │ │ ├── BatchEditUIFontHelper.cs.meta │ │ │ ├── BatchEditUISpriteHelper.cs.meta │ │ │ ├── BatchEditUISpriteHelper.cs │ │ │ └── BatchEditUIFontHelper.cs │ │ ├── BatchEditHelperBase.cs │ │ └── BatchEditor.cs │ ├── Utility │ │ ├── TypeUtility.cs.meta │ │ ├── PrefabUtility.cs.meta │ │ ├── PrefabUtility.cs │ │ └── TypeUtility.cs │ ├── GameObjectClone │ │ ├── GameObjectClone.cs.meta │ │ └── GameObjectClone.cs │ ├── ComponentCollection │ │ ├── IComponentCollector.cs.meta │ │ ├── ICollectionGenerator.cs.meta │ │ ├── ComponentCollectionInspector.cs.meta │ │ ├── ComponentCollectionSettings.cs.meta │ │ ├── DefaultCollectionGenerator.cs.meta │ │ ├── DefaultComponentCollector.cs.meta │ │ ├── Config.meta │ │ ├── Config │ │ │ ├── ComponentBehaviourCodeTemplate.txt.meta │ │ │ ├── ComponentCollectionCodeTemplate.txt.meta │ │ │ ├── ComponentCollectionExtensionCodeTemplete.txt.meta │ │ │ ├── ComponentCollectionSettings.asset.meta │ │ │ ├── ComponentBehaviourCodeTemplate.txt │ │ │ ├── ComponentCollectionExtensionCodeTemplete.txt │ │ │ ├── ComponentCollectionCodeTemplate.txt │ │ │ └── ComponentCollectionSettings.asset │ │ ├── ComponentCollectionSettingsInspector.cs.meta │ │ ├── IComponentCollector.cs │ │ ├── ICollectionGenerator.cs │ │ ├── DefaultCollectionGenerator.cs │ │ ├── DefaultComponentCollector.cs │ │ ├── ComponentCollectionSettings.cs │ │ ├── ComponentCollectionSettingsInspector.cs │ │ └── ComponentCollectionInspector.cs │ ├── Utility.meta │ ├── GUIStyleViewer.meta │ ├── UGUIExtension.meta │ ├── ComponentCollection.meta │ ├── GameObjectClone.meta │ ├── UGUIExtension │ │ ├── UGUICreator.cs.meta │ │ ├── UGUIMenuExtension.cs.meta │ │ ├── UGUIRaycastTargetDebug.cs.meta │ │ ├── UGUIRaycastTargetDebug.cs │ │ ├── UGUIMenuExtension.cs │ │ └── UGUICreator.cs │ └── GUIStyleViewer │ │ ├── GUIStyleViewer.cs.meta │ │ └── GUIStyleViewer.cs ├── Runtime │ ├── UGUIExtension │ │ ├── PolygonImage.cs.meta │ │ ├── RectRaycast2D.cs.meta │ │ ├── RectRaycast2D.cs │ │ └── PolygonImage.cs │ ├── ComponentCollection │ │ ├── ComponentCollection.cs.meta │ │ └── ComponentCollection.cs │ ├── UGUIExtension.meta │ └── ComponentCollection.meta └── Runtime.meta ├── Readme ├── GUIStyleViewer.md ├── GameObjectClone.md ├── BatchEditor.md.meta ├── GUIStyleViewer.md.meta ├── GameObjectClone.md.meta ├── UGUIExtension.md.meta ├── ComponentCollection.md.meta ├── BatchEditor.md ├── UGUIExtension.md └── ComponentCollection.md ├── LICENSE.meta ├── README.md.meta ├── Readme.meta ├── README.md └── LICENSE /Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2b24dcef064e40a8b02e990d01ba3b13 3 | timeCreated: 1659062435 -------------------------------------------------------------------------------- /Scripts/Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2a9323536fdf4b5abcf5b0b088377b00 3 | timeCreated: 1659062444 -------------------------------------------------------------------------------- /Readme/GUIStyleViewer.md: -------------------------------------------------------------------------------- 1 | # GUIStyleViewer 2 | 3 | 编辑器 GUIStyle 查看器。 4 | 5 | 支持查找。 6 | 7 | 支持点击右侧效果或选择左侧字符复制到剪贴板。 8 | -------------------------------------------------------------------------------- /Scripts/Editor/BatchEditor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2073a6981bb44b09aab0bde8011bf884 3 | timeCreated: 1658972249 -------------------------------------------------------------------------------- /Scripts/Editor/BatchEditor/Helper.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ed1ac9e764874c08985435cf248bd17d 3 | timeCreated: 1658996270 -------------------------------------------------------------------------------- /Scripts/Editor/Utility/TypeUtility.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 13560e23b5bc4944aedcf0d5893cc28a 3 | timeCreated: 1658988608 -------------------------------------------------------------------------------- /Scripts/Editor/BatchEditor/BatchEditor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bb82aa0d1b5244b1ad55c4d2248bc310 3 | timeCreated: 1658971701 -------------------------------------------------------------------------------- /Scripts/Editor/Utility/PrefabUtility.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c40ac055731a4331bef5c1017b0c9d1d 3 | timeCreated: 1659074230 -------------------------------------------------------------------------------- /Scripts/Runtime/UGUIExtension/PolygonImage.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 42aabdf480b94cd4aa4d73f39557b10a 3 | timeCreated: 1658905487 -------------------------------------------------------------------------------- /Scripts/Runtime/UGUIExtension/RectRaycast2D.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 76995126086140379f4fbb9646c29515 3 | timeCreated: 1658903344 -------------------------------------------------------------------------------- /Scripts/Editor/BatchEditor/BatchEditHelperBase.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0e3ef4f43332466b9c6d855b76cc72c3 3 | timeCreated: 1658972188 -------------------------------------------------------------------------------- /Scripts/Editor/GameObjectClone/GameObjectClone.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 61cb7bd12557422c8851d3cc7c206b90 3 | timeCreated: 1659085959 -------------------------------------------------------------------------------- /Scripts/Editor/ComponentCollection/IComponentCollector.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b3208ca3746644dd877a52e5dece4458 3 | timeCreated: 1657617149 -------------------------------------------------------------------------------- /Scripts/Editor/BatchEditor/Helper/BatchEditUIFontHelper.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9f4614ecf22c441bad0f32c9f0e71afb 3 | timeCreated: 1658972336 -------------------------------------------------------------------------------- /Scripts/Editor/ComponentCollection/ICollectionGenerator.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6d302d03edb54a1da100a9b039ac014f 3 | timeCreated: 1658386256 -------------------------------------------------------------------------------- /Scripts/Runtime/ComponentCollection/ComponentCollection.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d5100889a0684c6184c03ad3919f4ec2 3 | timeCreated: 1657615672 -------------------------------------------------------------------------------- /Scripts/Editor/ComponentCollection/ComponentCollectionInspector.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0776942984154f39886b4ffcbac4d89a 3 | timeCreated: 1657621279 -------------------------------------------------------------------------------- /Scripts/Editor/ComponentCollection/ComponentCollectionSettings.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7e3e46a0b33f424c898c3d2b5ece5c98 3 | timeCreated: 1658472426 -------------------------------------------------------------------------------- /Scripts/Editor/ComponentCollection/DefaultCollectionGenerator.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 49230155c3a24dd886b4a5ef8fc6745b 3 | timeCreated: 1658386539 -------------------------------------------------------------------------------- /Scripts/Editor/ComponentCollection/DefaultComponentCollector.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2486ff9dcde243daab1cc1785a5f7b68 3 | timeCreated: 1657617427 -------------------------------------------------------------------------------- /LICENSE.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8a671a6890abdb4439b8ff4e7067c25c 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Readme/GameObjectClone.md: -------------------------------------------------------------------------------- 1 | # GameObjectClone 2 | 3 | 克隆 GameObject 。 4 | 5 | 根据目标物体 Target 的结构层级,检查并克隆模板 Template 的组件数据(仅复制值类型和String)。 6 | 7 | 主要用于运行时更新预制体:设置预制体为 Target ,运行时预制体对象为 Template 。 8 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ec918e0e0c6b4c840acfd4a7f4f3852f 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Readme/BatchEditor.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 464a4e74530e31c4a8edca5de9d73a52 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Readme.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0c85a25b5544267479281676df1c3b53 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Readme/GUIStyleViewer.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 06c2995c9cfe62d459a025be78c6c33b 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Readme/GameObjectClone.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e1f9715e525a30d4ba3336fc2b8472e6 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Readme/UGUIExtension.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0cba0e1b5dbeb2a4cb609d55e0af5d25 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Readme/ComponentCollection.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1aecf0e8547573444b6c5d1a45d81da6 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Scripts/Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b87fc0b41d5efd5448366f8315bdf31a 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Scripts/Editor/Utility.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f0f961b29e32ea64d90ff737b6b87466 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Scripts/Editor/GUIStyleViewer.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 53f990d3abb9e3848973b8c614f78b09 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Scripts/Editor/UGUIExtension.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4616f7fa162b7c94485a185a503df88b 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Scripts/Runtime/UGUIExtension.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c74443ce123da93448e0abac22a06af3 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Scripts/Editor/ComponentCollection.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d5874594dedbfb748bc437d2f4ddf7de 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Scripts/Editor/GameObjectClone.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 28a13f6fcc6a40cd993a7398607c456e 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Scripts/Runtime/ComponentCollection.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a4c5662eefa0e4449ba25dc53e49849c 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Scripts/Editor/ComponentCollection/Config.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 55f629fa78351c94bb7b12a642664da4 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Scripts/Editor/ComponentCollection/Config/ComponentBehaviourCodeTemplate.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 87d428d27da5eb9468fb96d62a01ca10 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Scripts/Editor/ComponentCollection/Config/ComponentCollectionCodeTemplate.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c6f0d32e04aad824d8dc966bc6d7aa10 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Scripts/Editor/ComponentCollection/Config/ComponentCollectionExtensionCodeTemplete.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 968fd7c800fec3d4baf9160f97e61dc1 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UnityGameEntension 2 | 3 | Unity 开发常用扩展。 4 | 5 | [BatchEditor](Readme/BatchEditor.md) 批量编辑预制体并保存。 6 | 7 | [ComponentCollection](Readme/ComponentCollection.md) 自动收集组件,创建字段并自动生成代码。 8 | 9 | [UGUIExtension](Readme/UGUIExtension.md) 常用的 UGUI 优化与扩展。 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Scripts/Editor/ComponentCollection/Config/ComponentCollectionSettings.asset.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2edb9fe8cf29ba546afa95dbf252d50a 3 | NativeFormatImporter: 4 | externalObjects: {} 5 | mainObjectFileID: 11400000 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Scripts/Editor/UGUIExtension/UGUICreator.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2b8ab25162d820e4e9a958e051377363 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Scripts/Editor/GUIStyleViewer/GUIStyleViewer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2021df0c67629eb49bab311d0a1df359 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Scripts/Editor/UGUIExtension/UGUIMenuExtension.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c15d938ba07818e47a2ece30f2c7b520 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Scripts/Editor/UGUIExtension/UGUIRaycastTargetDebug.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e2cf62bdc8e97264d9a59dcf75bc901e 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Scripts/Editor/BatchEditor/Helper/BatchEditUISpriteHelper.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3ce9411bf9c565f43b81dbef4a70bb36 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Scripts/Editor/ComponentCollection/ComponentCollectionSettingsInspector.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b0c51343cbd1c914b8c247891e6c1c0d 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Scripts/Editor/ComponentCollection/Config/ComponentBehaviourCodeTemplate.txt: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using GameExtension; 5 | 6 | namespace __NAME_SPACE__ 7 | { 8 | public partial class __CLASS_NAME__ : MonoBehaviour 9 | { 10 | void Start() 11 | { 12 | InitComponents(gameObject); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Readme/BatchEditor.md: -------------------------------------------------------------------------------- 1 | # BatchEditor 2 | 3 | 预制体批量编辑器。 4 | 5 | ![BatchEditor](https://gitee.com/great1217/cdn/raw/master/images/BatchEditor.png) 6 | 7 | 通过菜单栏 "Game Extension/Batch Editor" 打开批量编辑器窗口,可打开多个。 8 | 9 | 基础窗口提供了编辑对象列表 Target Objects、批量辅助器接口 Batch Helper。 10 | 11 | 单选或多选对象,拖入 Target Objects 列表中。 12 | 13 | 选择不同的 Batch Helper 进行不同功能的编辑。 14 | 15 | 可继承 ```BatchEditHelperBase``` 自行扩展功能。 16 | 17 | 目前已实现批量辅助器:批量替换 UI 字体 ```BatchEditUIFontHeper``` 、批量替换 UI Sprite ```BatchEditUISpriteHelper``` 。 -------------------------------------------------------------------------------- /Scripts/Runtime/UGUIExtension/RectRaycast2D.cs: -------------------------------------------------------------------------------- 1 | // copy from Empty4Raycast. https://blog.uwa4d.com/archives/fillrate.html 2 | namespace UnityEngine.UI 3 | { 4 | [AddComponentMenu("UI/Rect Raycast 2D", 18)] 5 | public class RectRaycast2D : MaskableGraphic 6 | { 7 | protected RectRaycast2D() 8 | { 9 | useLegacyMeshGeneration = false; 10 | } 11 | 12 | protected override void OnPopulateMesh(VertexHelper vh) 13 | { 14 | vh.Clear(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Scripts/Editor/ComponentCollection/Config/ComponentCollectionExtensionCodeTemplete.txt: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------ 2 | // 此文件由 ComponentCollection 自动生成,请勿直接修改。 3 | // 用于在不方便使用非泛型的语言中(如XLua)调用 GetComponent 函数 4 | // 生成时间:__CREATE_TIME__ 5 | //------------------------------------------------------------ 6 | 7 | using UnityEngine; 8 | using UnityEngine.UI; 9 | using GameExtension; 10 | 11 | namespace __NAME_SPACE__ 12 | { 13 | public static class ComponentCollectionExtension 14 | { 15 | __GET_COMPONENT__ 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Scripts/Editor/BatchEditor/BatchEditHelperBase.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine; 3 | 4 | namespace GameExtension.Editor 5 | { 6 | public abstract class BatchEditHelperBase 7 | { 8 | protected readonly List m_TargetObjectObjects; 9 | 10 | public abstract string HelperName 11 | { 12 | get; 13 | } 14 | 15 | protected BatchEditHelperBase(List targetObjects) 16 | { 17 | m_TargetObjectObjects = targetObjects; 18 | } 19 | 20 | public abstract void CustomEditGUI(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Scripts/Editor/ComponentCollection/Config/ComponentCollectionCodeTemplate.txt: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------ 2 | // 此文件由 ComponentCollection 自动生成,请勿直接修改。 3 | // 生成时间:__CREATE_TIME__ 4 | //------------------------------------------------------------ 5 | 6 | using System; 7 | using System.Collections.Generic; 8 | using UnityEngine; 9 | using UnityEngine.UI; 10 | using GameExtension; 11 | 12 | namespace __NAME_SPACE__ 13 | { 14 | public partial class __CLASS_NAME__ 15 | { 16 | __FIELD__ 17 | 18 | /// 19 | /// 初始化组件。 20 | /// 21 | public void InitComponents(GameObject target) 22 | { 23 | var collection = target.GetComponent(); 24 | __GET_FIELD__ 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Scripts/Editor/ComponentCollection/IComponentCollector.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine; 3 | 4 | namespace GameExtension.Editor 5 | { 6 | /// 7 | /// 组件收集器接口。 8 | /// 9 | public interface IComponentCollector 10 | { 11 | /// 12 | /// 收集 Transform 的所有子组件。 13 | /// 14 | /// 目标 Transform。 15 | /// 字段前缀。 16 | /// 是否使用组件类型作为字段名。 17 | /// 组件类型映射字典。 18 | /// 字段组件字典。 19 | Dictionary CollectComponentFields(Transform target, string fieldNamePrefix, bool fieldNameByType, Dictionary componentMapDict); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Readme/UGUIExtension.md: -------------------------------------------------------------------------------- 1 | # UGUIExtension 2 | 3 | UGUI 扩展与常用优化。 4 | 5 | 此工具主要优化 UGUI 的 Raycast Target、Mask 和 三角面绘制,包含: 6 | 7 | > 取消不必要的 Raycast Target ,降低每帧检测射线的性能消耗。 8 | > 9 | > 取消 Text.RichText,减少三角面的绘制(默认取消,根据自身情况勾选)。 10 | > 11 | > 根据自身情况选择 Mask 或 RectMask2D:前者内容可以合批,适合一个界面中使用多个;后者产生 drawcall 少,但不能合批。 12 | 13 | - 提供了自定义的 UGUI 创建菜单 Custom UI,用于创建 UI 时选择可优化的选项:是否勾选 RaycastTarget,是否创建 Text 子物体,使用 Mask 或 RactMask2D 。 14 | 15 | ![Custom_UI](https://gitee.com/great1217/cdn/raw/master/images/Custom_UI.png) 16 | 17 | - 提供了 RaycastTarget 查看器,可在菜单栏勾选 “Game Extension / Debug Raycast Target” 即可在 Scene 窗口中查看 UI 中的 RaycastTarget 对象,支持在预制体预览场景查看。 18 | 19 | ![UI_DebugRaycast](https://gitee.com/great1217/cdn/raw/master/images/UI_DebugRaycast.png) 20 | 21 | - 增加了由 UWA 开源的两个 UI 优化组件:[UGUI 降低填充率技巧两则 ](https://blog.uwa4d.com/archives/fillrate.html)。 22 | 23 | > ```PolygnImage``` 作为 Image 组件的辅助工具,优化三角面的绘制,减少无用填充。 24 | > 25 | > ```RactRaycast2D``` 用于优化接收射线检测的透明图片,不绘制三角面,不增加 drawcall 、不打断合批。 26 | > 27 | > 可在创建菜单中使用 Custom UI 创建:透明射线遮挡矩形 RectRaycast、透明矩形按钮 RectButton。 28 | 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 GREAT1217 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Scripts/Editor/Utility/PrefabUtility.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | using UnityEngine; 3 | 4 | namespace GameExtension.Editor 5 | { 6 | public static class PrefabUtility 7 | { 8 | public static bool SavePrefab(GameObject prefab) 9 | { 10 | if (UnityEditor.PrefabUtility.GetPrefabAssetType(prefab) == PrefabAssetType.NotAPrefab) 11 | { 12 | return false; 13 | } 14 | 15 | string prefabPath = UnityEditor.PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(prefab); 16 | if (string.IsNullOrEmpty(prefabPath)) 17 | { 18 | return false; 19 | } 20 | 21 | bool saveResult; 22 | string prefabAssetPath = AssetDatabase.GetAssetPath(prefab); 23 | if (string.IsNullOrEmpty(prefabAssetPath)) 24 | { 25 | UnityEditor.PrefabUtility.SaveAsPrefabAssetAndConnect(prefab, prefabPath, InteractionMode.AutomatedAction, out saveResult); 26 | return saveResult; 27 | } 28 | 29 | UnityEditor.PrefabUtility.SavePrefabAsset(prefab, out saveResult); 30 | return saveResult; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Scripts/Editor/ComponentCollection/ICollectionGenerator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace GameExtension.Editor 4 | { 5 | /// 6 | /// 集合生成器接口。 7 | /// 8 | public interface ICollectionGenerator 9 | { 10 | /// 11 | /// 生成 Components 代码。 12 | /// 13 | /// 文件路径。 14 | /// 代码模板。 15 | /// 命名空间。 16 | /// 类名。 17 | /// 字段类型字典。 18 | void GenerateComponentsCode(string filePath, string codeTemplate, string nameSpace, string className, Dictionary fieldTypeDict); 19 | 20 | /// 21 | /// 生成 MonoBehaviour 代码。 22 | /// 23 | /// 文件路径。 24 | /// 代码模板。 25 | /// 命名空间。 26 | /// 类名。 27 | void GenerateBehaviourCode(string filePath, string codeTemplate, string nameSpace, string className); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Scripts/Editor/GUIStyleViewer/GUIStyleViewer.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | 4 | namespace GameExtension.Editor 5 | { 6 | public class GUIStyleViewer : EditorWindow 7 | { 8 | private Vector2 m_ScrollPosition = Vector2.zero; 9 | private string m_SearchText = string.Empty; 10 | 11 | [MenuItem("Game Extension/GUIStyle Viewer")] 12 | public static void ShowWindow() 13 | { 14 | var window = GetWindow("GUIStyle Viewer", true); 15 | window.minSize = new Vector2(600, 800); 16 | window.Show(); 17 | } 18 | 19 | void OnGUI() 20 | { 21 | m_SearchText = EditorGUILayout.TextField("", m_SearchText, "SearchTextField"); 22 | EditorGUILayout.HelpBox("Click item right or choose item left to copy to clipboard.", MessageType.Info); 23 | 24 | m_ScrollPosition = GUILayout.BeginScrollView(m_ScrollPosition, "HelpBox"); 25 | { 26 | foreach (GUIStyle style in GUI.skin) 27 | { 28 | if (style.name.ToLower().Contains(m_SearchText.ToLower())) 29 | { 30 | GUILayout.BeginHorizontal("Box"); 31 | { 32 | EditorGUILayout.SelectableLabel("\"" + style.name + "\""); 33 | GUILayout.FlexibleSpace(); 34 | if (GUILayout.Button(style.name, style)) 35 | { 36 | EditorGUIUtility.systemCopyBuffer = "\"" + style.name + "\""; 37 | } 38 | GUILayout.Space(10); 39 | } 40 | GUILayout.EndHorizontal(); 41 | } 42 | } 43 | } 44 | GUILayout.EndScrollView(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Readme/ComponentCollection.md: -------------------------------------------------------------------------------- 1 | # ComponentCollection 2 | 3 | 组件自动绑定工具。本项目代码灵感来源于 [CatImmortal](https://github.com/CatImmortal) 的 [ComponentAutoBind](https://github.com/CatImmortal/ComponentAutoBindTool) 。 4 | 5 | 重构了 Editor 代码逻辑,增加了一些编辑器辅助功能。 6 | 7 | 实现了 Editor 使用字符串、Runtime 使用索引获取组件引用,以消除运行时字符串占用的内存分配。 8 | 9 | 根据物体命名后缀,进行组件映射识别,支持单个物体的多组件识别。 10 | 11 | 提供了默认的组件映射规则,支持自定义组件映射规则。 12 | 13 | 支持自定义组件字段生成规则,包含字段前缀和字段命名类型。 14 | 15 | | ![ComponentCollection](https://gitee.com/great1217/cdn/raw/master/images/ComponentCollection.png) | ![ComponentCollectionCode](https://gitee.com/great1217/cdn/raw/master/images/ComponentCollectionCode.png) | 16 | | ------------------------------------------------------------ | ------------------------------------------------------------ | 17 | 18 | ## 基本用法 19 | 20 | 本项目包含设置文件 ComponentCollectionSettings.asset,可放置于 Asset 目录下任意位置。 21 | 22 | 设置文件仅需要一个,丢失后可在 Project 窗口中通过 "Create/Game/Component Collection Settings" 创建。 23 | 24 | 1 根据 ComponentCollectionSettings.asset 编辑的组件映射规则,编辑物体名; 25 | 26 | 2 为物体挂载 ComponentCollection 脚本; 27 | 28 | 3 设置字段生成规则 Field Name Rule,包含字段前缀,字段命名类型:组件类型名称 TypeName、组件映射的键值 TypeKey; 29 | 30 | 4.1 点击 Collect Components To Update,识别并绑定此物体(及子物体)中所有符合映射规则的组件引用。 31 | 32 | 4.2 点击 Collect Components To Add,在不修改已保存的组件列表的顺序的情况下,识别并绑定增加的组件。(首次绑定不需要) 33 | 34 | 4.3 点击 Remove Null Component 移除组件列表中的空组件。(首次绑定不需要) 35 | 36 | 5 设置自动生成的绑定代码的命名空间 NameSpace、类名 Class Name 与保存路径 Code Save Path。 37 | 38 | 6.1 点击 Generate Components Code,自动生成代码文件 ClassName.Components.cs ,包含所有绑定的组件字段。 39 | 40 | 6.2 点击 Generate Behaviour Code,自动生成代码文件 ClassName.cs,继承自 MonoBehaviour,可直接访问绑定的组件字段。 41 | 42 | 7 等待生成代码编译后,点击 Add Behaviour Code,自动添加 ClassName.cs 组件。 43 | 44 | 可以修改 ComponentCollectionSettings.asset 中设置的代码模板 Code Template。 45 | 46 | ## 其他用法 47 | 48 | 在不使用 Generate 自动生成代码时,可以自定义调用 ```ComponentCollection 的 GetComponent(int)``` 根据索引获取组件。 49 | 50 | 自定义调用后,如果修改了子物体层级,或增删了子物体,可以使用 Collect To Add:在不修改旧的组件列表的顺序的情况下,增加组件。 51 | 52 | 当然,Remove Null 也不能在自定义调用后使用。移除空组件会使索引错乱。 53 | 54 | ## 感谢 55 | 56 | ❤ 再次感谢 [CatImmortal](https://github.com/CatImmortal) 的开源。 57 | -------------------------------------------------------------------------------- /Scripts/Runtime/ComponentCollection/ComponentCollection.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine; 3 | 4 | namespace GameExtension 5 | { 6 | [AddComponentMenu("Game/Component Collection")] 7 | [DisallowMultipleComponent] 8 | public sealed class ComponentCollection : MonoBehaviour 9 | { 10 | #if UNITY_EDITOR 11 | /* 12 | * 编辑器使用的序列化数据。不会影响运行时逻辑。 13 | * 这些字段只能在编辑器宏中调用,否则打包会提示错误。一般也不会在外部调用。 14 | * 此脚本一般不会修改,能在外部访问的只有 GetComponent(int) 函数。 15 | */ 16 | 17 | // 是否已设置过默认值 18 | #pragma warning disable 0414 19 | [SerializeField] private bool m_Setup; 20 | 21 | // Collect settings 22 | [SerializeField] private string m_CollectorTypeName; 23 | [SerializeField] private string m_FieldNamePrefix; 24 | [SerializeField] private bool m_FieldNameByType; 25 | [SerializeField] private List m_FieldNames; 26 | [SerializeField] private List m_FieldComponents; 27 | 28 | // Generate settings 29 | [SerializeField] private string m_GeneratorTypeName; 30 | [SerializeField] private string m_NameSpace; 31 | [SerializeField] private string m_ClassName; 32 | [SerializeField] private string m_CodeSavePath; 33 | 34 | private void Reset() 35 | { 36 | m_Setup = false; 37 | } 38 | #endif 39 | 40 | [SerializeField] private List m_Components; 41 | 42 | public T GetComponent(int index) where T : Component 43 | { 44 | if (index < 0 || index >= m_Components.Count) 45 | { 46 | Debug.LogError("Get component failed with invalid index."); 47 | return null; 48 | } 49 | 50 | T component = m_Components[index] as T; 51 | if (component == null) 52 | { 53 | Debug.LogErrorFormat("Get component failed with invalid type, index = {0}.", index); 54 | return null; 55 | } 56 | 57 | return component; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Scripts/Editor/ComponentCollection/Config/ComponentCollectionSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!114 &11400000 4 | MonoBehaviour: 5 | m_ObjectHideFlags: 0 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | m_GameObject: {fileID: 0} 10 | m_Enabled: 1 11 | m_EditorHideFlags: 0 12 | m_Script: {fileID: 11500000, guid: 7e3e46a0b33f424c898c3d2b5ece5c98, type: 3} 13 | m_Name: ComponentCollectionSettings 14 | m_EditorClassIdentifier: 15 | m_CollectorTypeName: GameExtension.Editor.DefaultComponentCollector 16 | m_ComponentMaps: 17 | - m_TypeKey: Tran 18 | m_TypeName: Transform 19 | - m_TypeKey: Rect 20 | m_TypeName: RectTransform 21 | - m_TypeKey: Anim 22 | m_TypeName: Animation 23 | - m_TypeKey: Animator 24 | m_TypeName: Animator 25 | - m_TypeKey: Text 26 | m_TypeName: Text 27 | - m_TypeKey: Image 28 | m_TypeName: Image 29 | - m_TypeKey: RawImage 30 | m_TypeName: RawImage 31 | - m_TypeKey: Button 32 | m_TypeName: Button 33 | - m_TypeKey: Toggle 34 | m_TypeName: Toggle 35 | - m_TypeKey: TGroup 36 | m_TypeName: ToggleGroup 37 | - m_TypeKey: Slider 38 | m_TypeName: Slider 39 | - m_TypeKey: Scrollbar 40 | m_TypeName: Scrollbar 41 | - m_TypeKey: Dropdown 42 | m_TypeName: Dropdown 43 | - m_TypeKey: InputField 44 | m_TypeName: InputField 45 | - m_TypeKey: Canvas 46 | m_TypeName: Canvas 47 | - m_TypeKey: ScrollView 48 | m_TypeName: ScrollRect 49 | - m_TypeKey: CGroup 50 | m_TypeName: CanvasGroup 51 | - m_TypeKey: GLGroup 52 | m_TypeName: GridLayoutGroup 53 | - m_TypeKey: VLGroup 54 | m_TypeName: VerticalLayoutGroup 55 | - m_TypeKey: HLGroup 56 | m_TypeName: HorizontalLayoutGroup 57 | - m_TypeKey: Mask 58 | m_TypeName: Mask 59 | - m_TypeKey: RectMask 60 | m_TypeName: RectMask2D 61 | m_DefaultFieldNamePrefix: m_ 62 | m_DefaultFieldNameByType: 0 63 | m_GeneratorTypeName: GameExtension.Editor.DefaultCollectionGenerator 64 | m_DefaultNameSpace: Game 65 | m_DefaultCodeSavePath: Assets 66 | m_ComponentsCodeTemplate: {fileID: 4900000, guid: c6f0d32e04aad824d8dc966bc6d7aa10, 67 | type: 3} 68 | m_BehaviourCodeTemplate: {fileID: 4900000, guid: 87d428d27da5eb9468fb96d62a01ca10, 69 | type: 3} 70 | m_CollectionExtensionCodeTemplate: {fileID: 4900000, guid: 968fd7c800fec3d4baf9160f97e61dc1, 71 | type: 3} 72 | -------------------------------------------------------------------------------- /Scripts/Editor/UGUIExtension/UGUIRaycastTargetDebug.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | using UnityEditor.Experimental.SceneManagement; 3 | using UnityEditor.SceneManagement; 4 | using UnityEngine; 5 | using UnityEngine.UI; 6 | 7 | namespace GameExtension.Editor 8 | { 9 | public class UGUIRaycastTargetDebug : UnityEditor.Editor 10 | { 11 | private const string MenuItemName = "Game Extension/Debug Raycast Target"; 12 | private const string PrefsKey = "DebugRaycastTarget"; 13 | 14 | private static bool s_DebugRaycastTarget; 15 | private static readonly Color s_DebugFaceColor = new Color(0f, 1f, 0f, 0.2f); 16 | private static readonly Color s_DebugOutlineColor = new Color(0f, 1f, 0f, 1f); 17 | 18 | [InitializeOnLoadMethod] 19 | private static void Init() 20 | { 21 | #if UNITY_2019_1_OR_NEWER 22 | SceneView.duringSceneGui += DrawRaycastTargetGUI; 23 | #else 24 | SceneView.onSceneGUIDelegate += DrawRaycastTargetGUI; 25 | #endif 26 | s_DebugRaycastTarget = EditorPrefs.GetBool(PrefsKey, false); 27 | Menu.SetChecked(MenuItemName, s_DebugRaycastTarget); 28 | } 29 | 30 | [MenuItem(MenuItemName, false, 0)] 31 | public static void SwitchDebugRaycastTarget() 32 | { 33 | s_DebugRaycastTarget = !s_DebugRaycastTarget; 34 | EditorPrefs.SetBool(PrefsKey, s_DebugRaycastTarget); 35 | Menu.SetChecked(MenuItemName, s_DebugRaycastTarget); 36 | } 37 | 38 | private static void DrawRaycastTargetGUI(SceneView sceneView) 39 | { 40 | if (!s_DebugRaycastTarget) 41 | { 42 | return; 43 | } 44 | 45 | PrefabStage prefabStage = PrefabStageUtility.GetCurrentPrefabStage(); 46 | MaskableGraphic[] maskableGraphic = prefabStage == null ? FindObjectsOfType() : prefabStage.prefabContentsRoot.GetComponentsInChildren(); 47 | 48 | Vector3[] fourCornerArray = new Vector3[4]; 49 | foreach (var graphic in maskableGraphic) 50 | { 51 | if (graphic.raycastTarget) 52 | { 53 | graphic.rectTransform.GetWorldCorners(fourCornerArray); 54 | Handles.DrawSolidRectangleWithOutline(fourCornerArray, s_DebugFaceColor, s_DebugOutlineColor); 55 | } 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Scripts/Editor/Utility/TypeUtility.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | 5 | namespace GameExtension.Editor 6 | { 7 | internal static class TypeUtility 8 | { 9 | private static readonly string[] EditorAssemblyNames = {"Assembly-CSharp-Editor", "GameExtension.Editor", "Game.Editor"}; 10 | private static readonly string[] RuntimeAssemblyNames = {"Assembly-CSharp", "GameExtension", "Game"}; 11 | 12 | public static Type GetEditorType(string typeName) 13 | { 14 | return GetType(typeName, EditorAssemblyNames); 15 | } 16 | 17 | public static Type GetRuntimeType(string typeName) 18 | { 19 | return GetType(typeName, RuntimeAssemblyNames); 20 | } 21 | 22 | public static string[] GetEditorTypeNames(Type typeBase) 23 | { 24 | return GetTypeNames(typeBase, EditorAssemblyNames); 25 | } 26 | 27 | public static string[] GetRuntimeTypeNames(Type typeBase) 28 | { 29 | return GetTypeNames(typeBase, RuntimeAssemblyNames); 30 | } 31 | 32 | private static Type GetType(string typeName, string[] assemblyNames) 33 | { 34 | if (string.IsNullOrEmpty(typeName)) 35 | { 36 | return null; 37 | } 38 | 39 | Type type1 = Type.GetType(typeName); 40 | if (type1 != null) 41 | { 42 | return type1; 43 | } 44 | 45 | foreach (string assemblyName in assemblyNames) 46 | { 47 | Type type2 = Type.GetType(string.Format("{0}, {1}", typeName, assemblyName)); 48 | if (type2 != null) 49 | { 50 | return type2; 51 | } 52 | } 53 | 54 | return null; 55 | } 56 | 57 | private static string[] GetTypeNames(Type typeBase, string[] assemblyNames) 58 | { 59 | List typeNames = new List(); 60 | foreach (string assemblyName in assemblyNames) 61 | { 62 | Assembly assembly; 63 | try 64 | { 65 | assembly = Assembly.Load(assemblyName); 66 | } 67 | catch 68 | { 69 | continue; 70 | } 71 | 72 | if (assembly == null) 73 | { 74 | continue; 75 | } 76 | 77 | Type[] types = assembly.GetTypes(); 78 | foreach (Type type in types) 79 | { 80 | if (type.IsClass && !type.IsAbstract && typeBase.IsAssignableFrom(type)) 81 | { 82 | typeNames.Add(type.FullName); 83 | } 84 | } 85 | } 86 | 87 | typeNames.Sort(); 88 | return typeNames.ToArray(); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /Scripts/Runtime/UGUIExtension/PolygonImage.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | // copy from Empty4Raycast. https://blog.uwa4d.com/archives/fillrate.html 4 | namespace UnityEngine.UI 5 | { 6 | [AddComponentMenu("UI/Effects/Polygon Image", 17)] 7 | [DisallowMultipleComponent] 8 | [RequireComponent(typeof(Image))] 9 | public class PolygonImage : BaseMeshEffect 10 | { 11 | protected PolygonImage() 12 | { 13 | } 14 | 15 | // GC Friendly 16 | private static Vector3[] fourCorners = new Vector3[4]; 17 | private static UIVertex vertice = new UIVertex(); 18 | private RectTransform rectTransform = null; 19 | private Image image = null; 20 | 21 | public override void ModifyMesh(VertexHelper vh) 22 | { 23 | if (!isActiveAndEnabled) return; 24 | 25 | if (rectTransform == null) 26 | { 27 | rectTransform = GetComponent(); 28 | } 29 | if (image == null) 30 | { 31 | image = GetComponent(); 32 | } 33 | if (image.type != Image.Type.Simple) 34 | { 35 | return; 36 | } 37 | Sprite sprite = image.overrideSprite; 38 | if (sprite == null || sprite.triangles.Length == 6) 39 | { 40 | // only 2 triangles 41 | return; 42 | } 43 | 44 | // Kanglai: at first I copy codes from Image.GetDrawingDimensions 45 | // to calculate Image's dimensions. But now for easy to read, I just take usage of corners. 46 | if (vh.currentVertCount != 4) 47 | { 48 | return; 49 | } 50 | 51 | rectTransform.GetLocalCorners(fourCorners); 52 | 53 | // Kanglai: recalculate vertices from Sprite! 54 | int len = sprite.vertices.Length; 55 | var vertices = new List(len); 56 | Vector2 Center = sprite.bounds.center; 57 | Vector2 invExtend = new Vector2(1 / sprite.bounds.size.x, 1 / sprite.bounds.size.y); 58 | for (int i = 0; i < len; i++) 59 | { 60 | // normalize 61 | float x = (sprite.vertices[i].x - Center.x) * invExtend.x + 0.5f; 62 | float y = (sprite.vertices[i].y - Center.y) * invExtend.y + 0.5f; 63 | // lerp to position 64 | vertice.position = new Vector2(Mathf.Lerp(fourCorners[0].x, fourCorners[2].x, x), Mathf.Lerp(fourCorners[0].y, fourCorners[2].y, y)); 65 | vertice.color = image.color; 66 | vertice.uv0 = sprite.uv[i]; 67 | vertices.Add(vertice); 68 | } 69 | 70 | len = sprite.triangles.Length; 71 | var triangles = new List(len); 72 | for (int i = 0; i < len; i++) 73 | { 74 | triangles.Add(sprite.triangles[i]); 75 | } 76 | 77 | vh.Clear(); 78 | vh.AddUIVertexStream(vertices, triangles); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Scripts/Editor/BatchEditor/BatchEditor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using UnityEditor; 4 | using UnityEngine; 5 | 6 | namespace GameExtension.Editor 7 | { 8 | public class BatchEditor : EditorWindow 9 | { 10 | private const string DefaultTitleName = "Batch Editor"; 11 | private const string NoneOptionName = ""; 12 | 13 | [SerializeField] 14 | private List m_TargetObjects = new List(); 15 | private SerializedObject m_SerializedThis; 16 | private SerializedProperty m_SerializedObjects; 17 | private string[] m_HelperTypeNames; 18 | private int m_HelperTypeNameIndex; 19 | private BatchEditHelperBase m_Helper; 20 | 21 | [MenuItem("Game Extension/Batch Editor", false)] 22 | private static void ShowWindow() 23 | { 24 | var window = CreateWindow(); 25 | window.titleContent = new GUIContent(DefaultTitleName); 26 | window.minSize = new Vector2(400, 200); 27 | window.Show(); 28 | } 29 | 30 | private void OnEnable() 31 | { 32 | m_SerializedThis = new SerializedObject(this); 33 | m_SerializedObjects = m_SerializedThis.FindProperty("m_TargetObjects"); 34 | 35 | List helperTypeNames = new List {NoneOptionName}; 36 | helperTypeNames.AddRange(TypeUtility.GetEditorTypeNames(typeof(BatchEditHelperBase))); 37 | m_HelperTypeNames = helperTypeNames.ToArray(); 38 | helperTypeNames.Clear(); 39 | } 40 | 41 | private void OnGUI() 42 | { 43 | GUISelectHelper(); 44 | GUISelectTarget(); 45 | if (m_Helper != null) 46 | { 47 | m_Helper.CustomEditGUI(); 48 | } 49 | } 50 | 51 | private void GUISelectTarget() 52 | { 53 | m_SerializedThis.Update(); 54 | EditorGUI.BeginChangeCheck(); 55 | EditorGUILayout.PropertyField(m_SerializedObjects, true); 56 | if (EditorGUI.EndChangeCheck()) 57 | { 58 | m_SerializedThis.ApplyModifiedProperties(); 59 | } 60 | } 61 | 62 | private void GUISelectHelper() 63 | { 64 | int helperTypeNameIndex = EditorGUILayout.Popup("Batch Helper", m_HelperTypeNameIndex, m_HelperTypeNames); 65 | if (helperTypeNameIndex != m_HelperTypeNameIndex) 66 | { 67 | m_HelperTypeNameIndex = helperTypeNameIndex; 68 | Type helperType = TypeUtility.GetEditorType(m_HelperTypeNames[m_HelperTypeNameIndex]); 69 | if (helperType != null) 70 | { 71 | m_Helper = (BatchEditHelperBase) Activator.CreateInstance(helperType, m_TargetObjects); 72 | titleContent.text = m_Helper.HelperName; 73 | } 74 | else 75 | { 76 | m_Helper = null; 77 | titleContent.text = DefaultTitleName; 78 | } 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Scripts/Editor/BatchEditor/Helper/BatchEditUISpriteHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEditor; 3 | using UnityEngine; 4 | using UnityEngine.UI; 5 | 6 | namespace GameExtension.Editor 7 | { 8 | public class BatchEditUISpriteHelper : BatchEditHelperBase 9 | { 10 | private Sprite m_OldSprite; 11 | private Sprite m_NewSprite; 12 | private bool m_ModifyNullSprite; 13 | private readonly GUILayoutOption m_SpriteFieldHeight; 14 | 15 | public override string HelperName 16 | { 17 | get 18 | { 19 | return "Batch Sprite Editor"; 20 | } 21 | } 22 | 23 | public BatchEditUISpriteHelper(List targetObjects) : base(targetObjects) 24 | { 25 | m_SpriteFieldHeight = GUILayout.Height(18); 26 | } 27 | 28 | public override void CustomEditGUI() 29 | { 30 | m_OldSprite = (Sprite) EditorGUILayout.ObjectField("Old Sprite", m_OldSprite, typeof(Sprite), false, m_SpriteFieldHeight); 31 | m_NewSprite = (Sprite) EditorGUILayout.ObjectField("New Sprite", m_NewSprite, typeof(Sprite), false, m_SpriteFieldHeight); 32 | m_ModifyNullSprite = EditorGUILayout.Toggle("Modify Null Sprite", m_ModifyNullSprite); 33 | 34 | if (GUILayout.Button("Batch Modify")) 35 | { 36 | ModifySprites(); 37 | } 38 | } 39 | 40 | private void ModifySprites() 41 | { 42 | if (m_NewSprite == m_OldSprite) 43 | { 44 | return; 45 | } 46 | 47 | if (m_NewSprite == null) 48 | { 49 | Debug.LogError("New sprite is null."); 50 | return; 51 | } 52 | 53 | if (m_OldSprite == null) 54 | { 55 | Debug.LogError("Old sprite is null."); 56 | return; 57 | } 58 | 59 | for (int i = 0; i < m_TargetObjectObjects.Count; i++) 60 | { 61 | var item = m_TargetObjectObjects[i]; 62 | if (item != null) 63 | { 64 | EditorUtility.DisplayProgressBar("Modify Progress", item.name, (i + 1f) / m_TargetObjectObjects.Count); 65 | ModifySprite(item); 66 | if (PrefabUtility.SavePrefab(item)) 67 | { 68 | Debug.LogFormat("Prefab save succeed: {0}.", item.name); 69 | } 70 | else 71 | { 72 | Debug.LogErrorFormat("Prefab save failed: {0}.", item.name); 73 | } 74 | } 75 | } 76 | 77 | EditorUtility.ClearProgressBar(); 78 | AssetDatabase.Refresh(); 79 | } 80 | 81 | private void ModifySprite(GameObject target) 82 | { 83 | var texts = target.GetComponentsInChildren(true); 84 | foreach (var item in texts) 85 | { 86 | if (item.sprite == m_OldSprite || m_ModifyNullSprite && item.sprite == null) 87 | { 88 | item.sprite = m_NewSprite; 89 | } 90 | } 91 | Debug.LogFormat("Prefab modify succeed: {0}.", target.name); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /Scripts/Editor/BatchEditor/Helper/BatchEditUIFontHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEditor; 3 | using UnityEngine; 4 | using UnityEngine.UI; 5 | 6 | namespace GameExtension.Editor 7 | { 8 | public class BatchEditUIFontHelper : BatchEditHelperBase 9 | { 10 | private Font m_NewFont; 11 | private bool m_ModifyAllFont; 12 | private Font m_OldFont; 13 | private bool m_ModifyNullFont; 14 | 15 | public override string HelperName 16 | { 17 | get 18 | { 19 | return "Batch Font Editor"; 20 | } 21 | } 22 | 23 | public BatchEditUIFontHelper(List targetObjects) : base(targetObjects) 24 | { 25 | } 26 | 27 | public override void CustomEditGUI() 28 | { 29 | m_NewFont = (Font) EditorGUILayout.ObjectField("New Font", m_NewFont, typeof(Font), false); 30 | m_ModifyAllFont = EditorGUILayout.Toggle("Modify All Font", m_ModifyAllFont); 31 | EditorGUI.BeginDisabledGroup(m_ModifyAllFont); 32 | { 33 | // EditorGUI.indentLevel++; 34 | m_OldFont = (Font) EditorGUILayout.ObjectField("Old Font", m_OldFont, typeof(Font), false); 35 | m_ModifyNullFont = EditorGUILayout.Toggle("Modify Null Font", m_ModifyNullFont); 36 | // EditorGUI.indentLevel--; 37 | } 38 | EditorGUI.EndDisabledGroup(); 39 | 40 | if (GUILayout.Button("Batch Modify")) 41 | { 42 | ModifyFonts(); 43 | } 44 | } 45 | 46 | private void ModifyFonts() 47 | { 48 | if (m_NewFont == m_OldFont) 49 | { 50 | return; 51 | } 52 | 53 | if (m_NewFont == null) 54 | { 55 | Debug.LogError("New font is null."); 56 | return; 57 | } 58 | 59 | if (m_OldFont == null && !m_ModifyAllFont) 60 | { 61 | Debug.LogError("Old font is null. Please set or choose the modify all font option."); 62 | return; 63 | } 64 | 65 | for (int i = 0; i < m_TargetObjectObjects.Count; i++) 66 | { 67 | var item = m_TargetObjectObjects[i]; 68 | if (item != null) 69 | { 70 | EditorUtility.DisplayProgressBar("Modify Progress", item.name, (i + 1f) / m_TargetObjectObjects.Count); 71 | ModifyFont(item); 72 | if (PrefabUtility.SavePrefab(item)) 73 | { 74 | Debug.LogFormat("Prefab save succeed: {0}.", item.name); 75 | } 76 | else 77 | { 78 | Debug.LogErrorFormat("Prefab save failed: {0}.", item.name); 79 | } 80 | } 81 | } 82 | 83 | EditorUtility.ClearProgressBar(); 84 | AssetDatabase.Refresh(); 85 | } 86 | 87 | private void ModifyFont(GameObject target) 88 | { 89 | var texts = target.GetComponentsInChildren(true); 90 | foreach (var item in texts) 91 | { 92 | if (m_ModifyAllFont || item.font == m_OldFont || m_ModifyNullFont && item.font == null) 93 | { 94 | item.font = m_NewFont; 95 | } 96 | } 97 | Debug.LogFormat("Prefab modify succeed: {0}.", target.name); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Scripts/Editor/ComponentCollection/DefaultCollectionGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Text; 5 | using UnityEngine; 6 | 7 | namespace GameExtension.Editor 8 | { 9 | public class DefaultCollectionGenerator : ICollectionGenerator 10 | { 11 | /// 12 | /// 生成 Components 代码。 13 | /// 14 | /// 文件路径。 15 | /// 代码模板。 16 | /// 命名空间。 17 | /// 类名。 18 | /// 字段类型字典。 19 | public void GenerateComponentsCode(string filePath, string codeTemplate, string nameSpace, string className, Dictionary fieldTypeDict) 20 | { 21 | if (string.IsNullOrEmpty(codeTemplate)) 22 | { 23 | Debug.LogError("Component collection code template file is invalid."); 24 | return; 25 | } 26 | 27 | StringBuilder stringBuilder = new StringBuilder(codeTemplate); 28 | stringBuilder.Replace("__CREATE_TIME__", DateTime.UtcNow.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss.ff")); 29 | stringBuilder.Replace("__NAME_SPACE__", nameSpace); 30 | stringBuilder.Replace("__CLASS_NAME__", className); 31 | string field, getField; 32 | GenerateComponentField(fieldTypeDict, out field, out getField); 33 | stringBuilder.Replace("__FIELD__", field); 34 | stringBuilder.Replace("__GET_FIELD__", getField); 35 | 36 | SaveFile(filePath, stringBuilder.ToString()); 37 | } 38 | 39 | /// 40 | /// 生成 MonoBehaviour 代码。 41 | /// 42 | /// 文件路径。 43 | /// 代码模板。 44 | /// 命名空间。 45 | /// 类名。 46 | public void GenerateBehaviourCode(string filePath, string codeTemplate, string nameSpace, string className) 47 | { 48 | if (string.IsNullOrEmpty(codeTemplate)) 49 | { 50 | Debug.LogError("Component behaviour code template file is invalid."); 51 | return; 52 | } 53 | 54 | StringBuilder stringBuilder = new StringBuilder(codeTemplate); 55 | 56 | stringBuilder.Replace("__NAME_SPACE__", nameSpace); 57 | stringBuilder.Replace("__CLASS_NAME__", className); 58 | 59 | SaveFile(filePath, stringBuilder.ToString()); 60 | } 61 | 62 | /// 63 | /// 生成组件字段。 64 | /// 65 | /// 字段类型字典。 66 | /// 字段代码区域 的字符。 67 | /// 获取组件代码区域 的字符。 68 | private void GenerateComponentField(Dictionary fieldTypeDict, out string field, out string getField) 69 | { 70 | StringBuilder fieldBuilder = new StringBuilder(); 71 | StringBuilder getFieldBuilder = new StringBuilder(); 72 | int index = 0; 73 | foreach (var pair in fieldTypeDict) 74 | { 75 | fieldBuilder.AppendLine(string.Format(" private {0} {1};", pair.Value, pair.Key)); 76 | getFieldBuilder.AppendLine(string.Format(" {0} = collection.GetComponent<{1}>({2});", pair.Key, pair.Value, index++)); 77 | } 78 | 79 | // 去除最后的换行符 80 | field = fieldBuilder.ToString().TrimEnd(); 81 | getField = getFieldBuilder.ToString().TrimEnd(); 82 | } 83 | 84 | /// 85 | /// 保存文件。 86 | /// 87 | /// 文件地址。 88 | /// 内容字符串。 89 | private void SaveFile(string filePath, string content) 90 | { 91 | using (FileStream stream = new FileStream(filePath, FileMode.Create, FileAccess.Write)) 92 | { 93 | using (StreamWriter writer = new StreamWriter(stream, Encoding.UTF8)) 94 | { 95 | writer.Write(content); 96 | } 97 | } 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Scripts/Editor/ComponentCollection/DefaultComponentCollector.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine; 3 | 4 | namespace GameExtension.Editor 5 | { 6 | public class DefaultComponentCollector : IComponentCollector 7 | { 8 | private const char ComponentSeparator = '_'; 9 | 10 | /// 11 | /// 收集 Transform 的所有子组件。 12 | /// 13 | /// 目标 Transform。 14 | /// 字段前缀。 15 | /// 是否使用组件类型作为字段名。 16 | /// 组件类型映射字典。 17 | /// 字段组件字典。 18 | public Dictionary CollectComponentFields(Transform target, string fieldNamePrefix, bool fieldNameByType, Dictionary componentMapDict) 19 | { 20 | Dictionary fieldComponentDict = new Dictionary(); 21 | var children = target.GetComponentsInChildren(true); 22 | foreach (var child in children) 23 | { 24 | CollectComponentField(child, fieldComponentDict, fieldNamePrefix, fieldNameByType, componentMapDict); 25 | } 26 | return fieldComponentDict; 27 | } 28 | 29 | /// 30 | /// 收集 Transform 的组件。 31 | /// 32 | /// 目标 Transform。 33 | /// 收集的结果字典。 34 | /// 字段前缀。 35 | /// 是否使用组件类型作为字段名。 36 | /// 组件类型映射字典。 37 | private void CollectComponentField(Transform target, Dictionary fieldComponentDict, string fieldNamePrefix, bool fieldNameByType, Dictionary componentMapDict) 38 | { 39 | string[] splits = target.name.Split(ComponentSeparator); 40 | if (splits.Length <= 1) 41 | { 42 | return; 43 | } 44 | 45 | int nameIndex = splits.Length - 1; 46 | for (int i = 0; i < nameIndex; i++) 47 | { 48 | string typeKey = splits[i]; 49 | string typeName; 50 | if (!componentMapDict.TryGetValue(typeKey, out typeName)) 51 | { 52 | Debug.LogErrorFormat("Component type key '{0}' has no mapping component type.", typeKey); 53 | continue; 54 | } 55 | 56 | Component component = target.GetComponent(typeName); 57 | if (component == null) 58 | { 59 | Debug.LogErrorFormat("Transform '{0}' has no component '{1}'.", target.name, typeName); 60 | continue; 61 | } 62 | 63 | string name = splits[nameIndex]; 64 | name = string.IsNullOrEmpty(fieldNamePrefix) ? LowerFirst(name) : UpperFirst(name); 65 | string fieldName = string.Format("{0}{1}{2}", fieldNamePrefix, name, fieldNameByType ? typeName : typeKey); 66 | if (fieldComponentDict.ContainsKey(fieldName)) 67 | { 68 | Debug.LogErrorFormat("Already exits the same field '{0}'. Please modify the name of transform '{1}'.", fieldName, target.name); 69 | continue; 70 | } 71 | 72 | fieldComponentDict.Add(fieldName, component); 73 | } 74 | } 75 | 76 | /// 77 | /// 首字符大写。 78 | /// 79 | /// 80 | /// 81 | private string UpperFirst(string str) 82 | { 83 | if (string.IsNullOrEmpty(str)) 84 | { 85 | return null; 86 | } 87 | if (str.Length <= 1) 88 | { 89 | return str.ToUpper(); 90 | } 91 | return str.Substring(0, 1).ToUpper() + str.Substring(1); 92 | } 93 | 94 | /// 95 | /// 首字符小写。 96 | /// 97 | /// 98 | /// 99 | private string LowerFirst(string str) 100 | { 101 | if (string.IsNullOrEmpty(str)) 102 | { 103 | return null; 104 | } 105 | if (str.Length <= 1) 106 | { 107 | return str.ToLower(); 108 | } 109 | return str.Substring(0, 1).ToLower() + str.Substring(1); 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Scripts/Editor/ComponentCollection/ComponentCollectionSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text.RegularExpressions; 4 | using UnityEngine; 5 | 6 | namespace GameExtension.Editor 7 | { 8 | [CreateAssetMenu(fileName = "ComponentCollectionSettings", menuName = "Game/Component Collection Settings", order = 1)] 9 | public class ComponentCollectionSettings : ScriptableObject 10 | { 11 | /// 12 | /// 组件类型映射。 13 | /// 14 | [Serializable] 15 | public class ComponentTypeMap 16 | { 17 | public string m_TypeKey; 18 | public string m_TypeName; 19 | 20 | public ComponentTypeMap(string typeKey, string typeName) 21 | { 22 | m_TypeKey = typeKey; 23 | m_TypeName = typeName; 24 | } 25 | } 26 | 27 | [Space(4)] 28 | [Header("Collect")] 29 | [Tooltip("ComponentCollector。")] 30 | public string m_CollectorTypeName = "GameExtension.Editor.DefaultComponentCollector"; 31 | [Tooltip("组件类型映射列表。")] 32 | public List m_ComponentMaps = new List 33 | { 34 | // Transform 35 | new ComponentTypeMap("Tran", "Transform"), 36 | new ComponentTypeMap("Rect", "RectTransform"), 37 | 38 | // Animation 39 | new ComponentTypeMap("Anim", "Animation"), 40 | new ComponentTypeMap("Animator", "Animator"), 41 | 42 | // Graphic 43 | new ComponentTypeMap("Text", "Text"), 44 | new ComponentTypeMap("Image", "Image"), 45 | new ComponentTypeMap("RawImage", "RawImage"), 46 | 47 | // Controls 48 | new ComponentTypeMap("Button", "Button"), 49 | new ComponentTypeMap("Toggle", "Toggle"), 50 | new ComponentTypeMap("TGroup", "ToggleGroup"), 51 | new ComponentTypeMap("Slider", "Slider"), 52 | new ComponentTypeMap("Scrollbar", "Scrollbar"), 53 | new ComponentTypeMap("Dropdown", "Dropdown"), 54 | new ComponentTypeMap("InputField", "InputField"), 55 | 56 | // Container 57 | new ComponentTypeMap("Canvas", "Canvas"), 58 | new ComponentTypeMap("ScrollView", "ScrollRect"), 59 | new ComponentTypeMap("CGroup", "CanvasGroup"), 60 | new ComponentTypeMap("GLGroup", "GridLayoutGroup"), 61 | new ComponentTypeMap("VLGroup", "VerticalLayoutGroup"), 62 | new ComponentTypeMap("HLGroup", "HorizontalLayoutGroup"), 63 | 64 | // Mask 65 | new ComponentTypeMap("Mask", "Mask"), 66 | new ComponentTypeMap("RectMask", "RectMask2D"), 67 | }; 68 | [Tooltip("默认字段前缀。")] 69 | public string m_DefaultFieldNamePrefix = "m_"; 70 | [Tooltip("字段名是否使用组件类型 默认值。")] 71 | public bool m_DefaultFieldNameByType = false; 72 | // 组件类型映射字典。 73 | private Dictionary m_ComponentMapDict; 74 | 75 | [Space(4)] 76 | [Header("Generate")] 77 | [Tooltip("CollectionGenerator。")] 78 | public string m_GeneratorTypeName = "GameExtension.Editor.DefaultCollectionGenerator"; 79 | [Tooltip("默认命名空间。")] 80 | public string m_DefaultNameSpace = "Game"; 81 | [Tooltip("默认代码保存地址。")] 82 | public string m_DefaultCodeSavePath = "Assets"; 83 | [Tooltip("代码模板。")] 84 | public TextAsset m_ComponentsCodeTemplate; 85 | [Tooltip("代码模板。")] 86 | public TextAsset m_BehaviourCodeTemplate; 87 | [Space(4)] 88 | [Header("Extension")] 89 | [Tooltip("代码模板。")] 90 | public TextAsset m_CollectionExtensionCodeTemplate; 91 | 92 | // 命名规范正则表达式 93 | public Regex m_DefaultNameRegex = new Regex(@"^[A-Za-z][A-Za-z0-9_]*$"); 94 | public Regex m_FieldNameRegex = new Regex(@"^[A-Za-z_][A-Za-z0-9_]*$"); 95 | 96 | /// 97 | /// 组件类型映射字典。 98 | /// 99 | public Dictionary ComponentMapDict 100 | { 101 | get 102 | { 103 | if (m_ComponentMapDict == null) 104 | { 105 | m_ComponentMapDict = new Dictionary(); 106 | for (int i = m_ComponentMaps.Count - 1; i >= 0; i--) 107 | { 108 | ComponentTypeMap map = m_ComponentMaps[i]; 109 | if (m_ComponentMapDict.ContainsKey(map.m_TypeKey)) 110 | { 111 | m_ComponentMaps.RemoveAt(i); 112 | continue; 113 | } 114 | m_ComponentMapDict.Add(map.m_TypeKey, map.m_TypeName); 115 | } 116 | } 117 | 118 | return m_ComponentMapDict; 119 | } 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /Scripts/Editor/ComponentCollection/ComponentCollectionSettingsInspector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using UnityEditor; 7 | using UnityEngine; 8 | 9 | namespace GameExtension.Editor 10 | { 11 | [CustomEditor(typeof(ComponentCollectionSettings))] 12 | public class ComponentCollectionSettingsInspector : UnityEditor.Editor 13 | { 14 | public override void OnInspectorGUI() 15 | { 16 | base.OnInspectorGUI(); 17 | 18 | if (GUILayout.Button("Generate Extension Code")) 19 | { 20 | GenerateExtensionCode(); 21 | } 22 | } 23 | 24 | /// 25 | /// 生成 Extension 代码。 26 | /// 27 | private void GenerateExtensionCode() 28 | { 29 | ComponentCollectionSettings settings = (ComponentCollectionSettings) target; 30 | 31 | if (string.IsNullOrEmpty(settings.m_DefaultCodeSavePath)) 32 | { 33 | Debug.LogError("Code save path is invalid."); 34 | return; 35 | } 36 | 37 | if (!Directory.Exists(settings.m_DefaultCodeSavePath)) 38 | { 39 | Directory.CreateDirectory(settings.m_DefaultCodeSavePath); 40 | } 41 | 42 | string nameSpace = settings.m_DefaultNameSpace; 43 | if (string.IsNullOrEmpty(nameSpace) || !settings.m_DefaultNameRegex.IsMatch(nameSpace)) 44 | { 45 | Debug.LogErrorFormat("NameSpace '{0}' is invalid.", nameSpace); 46 | return; 47 | } 48 | 49 | if (settings.m_CollectionExtensionCodeTemplate == null) 50 | { 51 | Debug.LogError("CollectionExtensionCodeTemplate is null, Please check 'ComponentCollectionSettings' asset."); 52 | return; 53 | } 54 | 55 | string codeFileName = string.Format("{0}/ComponentCollectionExtension.cs", settings.m_DefaultCodeSavePath); 56 | if (!CheckGenerateFile(codeFileName)) 57 | { 58 | return; 59 | } 60 | 61 | GenerateExtensionCode(codeFileName, settings.m_CollectionExtensionCodeTemplate.text, settings.m_DefaultNameSpace, settings.ComponentMapDict.Values.ToList()); 62 | AssetDatabase.SaveAssets(); 63 | AssetDatabase.Refresh(); 64 | } 65 | 66 | /// 67 | /// 检查文件是否可以生成。 68 | /// 69 | /// 文件路径 70 | /// 71 | private bool CheckGenerateFile(string filePath) 72 | { 73 | if (File.Exists(filePath)) 74 | { 75 | return EditorUtility.DisplayDialog("File Exits", " File already exits, continue regenerate ?", "Continue", "Cancel"); 76 | } 77 | return true; 78 | } 79 | 80 | /// 81 | /// 生成 Extension 代码。 82 | /// 83 | /// 文件路径。 84 | /// 代码模板。 85 | /// 命名空间。 86 | /// 组件类型。 87 | public void GenerateExtensionCode(string filePath, string codeTemplate, string nameSpace, List componentTypes) 88 | { 89 | if (string.IsNullOrEmpty(codeTemplate)) 90 | { 91 | Debug.LogError("Component behaviour code template file is invalid."); 92 | return; 93 | } 94 | 95 | StringBuilder stringBuilder = new StringBuilder(codeTemplate); 96 | 97 | stringBuilder.Replace("__CREATE_TIME__", DateTime.UtcNow.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss.ff")); 98 | stringBuilder.Replace("__NAME_SPACE__", nameSpace); 99 | string function; 100 | GenerateGetComponentFunction(componentTypes, out function); 101 | stringBuilder.Replace("__GET_COMPONENT__", function); 102 | 103 | SaveFile(filePath, stringBuilder.ToString()); 104 | } 105 | 106 | /// 107 | /// 生成获取组件扩展方法。 108 | /// 109 | /// 组件类型。 110 | /// 获取组件扩展方法代码区域 的字符。 111 | private void GenerateGetComponentFunction(List componentTypes, out string function) 112 | { 113 | StringBuilder functionBuilder = new StringBuilder(); 114 | foreach (string component in componentTypes) 115 | { 116 | functionBuilder.AppendLine(string.Format(" public static {0} Get{0}(this ComponentCollection componentCollection, int index)", component)); 117 | functionBuilder.AppendLine(" {"); 118 | functionBuilder.AppendLine(string.Format(" return componentCollection.GetComponent<{0}>(index);", component)); 119 | functionBuilder.AppendLine(" }"); 120 | functionBuilder.AppendLine(); 121 | } 122 | function = functionBuilder.ToString().TrimEnd(); 123 | } 124 | 125 | /// 126 | /// 保存文件。 127 | /// 128 | /// 文件地址。 129 | /// 内容字符串。 130 | private void SaveFile(string filePath, string content) 131 | { 132 | using (FileStream stream = new FileStream(filePath, FileMode.Create, FileAccess.Write)) 133 | { 134 | using (StreamWriter writer = new StreamWriter(stream, Encoding.UTF8)) 135 | { 136 | writer.Write(content); 137 | } 138 | } 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /Scripts/Editor/GameObjectClone/GameObjectClone.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using UnityEditor; 5 | using UnityEngine; 6 | 7 | namespace GameExtension.Editor 8 | { 9 | public class GameObjectClone : EditorWindow 10 | { 11 | private readonly List m_CacheFieldInfo = new List(); 12 | 13 | private GameObject m_Template; 14 | private GameObject m_Target; 15 | 16 | [MenuItem("Game Extension/GameObject Clone", false)] 17 | private static void ShowWindow() 18 | { 19 | var window = GetWindow("GameObjectClone", true); 20 | window.minSize = new Vector2(400, 200); 21 | window.Show(); 22 | } 23 | 24 | private void OnGUI() 25 | { 26 | GUIClonePrefab(); 27 | } 28 | 29 | private void GUIClonePrefab() 30 | { 31 | EditorGUILayout.BeginVertical(); 32 | { 33 | m_Template = (GameObject) EditorGUILayout.ObjectField("Template", m_Template, typeof(GameObject), true); 34 | m_Target = (GameObject) EditorGUILayout.ObjectField("Target", m_Target, typeof(GameObject), true); 35 | 36 | GUILayout.Space(10); 37 | 38 | if (GUILayout.Button("Clone")) 39 | { 40 | Clone(); 41 | } 42 | 43 | if (GUILayout.Button("Apply Prefab")) 44 | { 45 | string prefabPath = UnityEditor.PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(m_Target); 46 | if (!string.IsNullOrEmpty(prefabPath)) 47 | { 48 | var newPrefab = Clone(); 49 | if (newPrefab != null) 50 | { 51 | SaveNewPrefab(newPrefab, prefabPath); 52 | } 53 | DestroyImmediate(newPrefab); 54 | } 55 | } 56 | 57 | if (GUILayout.Button("Save As")) 58 | { 59 | string prefabPath = UnityEditor.PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(m_Target); 60 | string newPrefabPath = EditorUtility.OpenFilePanel("Save as", prefabPath, "prefab"); 61 | if (!string.IsNullOrEmpty(newPrefabPath)) 62 | { 63 | var newPrefab = Clone(); 64 | if (newPrefab != null) 65 | { 66 | SaveNewPrefab(newPrefab, prefabPath); 67 | } 68 | DestroyImmediate(newPrefab); 69 | } 70 | } 71 | } 72 | EditorGUILayout.EndVertical(); 73 | } 74 | 75 | private GameObject Clone() 76 | { 77 | var templateTransform = m_Target.transform; 78 | var targetTransform = Instantiate(m_Template.gameObject, m_Template.transform.parent).transform; 79 | CloneComponentAndStripChildren(templateTransform, targetTransform); 80 | return targetTransform.gameObject; 81 | } 82 | 83 | private void CloneComponentAndStripChildren(Transform templateTransform, Transform targetTransform) 84 | { 85 | // if (!targetTransform.name.StartsWith(templateTransform.name)) 86 | // { 87 | // Destroy(targetTransform.gameObject); 88 | // return; 89 | // } 90 | 91 | // CloneComponents(templateTransform, targetTransform); 92 | 93 | // 递归处理子物体 94 | 95 | var templateChildren = templateTransform.childCount; 96 | var targetChildren = targetTransform.childCount; 97 | 98 | if (targetChildren < templateChildren) 99 | { 100 | Debug.LogErrorFormat("The number of this {0}'s children is less than the template's.", targetTransform.name); 101 | } 102 | 103 | for (int i = targetChildren - 1; i >= templateChildren; i--) // 去除 相对于模板的 多余的子物体 104 | { 105 | DestroyImmediate(targetTransform.GetChild(i).gameObject); 106 | } 107 | 108 | int number = Mathf.Min(templateChildren, targetChildren); 109 | for (int i = number - 1; i >= 0; i--) 110 | { 111 | CloneComponentAndStripChildren(templateTransform.GetChild(i), targetTransform.GetChild(i)); 112 | } 113 | } 114 | 115 | private void CloneComponents(Transform templateTransform, Transform targetTransform) 116 | { 117 | var templateComponents = templateTransform.GetComponents(); 118 | for (int i = 0; i < templateComponents.Length; i++) 119 | { 120 | Component templateComponent = templateComponents[i]; 121 | Type componentType = templateComponent.GetType(); 122 | Component targetComponent = targetTransform.GetComponent(componentType); 123 | if (targetComponent == null) 124 | { 125 | targetComponent = targetTransform.gameObject.AddComponent(componentType); 126 | } 127 | 128 | CloneComponent(templateComponent, targetComponent); 129 | } 130 | } 131 | 132 | private void CloneComponent(Component templateComponent, Component targetComponent) 133 | { 134 | if (templateComponent == null || targetComponent == null) 135 | { 136 | return; 137 | } 138 | 139 | CacheValueOfReference(targetComponent); 140 | UnityEditorInternal.ComponentUtility.CopyComponent(templateComponent); 141 | UnityEditorInternal.ComponentUtility.PasteComponentValues(targetComponent); 142 | ApplyValueOfReference(targetComponent); 143 | } 144 | 145 | private void CacheValueOfReference(Component targetComponent) 146 | { 147 | FieldInfo[] fieldInfos = targetComponent.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); 148 | foreach (FieldInfo fieldInfo in fieldInfos) 149 | { 150 | if (fieldInfo.FieldType.IsValueType || fieldInfo.FieldType.Name.ToLower().Equals("string")) 151 | { 152 | continue; 153 | } 154 | m_CacheFieldInfo.Add(fieldInfo); 155 | } 156 | } 157 | 158 | private void ApplyValueOfReference(Component targetComponent) 159 | { 160 | foreach (FieldInfo info in m_CacheFieldInfo) 161 | { 162 | FieldInfo fieldInfo = targetComponent.GetType().GetField(info.Name); 163 | if (fieldInfo == null) 164 | { 165 | Debug.LogErrorFormat("Get Field info failed '{0}.{1}'.", targetComponent, info.Name); 166 | continue; 167 | } 168 | fieldInfo.SetValue(targetComponent, info.GetValue(targetComponent)); 169 | } 170 | 171 | m_CacheFieldInfo.Clear(); 172 | } 173 | 174 | private void SaveNewPrefab(GameObject newPrefab, string prefabPath) 175 | { 176 | UnityEditor.PrefabUtility.SaveAsPrefabAssetAndConnect(newPrefab, prefabPath, InteractionMode.AutomatedAction, out bool saveResult); 177 | if (saveResult) 178 | { 179 | Debug.LogFormat("Prefab apply succeed: {0}.", prefabPath); 180 | } 181 | else 182 | { 183 | Debug.LogErrorFormat("Prefab apply failed: {0}.", prefabPath); 184 | } 185 | } 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /Scripts/Editor/UGUIExtension/UGUIMenuExtension.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | using UnityEditor.Experimental.SceneManagement; 3 | using UnityEditor.SceneManagement; 4 | using UnityEngine; 5 | using UnityEngine.EventSystems; 6 | using UnityEngine.SceneManagement; 7 | using UnityEngine.UI; 8 | 9 | // copy from MenuOptions of https://github.com/Unity-Technologies/uGUI.git 10 | namespace GameExtension.Editor 11 | { 12 | /// 13 | /// This script adds the UI menu options to the Unity Editor. 14 | /// 15 | internal static class UGUIMenuExtension 16 | { 17 | private const string kUILayerName = "UI"; 18 | private const string kStandardSpritePath = "UI/Skin/UISprite.psd"; 19 | private const string kBackgroundSpritePath = "UI/Skin/Background.psd"; 20 | private const string kInputFieldBackgroundPath = "UI/Skin/InputFieldBackground.psd"; 21 | private const string kKnobPath = "UI/Skin/Knob.psd"; 22 | private const string kCheckmarkPath = "UI/Skin/Checkmark.psd"; 23 | private const string kDropdownArrowPath = "UI/Skin/DropdownArrow.psd"; 24 | private const string kMaskPath = "UI/Skin/UIMask.psd"; 25 | 26 | private static UGUICreator.Resources s_StandardResources; 27 | 28 | private static UGUICreator.Resources GetStandardResources() 29 | { 30 | if (s_StandardResources.standard == null) 31 | { 32 | s_StandardResources.standard = AssetDatabase.GetBuiltinExtraResource(kStandardSpritePath); 33 | s_StandardResources.background = AssetDatabase.GetBuiltinExtraResource(kBackgroundSpritePath); 34 | s_StandardResources.inputField = AssetDatabase.GetBuiltinExtraResource(kInputFieldBackgroundPath); 35 | s_StandardResources.knob = AssetDatabase.GetBuiltinExtraResource(kKnobPath); 36 | s_StandardResources.checkmark = AssetDatabase.GetBuiltinExtraResource(kCheckmarkPath); 37 | s_StandardResources.dropdown = AssetDatabase.GetBuiltinExtraResource(kDropdownArrowPath); 38 | s_StandardResources.mask = AssetDatabase.GetBuiltinExtraResource(kMaskPath); 39 | } 40 | return s_StandardResources; 41 | } 42 | 43 | private static void SetPositionVisibleInSceneView(RectTransform canvasRTransform, RectTransform itemTransform) 44 | { 45 | // Find the best scene view 46 | SceneView sceneView = SceneView.lastActiveSceneView; 47 | if (sceneView == null && SceneView.sceneViews.Count > 0) 48 | sceneView = SceneView.sceneViews[0] as SceneView; 49 | 50 | // Couldn't find a SceneView. Don't set position. 51 | if (sceneView == null || sceneView.camera == null) 52 | return; 53 | 54 | // Create world space Plane from canvas position. 55 | Vector2 localPlanePosition; 56 | Camera camera = sceneView.camera; 57 | Vector3 position = Vector3.zero; 58 | if (RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRTransform, new Vector2(camera.pixelWidth / 2, camera.pixelHeight / 2), camera, out localPlanePosition)) 59 | { 60 | // Adjust for canvas pivot 61 | localPlanePosition.x = localPlanePosition.x + canvasRTransform.sizeDelta.x * canvasRTransform.pivot.x; 62 | localPlanePosition.y = localPlanePosition.y + canvasRTransform.sizeDelta.y * canvasRTransform.pivot.y; 63 | 64 | localPlanePosition.x = Mathf.Clamp(localPlanePosition.x, 0, canvasRTransform.sizeDelta.x); 65 | localPlanePosition.y = Mathf.Clamp(localPlanePosition.y, 0, canvasRTransform.sizeDelta.y); 66 | 67 | // Adjust for anchoring 68 | position.x = localPlanePosition.x - canvasRTransform.sizeDelta.x * itemTransform.anchorMin.x; 69 | position.y = localPlanePosition.y - canvasRTransform.sizeDelta.y * itemTransform.anchorMin.y; 70 | 71 | Vector3 minLocalPosition; 72 | minLocalPosition.x = canvasRTransform.sizeDelta.x * (0 - canvasRTransform.pivot.x) + itemTransform.sizeDelta.x * itemTransform.pivot.x; 73 | minLocalPosition.y = canvasRTransform.sizeDelta.y * (0 - canvasRTransform.pivot.y) + itemTransform.sizeDelta.y * itemTransform.pivot.y; 74 | 75 | Vector3 maxLocalPosition; 76 | maxLocalPosition.x = canvasRTransform.sizeDelta.x * (1 - canvasRTransform.pivot.x) - itemTransform.sizeDelta.x * itemTransform.pivot.x; 77 | maxLocalPosition.y = canvasRTransform.sizeDelta.y * (1 - canvasRTransform.pivot.y) - itemTransform.sizeDelta.y * itemTransform.pivot.y; 78 | 79 | position.x = Mathf.Clamp(position.x, minLocalPosition.x, maxLocalPosition.x); 80 | position.y = Mathf.Clamp(position.y, minLocalPosition.y, maxLocalPosition.y); 81 | } 82 | 83 | itemTransform.anchoredPosition = position; 84 | itemTransform.localRotation = Quaternion.identity; 85 | itemTransform.localScale = Vector3.one; 86 | } 87 | 88 | private static void PlaceUIElementRoot(GameObject element, MenuCommand menuCommand) 89 | { 90 | #if UNITY_2018_3_OR_NEWER 91 | GameObject parent = menuCommand.context as GameObject; 92 | bool explicitParentChoice = true; 93 | if (parent == null) 94 | { 95 | parent = GetOrCreateCanvasGameObject(); 96 | explicitParentChoice = false; 97 | 98 | // If in Prefab Mode, Canvas has to be part of Prefab contents, 99 | // otherwise use Prefab root instead. 100 | PrefabStage prefabStage = PrefabStageUtility.GetCurrentPrefabStage(); 101 | if (prefabStage != null && !prefabStage.IsPartOfPrefabContents(parent)) 102 | parent = prefabStage.prefabContentsRoot; 103 | } 104 | 105 | if (parent.GetComponentsInParent(true).Length == 0) 106 | { 107 | // Create canvas under context GameObject, 108 | // and make that be the parent which UI element is added under. 109 | GameObject canvas = CreateNewUI(); 110 | canvas.transform.SetParent(parent.transform, false); 111 | parent = canvas; 112 | } 113 | 114 | // Setting the element to be a child of an element already in the scene should 115 | // be sufficient to also move the element to that scene. 116 | // However, it seems the element needs to be already in its destination scene when the 117 | // RegisterCreatedObjectUndo is performed; otherwise the scene it was created in is dirtied. 118 | SceneManager.MoveGameObjectToScene(element, parent.scene); 119 | 120 | Undo.RegisterCreatedObjectUndo(element, "Create " + element.name); 121 | 122 | if (element.transform.parent == null) 123 | { 124 | Undo.SetTransformParent(element.transform, parent.transform, "Parent " + element.name); 125 | } 126 | 127 | GameObjectUtility.EnsureUniqueNameForSibling(element); 128 | 129 | // We have to fix up the undo name since the name of the object was only known after reparenting it. 130 | Undo.SetCurrentGroupName("Create " + element.name); 131 | 132 | GameObjectUtility.SetParentAndAlign(element, parent); 133 | if (!explicitParentChoice) // not a context click, so center in sceneview 134 | SetPositionVisibleInSceneView(parent.GetComponent(), element.GetComponent()); 135 | 136 | Selection.activeGameObject = element; 137 | #else 138 | GameObject parent = menuCommand.context as GameObject; 139 | if (parent == null || parent.GetComponentInParent() == null) 140 | { 141 | parent = GetOrCreateCanvasGameObject(); 142 | } 143 | 144 | string uniqueName = GameObjectUtility.GetUniqueNameForSibling(parent.transform, element.name); 145 | element.name = uniqueName; 146 | Undo.RegisterCreatedObjectUndo(element, "Create " + element.name); 147 | Undo.SetTransformParent(element.transform, parent.transform, "Parent " + element.name); 148 | GameObjectUtility.SetParentAndAlign(element, parent); 149 | if (parent != menuCommand.context) // not a context click, so center in sceneview 150 | SetPositionVisibleInSceneView(parent.GetComponent(), element.GetComponent()); 151 | 152 | Selection.activeGameObject = element; 153 | #endif 154 | } 155 | 156 | // Graphic elements 157 | 158 | [MenuItem("GameObject/Custom UI/Text", false, 0)] 159 | public static void AddText(MenuCommand menuCommand) 160 | { 161 | GameObject go = UGUICreator.CreateText(false); 162 | PlaceUIElementRoot(go, menuCommand); 163 | } 164 | 165 | [MenuItem("GameObject/Custom UI/Text - Raycast", false, 1)] 166 | public static void AddTextRaycast(MenuCommand menuCommand) 167 | { 168 | GameObject go = UGUICreator.CreateText(true); 169 | PlaceUIElementRoot(go, menuCommand); 170 | } 171 | 172 | [MenuItem("GameObject/Custom UI/Image", false, 6)] 173 | public static void AddImage(MenuCommand menuCommand) 174 | { 175 | GameObject go = UGUICreator.CreateImage(false); 176 | PlaceUIElementRoot(go, menuCommand); 177 | } 178 | 179 | [MenuItem("GameObject/Custom UI/Image - Raycast", false, 7)] 180 | public static void AddImageRaycast(MenuCommand menuCommand) 181 | { 182 | GameObject go = UGUICreator.CreateImage(true); 183 | PlaceUIElementRoot(go, menuCommand); 184 | } 185 | 186 | [MenuItem("GameObject/Custom UI/RawImage", false, 8)] 187 | public static void AddRawImage(MenuCommand menuCommand) 188 | { 189 | GameObject go = UGUICreator.CreateRawImage(false); 190 | PlaceUIElementRoot(go, menuCommand); 191 | } 192 | 193 | [MenuItem("GameObject/Custom UI/RawImage - Raycast", false, 9)] 194 | public static void AddRawImageRaycast(MenuCommand menuCommand) 195 | { 196 | GameObject go = UGUICreator.CreateRawImage(true); 197 | PlaceUIElementRoot(go, menuCommand); 198 | } 199 | 200 | // Button and toggle are controls you just click on. 201 | 202 | [MenuItem("GameObject/Custom UI/Button", false, 30)] 203 | public static void AddButton(MenuCommand menuCommand) 204 | { 205 | GameObject go = UGUICreator.CreateButton(GetStandardResources(), false); 206 | PlaceUIElementRoot(go, menuCommand); 207 | } 208 | 209 | [MenuItem("GameObject/Custom UI/Button - Text", false, 31)] 210 | public static void AddButtonWithText(MenuCommand menuCommand) 211 | { 212 | GameObject go = UGUICreator.CreateButton(GetStandardResources(), true); 213 | PlaceUIElementRoot(go, menuCommand); 214 | } 215 | 216 | [MenuItem("GameObject/Custom UI/Toggle", false, 36)] 217 | public static void AddToggle(MenuCommand menuCommand) 218 | { 219 | GameObject go = UGUICreator.CreateToggle(GetStandardResources(), false); 220 | PlaceUIElementRoot(go, menuCommand); 221 | } 222 | 223 | [MenuItem("GameObject/Custom UI/Toggle - Text", false, 38)] 224 | public static void AddToggleWithText(MenuCommand menuCommand) 225 | { 226 | GameObject go = UGUICreator.CreateToggle(GetStandardResources(), true); 227 | PlaceUIElementRoot(go, menuCommand); 228 | } 229 | 230 | // Slider and Scrollbar modify a number 231 | 232 | [MenuItem("GameObject/Custom UI/Slider", false, 38)] 233 | public static void AddSlider(MenuCommand menuCommand) 234 | { 235 | GameObject go = UGUICreator.CreateSlider(GetStandardResources()); 236 | PlaceUIElementRoot(go, menuCommand); 237 | } 238 | 239 | [MenuItem("GameObject/Custom UI/Scrollbar", false, 39)] 240 | public static void AddScrollbar(MenuCommand menuCommand) 241 | { 242 | GameObject go = UGUICreator.CreateScrollbar(GetStandardResources()); 243 | PlaceUIElementRoot(go, menuCommand); 244 | } 245 | 246 | // More advanced controls below 247 | 248 | [MenuItem("GameObject/Custom UI/InputField", false, 40)] 249 | public static void AddInputField(MenuCommand menuCommand) 250 | { 251 | GameObject go = UGUICreator.CreateInputField(GetStandardResources()); 252 | PlaceUIElementRoot(go, menuCommand); 253 | } 254 | 255 | // Containers 256 | 257 | [MenuItem("GameObject/Custom UI/Canvas", false, 60)] 258 | public static void AddCanvas(MenuCommand menuCommand) 259 | { 260 | var go = CreateNewUI(); 261 | GameObjectUtility.SetParentAndAlign(go, menuCommand.context as GameObject); 262 | if (go.transform.parent as RectTransform) 263 | { 264 | RectTransform rect = go.transform as RectTransform; 265 | rect.anchorMin = Vector2.zero; 266 | rect.anchorMax = Vector2.one; 267 | rect.anchoredPosition = Vector2.zero; 268 | rect.sizeDelta = Vector2.zero; 269 | } 270 | Selection.activeGameObject = go; 271 | } 272 | 273 | // [MenuItem("GameObject/Custom UI/Panel", false, 62)] 274 | // public static void AddPanel(MenuCommand menuCommand) 275 | // { 276 | // GameObject go = UGUICreator.CreatePanel(GetStandardResources()); 277 | // PlaceUIElementRoot(go, menuCommand); 278 | // 279 | // // Panel is special, we need to ensure there's no padding after repositioning. 280 | // RectTransform rect = go.GetComponent(); 281 | // rect.anchoredPosition = Vector2.zero; 282 | // rect.sizeDelta = Vector2.zero; 283 | // } 284 | 285 | [MenuItem("GameObject/Custom UI/ScrollView - Mask", false, 66)] 286 | public static void AddScrollViewUseMask(MenuCommand menuCommand) 287 | { 288 | GameObject go = UGUICreator.CreateScrollView(GetStandardResources(), true); 289 | PlaceUIElementRoot(go, menuCommand); 290 | } 291 | 292 | [MenuItem("GameObject/Custom UI/ScrollView - RectMask", false, 67)] 293 | public static void AddScrollViewUseRectMask(MenuCommand menuCommand) 294 | { 295 | GameObject go = UGUICreator.CreateScrollView(GetStandardResources(), false); 296 | PlaceUIElementRoot(go, menuCommand); 297 | } 298 | 299 | [MenuItem("GameObject/Custom UI/Dropdown - Mask", false, 68)] 300 | public static void AddDropdownUseMask(MenuCommand menuCommand) 301 | { 302 | GameObject go = UGUICreator.CreateDropdown(GetStandardResources(), true); 303 | PlaceUIElementRoot(go, menuCommand); 304 | } 305 | 306 | [MenuItem("GameObject/Custom UI/Dropdown - RectMask", false, 69)] 307 | public static void AddDropdownUseRectMask(MenuCommand menuCommand) 308 | { 309 | GameObject go = UGUICreator.CreateDropdown(GetStandardResources(), false); 310 | PlaceUIElementRoot(go, menuCommand); 311 | } 312 | 313 | // Helper methods 314 | 315 | private static GameObject CreateNewUI() 316 | { 317 | // Root for the UI 318 | var root = new GameObject("Canvas"); 319 | root.layer = LayerMask.NameToLayer(kUILayerName); 320 | Canvas canvas = root.AddComponent(); 321 | canvas.renderMode = RenderMode.ScreenSpaceOverlay; 322 | root.AddComponent(); 323 | root.AddComponent(); 324 | Undo.RegisterCreatedObjectUndo(root, "Create " + root.name); 325 | 326 | #if UNITY_2018_3_OR_NEWER 327 | // Works for all stages. 328 | StageUtility.PlaceGameObjectInCurrentStage(root); 329 | bool customScene = false; 330 | PrefabStage prefabStage = PrefabStageUtility.GetCurrentPrefabStage(); 331 | if (prefabStage != null) 332 | { 333 | root.transform.SetParent(prefabStage.prefabContentsRoot.transform, false); 334 | customScene = true; 335 | } 336 | 337 | Undo.RegisterCreatedObjectUndo(root, "Create " + root.name); 338 | 339 | // If there is no event system add one... 340 | // No need to place event system in custom scene as these are temporary anyway. 341 | // It can be argued for or against placing it in the user scenes, 342 | // but let's not modify scene user is not currently looking at. 343 | if (!customScene) 344 | CreateEventSystem(false); 345 | #else 346 | // if there is no event system add one... 347 | CreateEventSystem(false); 348 | #endif 349 | 350 | return root; 351 | } 352 | 353 | [MenuItem("GameObject/Custom UI/EventSystem", false, 100)] 354 | public static void CreateEventSystem(MenuCommand menuCommand) 355 | { 356 | GameObject parent = menuCommand.context as GameObject; 357 | CreateEventSystem(true, parent); 358 | } 359 | 360 | private static void CreateEventSystem(bool select) 361 | { 362 | CreateEventSystem(select, null); 363 | } 364 | 365 | private static void CreateEventSystem(bool select, GameObject parent) 366 | { 367 | #if UNITY_2018_3_OR_NEWER 368 | StageHandle stage = parent == null ? StageUtility.GetCurrentStageHandle() : StageUtility.GetStageHandle(parent); 369 | var esys = stage.FindComponentOfType(); 370 | if (esys == null) 371 | { 372 | var eventSystem = new GameObject("EventSystem"); 373 | if (parent == null) 374 | StageUtility.PlaceGameObjectInCurrentStage(eventSystem); 375 | else 376 | GameObjectUtility.SetParentAndAlign(eventSystem, parent); 377 | esys = eventSystem.AddComponent(); 378 | eventSystem.AddComponent(); 379 | 380 | Undo.RegisterCreatedObjectUndo(eventSystem, "Create " + eventSystem.name); 381 | } 382 | #else 383 | var esys = Object.FindObjectOfType(); 384 | if (esys == null) 385 | { 386 | var eventSystem = new GameObject("EventSystem"); 387 | GameObjectUtility.SetParentAndAlign(eventSystem, parent); 388 | esys = eventSystem.AddComponent(); 389 | eventSystem.AddComponent(); 390 | 391 | Undo.RegisterCreatedObjectUndo(eventSystem, "Create " + eventSystem.name); 392 | } 393 | #endif 394 | 395 | if (select && esys != null) 396 | { 397 | Selection.activeGameObject = esys.gameObject; 398 | } 399 | } 400 | 401 | // Helper function that returns a Canvas GameObject; preferably a parent of the selection, or other existing Canvas. 402 | private static GameObject GetOrCreateCanvasGameObject() 403 | { 404 | GameObject selectedGo = Selection.activeGameObject; 405 | 406 | #if UNITY_2018_3_OR_NEWER 407 | // Try to find a gameobject that is the selected GO or one if its parents. 408 | Canvas canvas = (selectedGo != null) ? selectedGo.GetComponentInParent() : null; 409 | if (IsValidCanvas(canvas)) 410 | return canvas.gameObject; 411 | 412 | // No canvas in selection or its parents? Then use any valid canvas. 413 | // We have to find all loaded Canvases, not just the ones in main scenes. 414 | Canvas[] canvasArray = StageUtility.GetCurrentStageHandle().FindComponentsOfType(); 415 | for (int i = 0; i < canvasArray.Length; i++) 416 | if (IsValidCanvas(canvasArray[i])) 417 | return canvasArray[i].gameObject; 418 | #else 419 | // Try to find a gameobject that is the selected GO or one if its parents. 420 | Canvas canvas = (selectedGo != null) ? selectedGo.GetComponentInParent() : null; 421 | if (canvas != null && canvas.gameObject.activeInHierarchy) 422 | return canvas.gameObject; 423 | 424 | // No canvas in selection or its parents? Then use just any canvas.. 425 | canvas = Object.FindObjectOfType(typeof(Canvas)) as Canvas; 426 | if (canvas != null && canvas.gameObject.activeInHierarchy) 427 | return canvas.gameObject; 428 | #endif 429 | 430 | // No canvas in the scene at all? Then create a new one. 431 | return CreateNewUI(); 432 | } 433 | 434 | #if UNITY_2018_3_OR_NEWER 435 | private static bool IsValidCanvas(Canvas canvas) 436 | { 437 | if (canvas == null || !canvas.gameObject.activeInHierarchy) 438 | return false; 439 | 440 | // It's important that the non-editable canvas from a prefab scene won't be rejected, 441 | // but canvases not visible in the Hierarchy at all do. Don't check for HideAndDontSave. 442 | if (EditorUtility.IsPersistent(canvas) || (canvas.hideFlags & HideFlags.HideInHierarchy) != 0) 443 | return false; 444 | 445 | if (StageUtility.GetStageHandle(canvas.gameObject) != StageUtility.GetCurrentStageHandle()) 446 | return false; 447 | 448 | return true; 449 | } 450 | #endif 451 | 452 | // Extension 453 | 454 | [MenuItem("GameObject/Custom UI/RectRaycast", false, 10)] 455 | public static void AddRectRaycast(MenuCommand menuCommand) 456 | { 457 | GameObject go = UGUICreator.CreateRectRaycast(); 458 | PlaceUIElementRoot(go, menuCommand); 459 | } 460 | 461 | [MenuItem("GameObject/Custom UI/RectButton", false, 11)] 462 | public static void AddRectButton(MenuCommand menuCommand) 463 | { 464 | GameObject go = UGUICreator.CreateRectButton(); 465 | PlaceUIElementRoot(go, menuCommand); 466 | } 467 | } 468 | } 469 | -------------------------------------------------------------------------------- /Scripts/Editor/ComponentCollection/ComponentCollectionInspector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using UnityEditor; 5 | using UnityEngine; 6 | 7 | namespace GameExtension.Editor 8 | { 9 | [CustomEditor(typeof(ComponentCollection))] 10 | public class ComponentCollectionInspector : UnityEditor.Editor 11 | { 12 | private const string NoneOptionName = ""; 13 | private readonly string[] m_FieldNameRule = {"TypeKey", "TypeName"}; 14 | 15 | private int m_ExitsSettingsCount; 16 | private ComponentCollectionSettings m_Settings; 17 | 18 | private SerializedProperty m_CollectorTypeName; 19 | private SerializedProperty m_FieldNamePrefix; 20 | private SerializedProperty m_FieldNameByType; 21 | private SerializedProperty m_FieldNames; 22 | private SerializedProperty m_FieldComponents; 23 | private string[] m_CollectorTypeNames; 24 | private int m_CollectorTypeNameIndex; 25 | private IComponentCollector m_Collector; 26 | private int m_FieldNameRuleIndex; 27 | private bool m_ShowComponentField; 28 | 29 | private SerializedProperty m_GeneratorTypeName; 30 | private SerializedProperty m_NameSpace; 31 | private SerializedProperty m_ClassName; 32 | private SerializedProperty m_CodeSavePath; 33 | private string[] m_GeneratorTypeNames; 34 | private int m_GeneratorTypeNameIndex; 35 | private ICollectionGenerator m_Generator; 36 | 37 | private SerializedProperty m_Setup; 38 | 39 | private SerializedProperty m_Components; 40 | 41 | private IComponentCollector Collector 42 | { 43 | get 44 | { 45 | if (m_Collector == null) 46 | { 47 | string collectorTypeName = m_CollectorTypeName.stringValue; 48 | if (string.IsNullOrEmpty(collectorTypeName)) 49 | { 50 | Debug.LogError("Collector is invalid."); 51 | return null; 52 | } 53 | 54 | Type collectorType = TypeUtility.GetEditorType(collectorTypeName); 55 | if (collectorType == null) 56 | { 57 | Debug.LogErrorFormat("Can not get collector type '{0}'.", collectorTypeName); 58 | return null; 59 | } 60 | 61 | m_Collector = (IComponentCollector) Activator.CreateInstance(collectorType); 62 | if (m_Collector == null) 63 | { 64 | Debug.LogErrorFormat("Can not create collector instance '{0}'.", collectorTypeName); 65 | return null; 66 | } 67 | } 68 | 69 | return m_Collector; 70 | } 71 | } 72 | 73 | private ICollectionGenerator Generator 74 | { 75 | get 76 | { 77 | if (m_Generator == null) 78 | { 79 | string generatorTypeName = m_GeneratorTypeName.stringValue; 80 | if (string.IsNullOrEmpty(generatorTypeName)) 81 | { 82 | Debug.LogError("Generator is invalid."); 83 | return null; 84 | } 85 | 86 | Type generatorType = TypeUtility.GetEditorType(generatorTypeName); 87 | if (generatorType == null) 88 | { 89 | Debug.LogErrorFormat("Can not get generator type '{0}'.", generatorTypeName); 90 | return null; 91 | } 92 | 93 | m_Generator = (ICollectionGenerator) Activator.CreateInstance(generatorType); 94 | if (m_Generator == null) 95 | { 96 | Debug.LogErrorFormat("Can not create generator instance '{0}'.", generatorTypeName); 97 | return null; 98 | } 99 | } 100 | 101 | return m_Generator; 102 | } 103 | } 104 | 105 | private void OnEnable() 106 | { 107 | string[] paths = AssetDatabase.FindAssets("t:ComponentCollectionSettings"); 108 | m_ExitsSettingsCount = paths.Length; 109 | if (m_ExitsSettingsCount != 1) 110 | { 111 | return; 112 | } 113 | 114 | string path = AssetDatabase.GUIDToAssetPath(paths[0]); 115 | m_Settings = AssetDatabase.LoadAssetAtPath(path); 116 | 117 | InitSerializeData(); 118 | m_ShowComponentField = true; 119 | } 120 | 121 | public override void OnInspectorGUI() 122 | { 123 | if (m_Settings == null) 124 | { 125 | DrawSettingsGUI(); 126 | return; 127 | } 128 | 129 | serializedObject.Update(); 130 | 131 | DrawCollectGUI(); 132 | DrawGenerateGUI(); 133 | 134 | if (m_Setup.boolValue == false) 135 | { 136 | SetupDefaultValue(); 137 | } 138 | 139 | serializedObject.ApplyModifiedProperties(); 140 | } 141 | 142 | /// 143 | /// 初始化序列化数据 144 | /// 145 | private void InitSerializeData() 146 | { 147 | // Setup 148 | m_Setup = serializedObject.FindProperty("m_Setup"); 149 | 150 | // Collect settings 151 | m_CollectorTypeName = serializedObject.FindProperty("m_CollectorTypeName"); 152 | m_FieldNamePrefix = serializedObject.FindProperty("m_FieldNamePrefix"); 153 | m_FieldNameByType = serializedObject.FindProperty("m_FieldNameByType"); 154 | m_FieldNames = serializedObject.FindProperty("m_FieldNames"); 155 | m_FieldComponents = serializedObject.FindProperty("m_FieldComponents"); 156 | 157 | // CollectorTypeNames 158 | List collectorTypeNames = new List {NoneOptionName}; 159 | collectorTypeNames.AddRange(TypeUtility.GetEditorTypeNames(typeof(IComponentCollector))); 160 | m_CollectorTypeNames = collectorTypeNames.ToArray(); 161 | 162 | // CollectorCollectorTypeNameIndex 163 | m_CollectorTypeNameIndex = 0; 164 | if (!string.IsNullOrEmpty(m_CollectorTypeName.stringValue)) 165 | { 166 | m_CollectorTypeNameIndex = collectorTypeNames.IndexOf(m_CollectorTypeName.stringValue); 167 | if (m_CollectorTypeNameIndex <= 0) 168 | { 169 | m_CollectorTypeNameIndex = 0; 170 | m_CollectorTypeName.stringValue = null; 171 | m_Collector = null; 172 | } 173 | } 174 | collectorTypeNames.Clear(); 175 | 176 | // Generate settings 177 | m_GeneratorTypeName = serializedObject.FindProperty("m_GeneratorTypeName"); 178 | m_NameSpace = serializedObject.FindProperty("m_NameSpace"); 179 | m_ClassName = serializedObject.FindProperty("m_ClassName"); 180 | m_CodeSavePath = serializedObject.FindProperty("m_CodeSavePath"); 181 | 182 | // GeneratorTypeNames 183 | List generatorTypeNames = new List {NoneOptionName}; 184 | generatorTypeNames.AddRange(TypeUtility.GetEditorTypeNames(typeof(ICollectionGenerator))); 185 | m_GeneratorTypeNames = generatorTypeNames.ToArray(); 186 | 187 | // GeneratorTypeNameIndex 188 | m_GeneratorTypeNameIndex = 0; 189 | if (!string.IsNullOrEmpty(m_GeneratorTypeName.stringValue)) 190 | { 191 | m_GeneratorTypeNameIndex = generatorTypeNames.IndexOf(m_GeneratorTypeName.stringValue); 192 | if (m_GeneratorTypeNameIndex <= 0) 193 | { 194 | m_GeneratorTypeNameIndex = 0; 195 | m_GeneratorTypeName.stringValue = null; 196 | m_Generator = null; 197 | } 198 | } 199 | generatorTypeNames.Clear(); 200 | 201 | // m_FieldNameRuleIndex 202 | m_FieldNameRuleIndex = m_FieldNameByType.boolValue ? 1 : 0; 203 | 204 | // Runtime Components 205 | m_Components = serializedObject.FindProperty("m_Components"); 206 | } 207 | 208 | /// 209 | /// 设置默认值。 210 | /// 211 | private void SetupDefaultValue() 212 | { 213 | m_Setup.boolValue = true; 214 | 215 | m_CollectorTypeName.stringValue = m_Settings.m_CollectorTypeName; 216 | m_FieldNamePrefix.stringValue = m_Settings.m_DefaultFieldNamePrefix; 217 | m_FieldNameByType.boolValue = m_Settings.m_DefaultFieldNameByType; 218 | 219 | m_GeneratorTypeName.stringValue = m_Settings.m_GeneratorTypeName; 220 | m_NameSpace.stringValue = m_Settings.m_DefaultNameSpace; 221 | m_ClassName.stringValue = serializedObject.targetObject.name; 222 | m_CodeSavePath.stringValue = m_Settings.m_DefaultCodeSavePath; 223 | 224 | List temp = new List(m_CollectorTypeNames); 225 | int collectorIndex = temp.IndexOf(m_Settings.m_CollectorTypeName); 226 | m_CollectorTypeNameIndex = Mathf.Max(0, collectorIndex); 227 | temp = new List(m_GeneratorTypeNames); 228 | int generatorIndex = temp.IndexOf(m_Settings.m_GeneratorTypeName); 229 | m_GeneratorTypeNameIndex = Mathf.Max(0, generatorIndex); 230 | temp.Clear(); 231 | 232 | m_FieldNameRuleIndex = m_FieldNameByType.boolValue ? 1 : 0; 233 | } 234 | 235 | /// 236 | /// 绘制 Settings GUI。 237 | /// 238 | private void DrawSettingsGUI() 239 | { 240 | if (m_ExitsSettingsCount > 1) 241 | { 242 | EditorGUILayout.HelpBox("Multiple 'ComponentCollectionSettings' asset exist. Please delete the redundant assets.", MessageType.Warning); 243 | return; 244 | } 245 | 246 | EditorGUILayout.HelpBox("Need a 'ComponentCollectionSettings' asset, Please create one.", MessageType.Error); 247 | if (GUILayout.Button("Create")) 248 | { 249 | m_Settings = CreateInstance(); 250 | AssetDatabase.CreateAsset(m_Settings, "Assets/ComponentCollectionSettings.asset"); 251 | AssetDatabase.SaveAssets(); 252 | AssetDatabase.Refresh(); 253 | InitSerializeData(); 254 | } 255 | } 256 | 257 | /// 258 | /// 绘制收集设置 GUI。 259 | /// 260 | private void DrawCollectGUI() 261 | { 262 | EditorGUILayout.BeginVertical("Box"); 263 | { 264 | GUILayout.Label("Collect Settings", "BoldLabel"); 265 | 266 | int collectorTypeNameIndex = EditorGUILayout.Popup("Collector", m_CollectorTypeNameIndex, m_CollectorTypeNames); 267 | if (collectorTypeNameIndex != m_CollectorTypeNameIndex) 268 | { 269 | m_CollectorTypeNameIndex = collectorTypeNameIndex; 270 | m_CollectorTypeName.stringValue = m_CollectorTypeNameIndex <= 0 ? null : m_CollectorTypeNames[m_CollectorTypeNameIndex]; 271 | m_Collector = null; 272 | } 273 | 274 | EditorGUILayout.BeginHorizontal(); 275 | { 276 | m_FieldNamePrefix.stringValue = EditorGUILayout.TextField("Field Name Rule", m_FieldNamePrefix.stringValue); 277 | int index = EditorGUILayout.Popup(m_FieldNameRuleIndex, m_FieldNameRule); 278 | if (m_FieldNameRuleIndex != index) 279 | { 280 | m_FieldNameRuleIndex = index; 281 | m_FieldNameByType.boolValue = m_FieldNameRuleIndex == 1; 282 | } 283 | } 284 | EditorGUILayout.EndHorizontal(); 285 | 286 | EditorGUI.indentLevel++; 287 | m_ShowComponentField = EditorGUILayout.Foldout(m_ShowComponentField, "Components", true); 288 | EditorGUI.indentLevel--; 289 | if (m_ShowComponentField) 290 | { 291 | int deleteIndex = -1; 292 | for (int i = 0; i < m_FieldNames.arraySize; i++) 293 | { 294 | EditorGUILayout.BeginHorizontal(); 295 | { 296 | GUILayout.Label(string.Format("[{0}]", i), GUILayout.Width(30)); 297 | SerializedProperty fieldName = m_FieldNames.GetArrayElementAtIndex(i); 298 | SerializedProperty component = m_FieldComponents.GetArrayElementAtIndex(i); 299 | fieldName.stringValue = EditorGUILayout.TextField(fieldName.stringValue); 300 | component.objectReferenceValue = EditorGUILayout.ObjectField(component.objectReferenceValue, typeof(Component), true); 301 | if (GUILayout.Button("", "OL Minus")) 302 | { 303 | deleteIndex = i; 304 | } 305 | } 306 | EditorGUILayout.EndHorizontal(); 307 | EditorGUILayout.Space(4); 308 | } 309 | if (deleteIndex != -1) 310 | { 311 | SerializedArrayDeleteIndex(m_FieldNames, deleteIndex); 312 | SerializedArrayDeleteIndex(m_FieldComponents, deleteIndex); 313 | } 314 | } 315 | 316 | if (GUILayout.Button("Collect Components To Update")) 317 | { 318 | CollectComponentFieldsToUpdate(); 319 | SyncSerializeComponents(); 320 | } 321 | 322 | if (GUILayout.Button("Collect Components To Add")) 323 | { 324 | CollectComponentFieldsToAdd(); 325 | SyncSerializeComponents(); 326 | } 327 | 328 | if (GUILayout.Button("Remove Null Component")) 329 | { 330 | RemoveNullComponent(); 331 | SyncSerializeComponents(); 332 | } 333 | } 334 | EditorGUILayout.EndVertical(); 335 | } 336 | 337 | /// 338 | /// 绘制生成设置 GUI。 339 | /// 340 | private void DrawGenerateGUI() 341 | { 342 | EditorGUILayout.BeginVertical("Box"); 343 | { 344 | GUILayout.Label("Generate Settings", "BoldLabel"); 345 | 346 | int generatorTypeNameIndex = EditorGUILayout.Popup("Generator", m_GeneratorTypeNameIndex, m_GeneratorTypeNames); 347 | if (generatorTypeNameIndex != m_GeneratorTypeNameIndex) 348 | { 349 | m_GeneratorTypeNameIndex = generatorTypeNameIndex; 350 | m_GeneratorTypeName.stringValue = m_GeneratorTypeNameIndex <= 0 ? null : m_GeneratorTypeNames[m_GeneratorTypeNameIndex]; 351 | m_Generator = null; 352 | } 353 | 354 | EditorGUILayout.BeginHorizontal(); 355 | { 356 | m_NameSpace.stringValue = EditorGUILayout.TextField("Name Space", m_NameSpace.stringValue); 357 | if (GUILayout.Button("Default", GUILayout.Width(60))) 358 | { 359 | m_NameSpace.stringValue = m_Settings.m_DefaultNameSpace; 360 | } 361 | } 362 | EditorGUILayout.EndHorizontal(); 363 | 364 | EditorGUILayout.BeginHorizontal(); 365 | { 366 | m_ClassName.stringValue = EditorGUILayout.TextField("Class Name", m_ClassName.stringValue); 367 | if (GUILayout.Button("Default", GUILayout.Width(60))) 368 | { 369 | m_ClassName.stringValue = serializedObject.targetObject.name; 370 | } 371 | } 372 | EditorGUILayout.EndHorizontal(); 373 | 374 | EditorGUILayout.BeginHorizontal(); 375 | { 376 | EditorGUILayout.LabelField("Code Save Path", m_CodeSavePath.stringValue); 377 | if (GUILayout.Button("Select", GUILayout.Width(60))) 378 | { 379 | string path = EditorUtility.SaveFolderPanel("Select Save Path", m_CodeSavePath.stringValue, string.Empty); 380 | if (string.IsNullOrEmpty(path)) 381 | { 382 | Debug.LogError("Select folder path is invalid."); 383 | } 384 | else 385 | { 386 | int index = path.IndexOf("Assets", StringComparison.Ordinal); 387 | if (index < 0) 388 | { 389 | Debug.LogWarning("Suggest to save to any folder of 'Assets'."); 390 | } 391 | m_CodeSavePath.stringValue = path.Substring(index); 392 | } 393 | } 394 | } 395 | EditorGUILayout.EndHorizontal(); 396 | 397 | if (GUILayout.Button("Generate Components Code")) 398 | { 399 | GenerateComponentsCode(); 400 | } 401 | 402 | if (GUILayout.Button("Generate Behaviour Code")) 403 | { 404 | GenerateBehaviourCode(); 405 | } 406 | 407 | if (GUILayout.Button("Add Behaviour Component")) 408 | { 409 | AddBehaviourComponent(); 410 | } 411 | } 412 | EditorGUILayout.EndVertical(); 413 | } 414 | 415 | #region Collect 416 | 417 | /// 418 | /// 以更新组件字段列表的方式收集。 419 | /// 420 | private void CollectComponentFieldsToUpdate() 421 | { 422 | if (Collector == null) 423 | { 424 | return; 425 | } 426 | 427 | m_FieldNames.ClearArray(); 428 | m_FieldComponents.ClearArray(); 429 | Transform transform = ((Component) target).transform; 430 | Dictionary fieldComponentDict = Collector.CollectComponentFields(transform, m_FieldNamePrefix.stringValue, m_FieldNameByType.boolValue, m_Settings.ComponentMapDict); 431 | foreach (var pair in fieldComponentDict) 432 | { 433 | CollectComponentField(pair.Key, pair.Value); 434 | } 435 | } 436 | 437 | /// 438 | /// 以增加组件字段到列表的方式收集。 439 | /// 440 | private void CollectComponentFieldsToAdd() 441 | { 442 | if (Collector == null) 443 | { 444 | return; 445 | } 446 | 447 | Transform transform = ((Component) target).transform; 448 | Dictionary fieldComponentDict = Collector.CollectComponentFields(transform, m_FieldNamePrefix.stringValue, m_FieldNameByType.boolValue, m_Settings.ComponentMapDict); 449 | foreach (var pair in fieldComponentDict) 450 | { 451 | CollectComponentField(pair.Key, pair.Value); 452 | } 453 | 454 | // 正序检查后面重复的字段索引。 455 | List adjectiveFieldIndex = new List(); 456 | List cacheFieldNames = new List(); 457 | for (int i = 0; i < m_FieldNames.arraySize; i++) 458 | { 459 | string fieldName = m_FieldNames.GetArrayElementAtIndex(i).stringValue; 460 | if (cacheFieldNames.Contains(fieldName)) 461 | { 462 | adjectiveFieldIndex.Add(i); 463 | continue; 464 | } 465 | cacheFieldNames.Add(fieldName); 466 | } 467 | cacheFieldNames.Clear(); 468 | 469 | // 倒序删除重复的字段索引。删除必须使用倒序,否则索引会错误 470 | for (int i = adjectiveFieldIndex.Count - 1; i >= 0; i--) 471 | { 472 | int adjectiveIndex = adjectiveFieldIndex[i]; 473 | SerializedArrayDeleteIndex(m_FieldNames, adjectiveIndex); 474 | SerializedArrayDeleteIndex(m_FieldComponents, adjectiveIndex); 475 | } 476 | adjectiveFieldIndex.Clear(); 477 | } 478 | 479 | /// 480 | /// 收集组件字段。 481 | /// 482 | /// 字段名。 483 | /// 组件引用。 484 | private void CollectComponentField(string fieldName, Component component) 485 | { 486 | int index = m_FieldNames.arraySize; 487 | m_FieldNames.InsertArrayElementAtIndex(index); 488 | m_FieldNames.GetArrayElementAtIndex(index).stringValue = fieldName; 489 | m_FieldComponents.InsertArrayElementAtIndex(index); 490 | m_FieldComponents.GetArrayElementAtIndex(index).objectReferenceValue = component; 491 | } 492 | 493 | /// 494 | /// 移除空组件。 495 | /// 496 | private void RemoveNullComponent() 497 | { 498 | for (int i = m_FieldNames.arraySize - 1; i >= 0; i--) 499 | { 500 | Component component = (Component) m_FieldComponents.GetArrayElementAtIndex(i).objectReferenceValue; 501 | if (component == null) 502 | { 503 | SerializedArrayDeleteIndex(m_FieldNames, i); 504 | SerializedArrayDeleteIndex(m_FieldComponents, i); 505 | } 506 | } 507 | } 508 | 509 | /// 510 | /// 序列化数组移除索引元素。 511 | /// 512 | /// 数组的序列化属性。 513 | /// 要移除的索引。 514 | private void SerializedArrayDeleteIndex(SerializedProperty serializedProperty, int index) 515 | { 516 | int originLength = serializedProperty.arraySize; 517 | if (originLength <= index) 518 | { 519 | return; 520 | } 521 | 522 | // 序列化的引用类型,第一次删除时会将该引用置空,第二次才会将索引移除。所以在这里检查一下。 523 | // 序列化 string 类型会被认为是值类型,可以直接移除。 524 | serializedProperty.DeleteArrayElementAtIndex(index); 525 | if (originLength == serializedProperty.arraySize) 526 | { 527 | serializedProperty.DeleteArrayElementAtIndex(index); 528 | } 529 | } 530 | 531 | /// 532 | /// 同步序列化的 Components。 533 | /// 534 | private void SyncSerializeComponents() 535 | { 536 | m_Components.ClearArray(); 537 | for (int i = 0; i < m_FieldComponents.arraySize; i++) 538 | { 539 | m_Components.InsertArrayElementAtIndex(i); 540 | m_Components.GetArrayElementAtIndex(i).objectReferenceValue = m_FieldComponents.GetArrayElementAtIndex(i).objectReferenceValue; 541 | } 542 | } 543 | 544 | #endregion 545 | 546 | #region Generate 547 | 548 | /// 549 | /// 检查生成文件的条件是否满足。 550 | /// 551 | /// 552 | private bool CheckGenerateCondition() 553 | { 554 | string savePath = m_CodeSavePath.stringValue; 555 | if (string.IsNullOrEmpty(savePath)) 556 | { 557 | Debug.LogError("Code save path is invalid."); 558 | return false; 559 | } 560 | 561 | if (!Directory.Exists(savePath)) 562 | { 563 | Directory.CreateDirectory(savePath); 564 | } 565 | 566 | string nameSpace = m_NameSpace.stringValue; 567 | if (string.IsNullOrEmpty(nameSpace) || !m_Settings.m_DefaultNameRegex.IsMatch(nameSpace)) 568 | { 569 | Debug.LogErrorFormat("NameSpace '{0}' is invalid.", nameSpace); 570 | return false; 571 | } 572 | 573 | string className = m_ClassName.stringValue; 574 | if (string.IsNullOrEmpty(className) || !m_Settings.m_DefaultNameRegex.IsMatch(className)) 575 | { 576 | Debug.LogErrorFormat("Class name '{0}' is invalid.", className); 577 | return false; 578 | } 579 | 580 | return true; 581 | } 582 | 583 | /// 584 | /// 检查文件是否可以生成。 585 | /// 586 | /// 文件路径 587 | /// 588 | private bool CheckGenerateFile(string filePath) 589 | { 590 | if (File.Exists(filePath)) 591 | { 592 | return EditorUtility.DisplayDialog("File Exits", " File already exits, continue regenerate ?", "Continue", "Cancel"); 593 | } 594 | return true; 595 | } 596 | 597 | /// 598 | /// 生成 Components 代码。 599 | /// 600 | private void GenerateComponentsCode() 601 | { 602 | if (Generator == null) 603 | { 604 | Debug.LogError("Generator is null."); 605 | return; 606 | } 607 | 608 | if (!CheckGenerateCondition()) 609 | { 610 | return; 611 | } 612 | 613 | if (m_Settings.m_ComponentsCodeTemplate == null) 614 | { 615 | Debug.LogError("ComponentsCodeTemplate is null, Please check 'ComponentCollectionSettings' asset."); 616 | return; 617 | } 618 | 619 | string codeFileName = string.Format("{0}/{1}.Components.cs", m_CodeSavePath.stringValue, m_ClassName.stringValue); 620 | if (!CheckGenerateFile(codeFileName)) 621 | { 622 | return; 623 | } 624 | 625 | RemoveNullComponent(); 626 | SyncSerializeComponents(); 627 | 628 | Dictionary fieldTypeDict = new Dictionary(); 629 | for (int i = 0; i < m_FieldNames.arraySize; i++) 630 | { 631 | string fieldName = m_FieldNames.GetArrayElementAtIndex(i).stringValue; 632 | if (string.IsNullOrEmpty(fieldName) || !m_Settings.m_FieldNameRegex.IsMatch(fieldName)) 633 | { 634 | Debug.LogErrorFormat("Field name '{0}' is invalid.", fieldName); 635 | continue; 636 | } 637 | 638 | string componentTypeName = m_FieldComponents.GetArrayElementAtIndex(i).objectReferenceValue.GetType().Name; 639 | fieldTypeDict.Add(fieldName, componentTypeName); 640 | } 641 | 642 | Generator.GenerateComponentsCode(codeFileName, m_Settings.m_ComponentsCodeTemplate.text, m_NameSpace.stringValue, m_ClassName.stringValue, fieldTypeDict); 643 | AssetDatabase.SaveAssets(); 644 | AssetDatabase.Refresh(); 645 | } 646 | 647 | /// 648 | /// 生成 MonoBehaviour 代码。 649 | /// 650 | private void GenerateBehaviourCode() 651 | { 652 | if (Generator == null) 653 | { 654 | Debug.LogError("Generator is null."); 655 | return; 656 | } 657 | 658 | if (!CheckGenerateCondition()) 659 | { 660 | return; 661 | } 662 | 663 | if (m_Settings.m_BehaviourCodeTemplate == null) 664 | { 665 | Debug.LogError("BehaviourCodeTemplate is null, Please check 'ComponentCollectionSettings' asset."); 666 | return; 667 | } 668 | 669 | string codeFileName = string.Format("{0}/{1}.cs", m_CodeSavePath.stringValue, m_ClassName.stringValue); 670 | if (!CheckGenerateFile(codeFileName)) 671 | { 672 | return; 673 | } 674 | 675 | Generator.GenerateBehaviourCode(codeFileName, m_Settings.m_BehaviourCodeTemplate.text, m_NameSpace.stringValue, m_ClassName.stringValue); 676 | AssetDatabase.SaveAssets(); 677 | AssetDatabase.Refresh(); 678 | } 679 | 680 | /// 681 | /// 自动添加组件。 682 | /// 683 | private void AddBehaviourComponent() 684 | { 685 | string typeName = string.Format("{0}.{1}", m_NameSpace.stringValue, m_ClassName.stringValue); 686 | Type componentType = TypeUtility.GetRuntimeType(typeName); 687 | if (componentType == null) 688 | { 689 | Debug.LogWarningFormat("Can't load type '{0}'. Please check whether 'TypeUtility.RuntimeAssemblyNames' contains the assembly name of this type? ", typeName); 690 | return; 691 | } 692 | ((Component) target).gameObject.AddComponent(componentType); 693 | } 694 | 695 | #endregion 696 | } 697 | } 698 | -------------------------------------------------------------------------------- /Scripts/Editor/UGUIExtension/UGUICreator.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.UI; 3 | 4 | // copy from DefaultControls of https://github.com/Unity-Technologies/uGUI.git 5 | namespace GameExtension.Editor 6 | { 7 | /// 8 | /// Utility class for creating default implementations of builtin UI controls. 9 | /// 10 | /// 11 | /// The recommended workflow for using UI controls with the UI system is to create a prefab for each type of control and instantiate those when needed. This way changes can be made to the prefabs which immediately have effect on all used instances. 12 | /// 13 | /// However, in certain cases there can be reasons to create UI controls entirely from code. The DefaultControls class provide methods to create each of the builtin UI controls. The resulting objects are the same as are obtained from using the corresponding UI menu entries in the GameObject menu in the Editor. 14 | /// 15 | /// An example use of this is creating menu items for custom new UI controls that mimics the ones that are builtin in Unity. Some such UI controls may contain other UI controls. For example, a scroll view contains scrollbars.By using the DefaultControls methods to create those parts, it is ensured that they are identical in look and setup to the ones provided in the menu items builtin with Unity. 16 | /// 17 | /// Note that the details of the setup of the UI controls created by the methods in this class may change with later revisions of the UI system.As such, they are not guaranteed to be 100% backwards compatible. It is recommended not to rely on the specific hierarchies of the GameObjects created by these methods, and limit your code to only interface with the root GameObject created by each method. 18 | /// 19 | public static class UGUICreator 20 | { 21 | /// 22 | /// Object used to pass resources to use for the default controls. 23 | /// 24 | public struct Resources 25 | { 26 | /// 27 | /// The primary sprite to be used for graphical UI elements, used by the button, toggle, and dropdown controls, among others. 28 | /// 29 | public Sprite standard; 30 | 31 | /// 32 | /// Sprite used for background elements. 33 | /// 34 | public Sprite background; 35 | 36 | /// 37 | /// Sprite used as background for input fields. 38 | /// 39 | public Sprite inputField; 40 | 41 | /// 42 | /// Sprite used for knobs that can be dragged, such as on a slider. 43 | /// 44 | public Sprite knob; 45 | 46 | /// 47 | /// Sprite used for representation of an "on" state when present, such as a checkmark. 48 | /// 49 | public Sprite checkmark; 50 | 51 | /// 52 | /// Sprite used to indicate that a button will open a dropdown when clicked. 53 | /// 54 | public Sprite dropdown; 55 | 56 | /// 57 | /// Sprite used for masking purposes, for example to be used for the viewport of a scroll view. 58 | /// 59 | public Sprite mask; 60 | } 61 | 62 | private const float kWidth = 160f; 63 | private const float kThickHeight = 30f; 64 | private const float kThinHeight = 20f; 65 | private static Vector2 s_ThickElementSize = new Vector2(kWidth, kThickHeight); 66 | private static Vector2 s_ThinElementSize = new Vector2(kWidth, kThinHeight); 67 | private static Vector2 s_ImageElementSize = new Vector2(100f, 100f); 68 | private static Vector2 s_GroupElementSize = new Vector2(200f, 200f); 69 | private static Color s_DefaultSelectableColor = new Color(1f, 1f, 1f, 1f); 70 | private static Color s_PanelColor = new Color(1f, 1f, 1f, 0.392f); 71 | private static Color s_TextColor = new Color(50f / 255f, 50f / 255f, 50f / 255f, 1f); 72 | 73 | // Helper methods at top 74 | 75 | private static GameObject CreateUIElementRoot(string name, Vector2 size) 76 | { 77 | GameObject child = new GameObject(name); 78 | RectTransform rectTransform = child.AddComponent(); 79 | rectTransform.sizeDelta = size; 80 | return child; 81 | } 82 | 83 | static GameObject CreateUIObject(string name, GameObject parent) 84 | { 85 | GameObject go = new GameObject(name); 86 | go.AddComponent(); 87 | SetParentAndAlign(go, parent); 88 | return go; 89 | } 90 | 91 | private static void SetDefaultTextValues(Text text) 92 | { 93 | // Set text values we want across UI elements in default controls. 94 | // Don't set values which are the same as the default values for the Text component, 95 | // since there's no point in that, and it's good to keep them as consistent as possible. 96 | text.color = s_TextColor; 97 | 98 | // Reset() is not called when playing. We still want the default font to be assigned 99 | text.font = UnityEngine.Resources.GetBuiltinResource("Arial.ttf"); 100 | } 101 | 102 | private static void SetDefaultColorTransitionValues(Selectable selectable) 103 | { 104 | ColorBlock colors = selectable.colors; 105 | colors.highlightedColor = new Color(0.882f, 0.882f, 0.882f); 106 | colors.pressedColor = new Color(0.698f, 0.698f, 0.698f); 107 | colors.disabledColor = new Color(0.521f, 0.521f, 0.521f); 108 | } 109 | 110 | private static void SetParentAndAlign(GameObject child, GameObject parent) 111 | { 112 | if (parent == null) 113 | return; 114 | 115 | child.transform.SetParent(parent.transform, false); 116 | SetLayerRecursively(child, parent.layer); 117 | } 118 | 119 | private static void SetLayerRecursively(GameObject go, int layer) 120 | { 121 | go.layer = layer; 122 | Transform t = go.transform; 123 | for (int i = 0; i < t.childCount; i++) 124 | SetLayerRecursively(t.GetChild(i).gameObject, layer); 125 | } 126 | 127 | /// 128 | /// Create the basic UI Panel. 129 | /// 130 | /// 131 | /// Hierarchy: 132 | /// (root) 133 | /// Image 134 | /// 135 | /// The resources to use for creation. 136 | /// The root GameObject of the created element. 137 | public static GameObject CreatePanel(Resources resources) 138 | { 139 | GameObject panelRoot = CreateUIElementRoot("Panel", s_GroupElementSize); 140 | 141 | // Set RectTransform to stretch 142 | RectTransform rectTransform = panelRoot.GetComponent(); 143 | rectTransform.anchorMin = Vector2.zero; 144 | rectTransform.anchorMax = Vector2.one; 145 | rectTransform.anchoredPosition = Vector2.zero; 146 | rectTransform.sizeDelta = Vector2.zero; 147 | 148 | Image image = panelRoot.AddComponent(); 149 | image.sprite = resources.background; 150 | image.type = Image.Type.Sliced; 151 | image.color = s_PanelColor; 152 | 153 | return panelRoot; 154 | } 155 | 156 | /// 157 | /// Create the basic UI Text. 158 | /// 159 | /// 160 | /// Hierarchy: 161 | /// (root) 162 | /// Text 163 | /// 164 | /// Is it as raycast target. 165 | /// The root GameObject of the created element. 166 | public static GameObject CreateText(bool raycastTarget) 167 | { 168 | GameObject go = CreateUIElementRoot("Text", s_ThickElementSize); 169 | 170 | Text lbl = go.AddComponent(); 171 | lbl.text = "New Text"; 172 | lbl.supportRichText = false; 173 | lbl.raycastTarget = raycastTarget; 174 | SetDefaultTextValues(lbl); 175 | 176 | return go; 177 | } 178 | 179 | /// 180 | /// Create the basic UI Image. 181 | /// 182 | /// 183 | /// Hierarchy: 184 | /// (root) 185 | /// Image 186 | /// 187 | /// Is it as raycast target. 188 | /// The root GameObject of the created element. 189 | public static GameObject CreateImage(bool raycastTarget) 190 | { 191 | GameObject go = CreateUIElementRoot("Image", s_ImageElementSize); 192 | Image image = go.AddComponent(); 193 | image.raycastTarget = raycastTarget; 194 | return go; 195 | } 196 | 197 | /// 198 | /// Create the basic UI RawImage. 199 | /// 200 | /// 201 | /// Hierarchy: 202 | /// (root) 203 | /// RawImage 204 | /// 205 | /// Is it as raycast target. 206 | /// The root GameObject of the created element. 207 | public static GameObject CreateRawImage(bool raycastTarget) 208 | { 209 | GameObject go = CreateUIElementRoot("RawImage", s_ImageElementSize); 210 | RawImage rawImage = go.AddComponent(); 211 | rawImage.raycastTarget = raycastTarget; 212 | return go; 213 | } 214 | 215 | /// 216 | /// Create the RectRaycast2D. 217 | /// 218 | /// 219 | /// Hierarchy: 220 | /// (root) 221 | /// RectRaycast 222 | /// 223 | /// The root GameObject of the created element. 224 | public static GameObject CreateRectRaycast() 225 | { 226 | GameObject go = CreateUIElementRoot("RectRaycast", s_ImageElementSize); 227 | go.AddComponent(); 228 | return go; 229 | } 230 | 231 | /// 232 | /// Create the RectRaycast2D Button. 233 | /// 234 | /// 235 | /// Hierarchy: 236 | /// (root) 237 | /// Button 238 | /// 239 | /// The root GameObject of the created element. 240 | public static GameObject CreateRectButton() 241 | { 242 | GameObject buttonRoot = CreateUIElementRoot("Button", s_ThickElementSize); 243 | 244 | buttonRoot.AddComponent(); 245 | 246 | Button bt = buttonRoot.AddComponent