text:
fileFormatVersion: 2
guid: b2c72d3061154917b9c3309efd8b8c9f
timeCreated: 1569324137


text:
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;

namespace ReadyGamerOne.EditorExtension
{
    #if UNITY_EDITOR
    /// <summary>
    /// Display a Scene Reference object in the editor.
    /// If scene is valid, provides basic buttons to interact with the scene's role in Build Settings.
    /// </summary>
    [CustomPropertyDrawer(typeof(SceneField))]
    public class SceneReferencePropertyDrawer : PropertyDrawer
    {
        // The exact name of the asset Object variable in the SceneField object
        const string sceneAssetPropertyString = "sceneAsset";
        // The exact name of  the scene Path variable in the SceneField object
        const string scenePathPropertyString = "scenePath";

        static readonly RectOffset boxPadding = EditorStyles.helpBox.padding;
        static readonly float padSize = 2f;
        static readonly float lineHeight = EditorGUIUtility.singleLineHeight;
        static readonly float paddedLine = lineHeight + padSize;
        static readonly float footerHeight = 10f;

        /// <summary>
        /// Drawing the 'SceneField' property
        /// </summary>
        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            var sceneAssetProperty = GetSceneAssetProperty(property);

            // Draw the Box Background
            position.height -= footerHeight;
            GUI.Box(EditorGUI.IndentedRect(position), GUIContent.none, EditorStyles.helpBox);
            position = boxPadding.Remove(position);
            position.height = lineHeight;

            // Draw the main Object field
            label.tooltip = "The actual Scene Asset reference.\nOn serialize this is also stored as the asset's path.";

            EditorGUI.BeginProperty(position, GUIContent.none, property);
            EditorGUI.BeginChangeCheck();
            int sceneControlID = GUIUtility.GetControlID(FocusType.Passive);
            var selectedObject = EditorGUI.ObjectField(position, label, sceneAssetProperty.objectReferenceValue, typeof(SceneAsset), false);
            BuildUtils.BuildScene buildScene = BuildUtils.GetBuildScene(selectedObject);

            if (EditorGUI.EndChangeCheck())
            {
                sceneAssetProperty.objectReferenceValue = selectedObject;

                // If no valid scene asset was selected, reset the stored path accordingly
                if (buildScene.scene == null)
                    GetScenePathProperty(property).stringValue = string.Empty;
            }
            position.y += paddedLine;

            if (buildScene.assetGUID.Empty() == false)
            {
                // Draw the Build Settings Info of the selected Scene
                DrawSceneInfoGUI(position, buildScene, sceneControlID + 1);
            }

            EditorGUI.EndProperty();
        }

        /// <summary>
        /// Ensure that what we draw in OnGUI always has the room it needs
        /// </summary>
        public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
        {
            int lines = 2;
            SerializedProperty sceneAssetProperty = GetSceneAssetProperty(property);
            if (sceneAssetProperty.objectReferenceValue == null)
                lines = 1;

            return boxPadding.vertical + lineHeight * lines + padSize * (lines - 1) + footerHeight;
        }

        /// <summary>
        /// Draws info box of the provided scene
        /// </summary>
        private void DrawSceneInfoGUI(Rect position, BuildUtils.BuildScene buildScene, int sceneControlID)
        {
            bool readOnly = BuildUtils.IsReadOnly();
            string readOnlyWarning = readOnly ? "\n\nWARNING: Build Settings is not checked out and so cannot be modified." : "";

            // Label Prefix
            GUIContent iconContent = new GUIContent();
            GUIContent labelContent = new GUIContent();

            // Missing from build scenes
            if (buildScene.buildIndex == -1)
            {
                iconContent = EditorGUIUtility.IconContent("d_winbtn_mac_close");
                labelContent.text = "NOT In Build";
                labelContent.tooltip = "This scene is NOT in build settings.\nIt will be NOT included in builds.";
            }
            // In build scenes and enabled
            else if (buildScene.scene.enabled)
            {
                iconContent = EditorGUIUtility.IconContent("d_winbtn_mac_max");
                labelContent.text = "BuildIndex: " + buildScene.buildIndex;
                labelContent.tooltip = "This scene is in build settings and ENABLED.\nIt will be included in builds." + readOnlyWarning;
            }
            // In build scenes and disabled
            else
            {
                iconContent = EditorGUIUtility.IconContent("d_winbtn_mac_min");
                labelContent.text = "BuildIndex: " + buildScene.buildIndex;
                labelContent.tooltip = "This scene is in build settings and DISABLED.\nIt will be NOT included in builds.";
            }

            // Left status label
            using (new EditorGUI.DisabledScope(readOnly))
            {
                Rect labelRect = DrawUtils.GetLabelRect(position);
                Rect iconRect = labelRect;
                iconRect.width = iconContent.image.width + padSize;
                labelRect.width -= iconRect.width;
                labelRect.x += iconRect.width;
                EditorGUI.PrefixLabel(iconRect, sceneControlID, iconContent);
                EditorGUI.PrefixLabel(labelRect, sceneControlID, labelContent);
            }

            // Right context buttons
            Rect buttonRect = DrawUtils.GetFieldRect(position);
            buttonRect.width = (buttonRect.width) / 3;

            string tooltipMsg = "";
            using (new EditorGUI.DisabledScope(readOnly))
            {
                // NOT in build settings
                if (buildScene.buildIndex == -1)
                {
                    buttonRect.width *= 2;
                    int addIndex = EditorBuildSettings.scenes.Length;
                    tooltipMsg = "Add this scene to build settings. It will be appended to the end of the build scenes as buildIndex: " + addIndex + "." + readOnlyWarning;
                    if (DrawUtils.ButtonHelper(buttonRect, "Add...", "Add (buildIndex " + addIndex + ")", EditorStyles.miniButtonLeft, tooltipMsg))
                        BuildUtils.AddBuildScene(buildScene);
                    buttonRect.width /= 2;
                    buttonRect.x += buttonRect.width;
                }
                // In build settings
                else
                {
                    bool isEnabled = buildScene.scene.enabled;
                    string stateString = isEnabled ? "Disable" : "Enable";
                    tooltipMsg = stateString + " this scene in build settings.\n" + (isEnabled ? "It will no longer be included in builds" : "It will be included in builds") + "." + readOnlyWarning;

                    if (DrawUtils.ButtonHelper(buttonRect, stateString, stateString + " In Build", EditorStyles.miniButtonLeft, tooltipMsg))
                        BuildUtils.SetBuildSceneState(buildScene, !isEnabled);
                    buttonRect.x += buttonRect.width;

                    tooltipMsg = "Completely remove this scene from build settings.\nYou will need to add it again for it to be included in builds!" + readOnlyWarning;
                    if (DrawUtils.ButtonHelper(buttonRect, "Remove...", "Remove from Build", EditorStyles.miniButtonMid, tooltipMsg))
                        BuildUtils.RemoveBuildScene(buildScene);
                }
            }

            buttonRect.x += buttonRect.width;

            tooltipMsg = "Open the 'Build Settings' Window for managing scenes." + readOnlyWarning;
            if (DrawUtils.ButtonHelper(buttonRect, "Settings", "Build Settings", EditorStyles.miniButtonRight, tooltipMsg))
            {
                BuildUtils.OpenBuildSettings();
            }

        }

        static SerializedProperty GetSceneAssetProperty(SerializedProperty property)
        {
            return property.FindPropertyRelative(sceneAssetPropertyString);
        }

        static SerializedProperty GetScenePathProperty(SerializedProperty property)
        {
            return property.FindPropertyRelative(scenePathPropertyString);
        }

        private static class DrawUtils
        {
            /// <summary>
            /// Draw a GUI button, choosing between a short and a long button text based on if it fits
            /// </summary>
            static public bool ButtonHelper(Rect position, string msgShort, string msgLong, GUIStyle style, string tooltip = null)
            {
                GUIContent content = new GUIContent(msgLong);
                content.tooltip = tooltip;

                float longWidth = style.CalcSize(content).x;
                if (longWidth > position.width)
                    content.text = msgShort;

                return GUI.Button(position, content, style);
            }

            /// <summary>
            /// Given a position rect, get its field portion
            /// </summary>
            static public Rect GetFieldRect(Rect position)
            {
                position.width -= EditorGUIUtility.labelWidth;
                position.x += EditorGUIUtility.labelWidth;
                return position;
            }
            /// <summary>
            /// Given a position rect, get its label portion
            /// </summary>
            static public Rect GetLabelRect(Rect position)
            {
                position.width = EditorGUIUtility.labelWidth - padSize;
                return position;
            }
        }

        /// <summary>
        /// Various BuildSettings interactions
        /// </summary>
        static private class BuildUtils
        {
            // time in seconds that we have to wait before we query again when IsReadOnly() is called.
            public static float minCheckWait = 3;

            static float lastTimeChecked = 0;
            static bool cachedReadonlyVal = true;

            /// <summary>
            /// A small container for tracking scene data BuildSettings
            /// </summary>
            public struct BuildScene
            {
                public int buildIndex;
                public GUID assetGUID;
                public string assetPath;
                public EditorBuildSettingsScene scene;
            }

            /// <summary>
            /// Check if the build settings asset is readonly.
            /// Caches value and only queries state a max of every 'minCheckWait' seconds.
            /// </summary>
            static public bool IsReadOnly()
            {
                float curTime = Time.realtimeSinceStartup;
                float timeSinceLastCheck = curTime - lastTimeChecked;

                if (timeSinceLastCheck > minCheckWait)
                {
                    lastTimeChecked = curTime;
                    cachedReadonlyVal = QueryBuildSettingsStatus();
                }

                return cachedReadonlyVal;
            }

            /// <summary>
            /// A blocking call to the Version Control system to see if the build settings asset is readonly.
            /// Use BuildSettingsIsReadOnly for version that caches the value for better responsivenes.
            /// </summary>
            static private bool QueryBuildSettingsStatus()
            {
                // If no version control provider, assume not readonly
                if (UnityEditor.VersionControl.Provider.enabled == false)
                    return false;

                // If we cannot checkout, then assume we are not readonly
                if (UnityEditor.VersionControl.Provider.hasCheckoutSupport == false)
                    return false;

                //// If offline (and are using a version control provider that requires checkout) we cannot edit.
                //if (UnityEditor.VersionControl.Provider.onlineState == UnityEditor.VersionControl.OnlineState.Offline)
                //    return true;

                // Try to get status for file
                var status = UnityEditor.VersionControl.Provider.Status("ProjectSettings/EditorBuildSettings.asset", false);
                status.Wait();

                // If no status listed we can edit
                if (status.assetList == null || status.assetList.Count != 1)
                    return true;

                // If is checked out, we can edit
                if (status.assetList[0].IsState(UnityEditor.VersionControl.Asset.States.CheckedOutLocal))
                    return false;

                return true;
            }

            /// <summary>
            /// For a given Scene Asset object reference, extract its build settings data, including buildIndex.
            /// </summary>
            static public BuildScene GetBuildScene(Object sceneObject)
            {
                BuildScene entry = new BuildScene()
                {
                    buildIndex = -1,
                    assetGUID = new GUID(string.Empty)
                };

                if (sceneObject as SceneAsset == null)
                    return entry;

                entry.assetPath = AssetDatabase.GetAssetPath(sceneObject);
                entry.assetGUID = new GUID(AssetDatabase.AssetPathToGUID(entry.assetPath));

                for (int index = 0; index < EditorBuildSettings.scenes.Length; ++index)
                {
                    if (entry.assetGUID.Equals(EditorBuildSettings.scenes[index].guid))
                    {
                        entry.scene = EditorBuildSettings.scenes[index];
                        entry.buildIndex = index;
                        return entry;
                    }
                }

                return entry;
            }

            /// <summary>
            /// Enable/Disable a given scene in the buildSettings
            /// </summary>
            static public void SetBuildSceneState(BuildScene buildScene, bool enabled)
            {
                bool modified = false;
                EditorBuildSettingsScene[] scenesToModify = EditorBuildSettings.scenes;
                foreach (var curScene in scenesToModify)
                {
                    if (curScene.guid.Equals(buildScene.assetGUID))
                    {
                        curScene.enabled = enabled;
                        modified = true;
                        break;
                    }
                }
                if (modified)
                    EditorBuildSettings.scenes = scenesToModify;
            }

            /// <summary>
            /// Display Dialog to add a scene to build settings
            /// </summary>
            static public void AddBuildScene(BuildScene buildScene, bool force = false, bool enabled = true)
            {
                if (force == false)
                {
                    int selection = EditorUtility.DisplayDialogComplex(
                        "Add Scene To Build",
                        "You are about to add scene at " + buildScene.assetPath + " To the Build Settings.",
                        "Add as Enabled",       // option 0
                        "Add as Disabled",      // option 1
                        "Cancel (do nothing)"); // option 2

                    switch (selection)
                    {
                        case 0: // enabled
                            enabled = true;
                            break;
                        case 1: // disabled
                            enabled = false;
                            break;
                        default:
                        case 2: // cancel
                            return;
                    }
                }

                EditorBuildSettingsScene newScene = new EditorBuildSettingsScene(buildScene.assetGUID, enabled);
                List<EditorBuildSettingsScene> tempScenes = EditorBuildSettings.scenes.ToList();
                tempScenes.Add(newScene);
                EditorBuildSettings.scenes = tempScenes.ToArray();
            }

            /// <summary>
            /// Display Dialog to remove a scene from build settings (or just disable it)
            /// </summary>
            static public void RemoveBuildScene(BuildScene buildScene, bool force = false)
            {
                bool onlyDisable = false;
                if (force == false)
                {
                    int selection = -1;

                    string title = "Remove Scene From Build";
                    string details = string.Format("You are about to remove the following scene from build settings:\n    {0}\n    buildIndex: {1}\n\n{2}",
                                    buildScene.assetPath, buildScene.buildIndex,
                                    "This will modify build settings, but the scene asset will remain untouched.");
                    string confirm = "Remove From Build";
                    string alt = "Just Disable";
                    string cancel = "Cancel (do nothing)";

                    if (buildScene.scene.enabled)
                    {
                        details += "\n\nIf you want, you can also just disable it instead.";
                        selection = EditorUtility.DisplayDialogComplex(title, details, confirm, alt, cancel);
                    }
                    else
                    {
                        selection = EditorUtility.DisplayDialog(title, details, confirm, cancel) ? 0 : 2;
                    }

                    switch (selection)
                    {
                        case 0: // remove
                            break;
                        case 1: // disable
                            onlyDisable = true;
                            break;
                        default:
                        case 2: // cancel
                            return;
                    }
                }

                // User chose to not remove, only disable the scene
                if (onlyDisable)
                {
                    SetBuildSceneState(buildScene, false);
                }
                // User chose to fully remove the scene from build settings
                else
                {
                    List<EditorBuildSettingsScene> tempScenes = EditorBuildSettings.scenes.ToList();
                    tempScenes.RemoveAll(scene => scene.guid.Equals(buildScene.assetGUID));
                    EditorBuildSettings.scenes = tempScenes.ToArray();
                }
            }

            /// <summary>
            /// Open the default Unity Build Settings window
            /// </summary>
            static public void OpenBuildSettings()
            {
                EditorWindow.GetWindow(typeof(BuildPlayerWindow));
            }
        }
    }

    #endif
}

mime_type: text/plain


text:
fileFormatVersion: 2
guid: 1a180bf9546e488095b4c795ed081eed
timeCreated: 1588751238


text:
fileFormatVersion: 2
guid: 67458eb96c888d54aa85d641bfcd92df
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:


text:
fileFormatVersion: 2
guid: 19b2ec36cf904b13997e9f80f2cfa752
timeCreated: 1564197422


text:
fileFormatVersion: 2
guid: 8acba67f3b8e4b2ab5cb064bc1fe822b
timeCreated: 1564151798


text:
fileFormatVersion: 2
guid: 39e309657acb4ccc961735ecde74a539
timeCreated: 1569417523


text:
using System.Collections.Generic;
using ReadyGamerOne.Utility;
using UnityEditor;
using UnityEngine;

namespace ReadyGamerOne.EditorExtension
{
    [CustomPropertyDrawer(typeof(TransformPathChooser))]
    public class TransformPathChooserDrawer:PropertyDrawer
    {
        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            var indexProp = property.FindPropertyRelative("selectedIndex");
            var goProp = property.FindPropertyRelative("go");
            var obj = goProp.objectReferenceValue as GameObject;
            var nowPath = property.FindPropertyRelative("path");
            var paths = new List<string>();

            EditorGUI.LabelField(position.GetLeft(TransformPathChooser.LabelWidth), property.displayName);
            goProp.objectReferenceValue = EditorGUI.ObjectField(
                position.GetLeft(TransformPathChooser.LabelWidth + TransformPathChooser.ObjectFidldWidth,
                    TransformPathChooser.LabelWidth + 0.015f), goProp.objectReferenceValue, typeof(GameObject), false);
            var enumWidth = 1 - TransformPathChooser.LabelWidth - TransformPathChooser.ObjectFidldWidth;

            if (obj == null)
            {
                EditorGUI.HelpBox(position.GetRight(enumWidth), "请选择预制体", MessageType.Error);
                return;
            }

            Search(obj.transform, paths, "");

            if (paths.Count == 0)
            {
                EditorGUI.HelpBox(position.GetRight(enumWidth), "此物体没有子物体", MessageType.Error);
                return;
            }

            if (indexProp.intValue >= paths.Count)
            {
                Debug.LogError("路径索引越界，路径已经重置，请重新选择路径" + property.serializedObject.targetObject.name);
                indexProp.intValue = 0;
            }

            EditorGUI.BeginChangeCheck();

            indexProp.intValue = EditorGUI.Popup(position.GetRight(enumWidth), indexProp.intValue, paths.ToArray());

            var changed = EditorGUI.EndChangeCheck();
            if (string.IsNullOrEmpty(nowPath.stringValue))
                nowPath.stringValue = paths[indexProp.intValue];
            else
            {
                if (nowPath.stringValue != paths[indexProp.intValue])
                {
                    if (changed)
                    {
                        //主动修改
                        nowPath.stringValue = paths[indexProp.intValue];
                    }
                    else
                    {
                        //如果原先路径和当前路径不同
                        var currentName = nowPath.stringValue;
                        if (currentName.Contains("/"))
                        {
                            var select = currentName.LastIndexOf('/') + 1;
                            currentName = currentName.Substring(select, currentName.Length - select);
                        }

                        var index = 0;
                        foreach (var VARIABLE in paths)
                        {
                            //找到新路径
                            if (VARIABLE.EndsWith(currentName))
                            {
                                indexProp.intValue = index;
                                nowPath.stringValue = VARIABLE;
                                Debug.Log("重新找到！");
                                return;
                            }

                            index++;
                        }

                        Debug.LogError("路径丢失，没有找到！错误路径：" + nowPath.stringValue);
                    }

                }
            }
        }

        private void Search(Transform parent, List<string> paths,string nowPath)
        {
            for (var i = 0; i < parent.childCount; i++)
            {
                var child = parent.GetChild(i);
                if (string.IsNullOrEmpty(nowPath))
                {
                    paths.Add(child.name);
                    Search(child,paths,child.name);
                }
                else
                {
                    paths.Add(nowPath + "/" + child.name);
                    Search(child, paths, nowPath + "/" + child.name);
                }
            }
        }

    }
}


text:
using ReadyGamerOne.Utility;
using UnityEditor;
using UnityEngine;

namespace ReadyGamerOne.EditorExtension
{
    [CustomPropertyDrawer(typeof(AnimationNameChooser))]
    public class AnimationNameChooserDrawer:PropertyDrawer
    {
        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            EditorGUI.LabelField(position.GetLeft(AnimationNameChooser.LabelWidth), property.displayName);

            var racProp = property.FindPropertyRelative("rac");
            racProp.objectReferenceValue= EditorGUI.ObjectField(
                position.GetLeft(AnimationNameChooser.LabelWidth + AnimationNameChooser.ObjectFieldWidth,
                    AnimationNameChooser.LabelWidth+0.015f), racProp.objectReferenceValue, typeof(RuntimeAnimatorController),
                false);

            var enumWidth = 1 - AnimationNameChooser.LabelWidth - AnimationNameChooser.ObjectFieldWidth;
            var rac = racProp.objectReferenceValue as RuntimeAnimatorController;

            if (rac == null)
            {
                EditorGUI.HelpBox(position.GetRight(enumWidth), "请选择状态机", MessageType.Error);
                return;
            }

            var names = new string[rac.animationClips.Length];
            for (var i = 0; i < rac.animationClips.Length; i++)
                names[i] = rac.animationClips[i].name;
            var indexProp = property.FindPropertyRelative("selectedIndex");
            indexProp.intValue = EditorGUI.Popup(position.GetRight(enumWidth), indexProp.intValue, names);

            property.FindPropertyRelative("stringValue").stringValue = names[indexProp.intValue];

        }
    }
}


text:
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;

namespace ReadyGamerOne.EditorExtension
{
    [CustomPropertyDrawer(typeof(ResourcesPathChooser))]
    public class ResourcesPathChooserDrawer:PropertyDrawer
    {
        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            var indexProp = property.FindPropertyRelative("selectedIndex");
            var nowPath = property.FindPropertyRelative("Path");
            var paths=new List<string>();

            var resPath = Application.dataPath + "/Resources";

            if (!Directory.Exists(resPath))
            {
                Directory.CreateDirectory(resPath);
                AssetDatabase.Refresh();
            }

            Search(resPath, paths);
            if (paths.Count==0)
            {
                EditorGUI.HelpBox(position, "现在Resources目录为空", MessageType.Error);
                return;
            }
            else if (indexProp.intValue >= paths.Count)
            {
                Debug.LogError("路径索引越界，路径已经重置，请重新选择路径" + property.serializedObject.targetObject.name);
                indexProp.intValue = 0;
            }
            EditorGUI.BeginChangeCheck();

            indexProp.intValue = EditorGUI.Popup(position,property.displayName, indexProp.intValue, paths.ToArray());

            var changed = EditorGUI.EndChangeCheck();
            if (string.IsNullOrEmpty(nowPath.stringValue))
                nowPath.stringValue = paths[indexProp.intValue];
            else
            {
                if (nowPath.stringValue != paths[indexProp.intValue])
                {
                    if (changed)
                    {
                        //主动修改
                        nowPath.stringValue = paths[indexProp.intValue];
                    }
                    else
                    {
                        //如果原先路径和当前路径不同
                        var currentName = nowPath.stringValue;
                        if (currentName.Contains("/"))
                        {
                            var select = currentName.LastIndexOf('/') + 1;
                            currentName = currentName.Substring(select, currentName.Length-select);
                        }

                        var index = 0;
                        foreach (var VARIABLE in paths)
                        {
                            //找到新路径
                            if (VARIABLE.EndsWith(currentName))
                            {
                                indexProp.intValue = index;
                                nowPath.stringValue = VARIABLE;
                                Debug.Log("重新找到！");
                                return;
                            }

                            index++;
                        }
                        Debug.LogError("路径丢失，没有找到！错误路径："+nowPath.stringValue);
                    }

                }
            }
        }

        private void Search(string path, List<string> paths,string pathBefore="")
        {
            var info = new DirectoryInfo(path);
            foreach (var VARIABLE in info.GetFileSystemInfos())
            {
                if (VARIABLE is DirectoryInfo)
                {
                    if(string.IsNullOrEmpty(pathBefore))
                        Search(path + "/" + VARIABLE.Name, paths, VARIABLE.Name);
                    else
                        Search(path + "/" + VARIABLE.Name, paths, pathBefore + "/" + VARIABLE.Name);
                }
                else
                {
                    if (VARIABLE.Name.EndsWith(".meta"))
                        continue;

                    var okName = VARIABLE.Name.Substring(0, VARIABLE.Name.LastIndexOf('.'));

                    if (string.IsNullOrEmpty(pathBefore))
                        paths.Add(okName);
                    else
                        paths.Add(pathBefore + "/" + okName);
                }
            }
        }
    }
}


text:
using ReadyGamerOne.Utility;
using UnityEditor;
using UnityEngine;

namespace ReadyGamerOne.EditorExtension
{
    [CustomPropertyDrawer(typeof(MessageTrigger))]
    public class MessagetTriggerDrawer:PropertyDrawer
    {
        public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
        {
//            var tick = property.FindPropertyRelative("tickMessage").boolValue;
//            int height = 1;
//            if (!tick)
//            {
//                return height * EditorGUIUtility.singleLineHeight;
//            }

            var height = 0;
            height+=2;
            var argCount = property.FindPropertyRelative("argCount").intValue;

            height += argCount;

            return height * EditorGUIUtility.singleLineHeight;

        }

        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            var index = 0;
            var argCount = property.FindPropertyRelative("argCount").intValue;

//            var tickMessageProp = property.FindPropertyRelative("tickMessage");
//            tickMessageProp.boolValue= EditorGUI.ToggleLeft(position.GetRectAtIndex(index++), tickMessageProp.displayName, tickMessageProp.boolValue);
//
//            if (!tickMessageProp.boolValue)
//                return;

            EditorGUI.PropertyField(position.GetRectAtIndex(index++), property.FindPropertyRelative("messageToTick"));

            argCount = EditorGUI.Popup(position.GetRectAtIndex(index++), "argCount",argCount,
                new[] {"0", "1", "2"});
            ArgChooser.typeChangAble = true;
            switch (argCount)
            {
                case 1:
                    EditorGUI.PropertyField(position.GetRectAtIndex(index++).GetRight(.9f),
                        property.FindPropertyRelative("arg1"));
                    break;
                case 2:
                    EditorGUI.PropertyField(position.GetRectAtIndex(index++).GetRight(.9f),
                        property.FindPropertyRelative("arg1"));
                    EditorGUI.PropertyField(position.GetRectAtIndex(index++).GetRight(.9f),
                        property.FindPropertyRelative("arg2"));
                    break;
            }

            property.FindPropertyRelative("argCount").intValue = argCount;

        }
    }
}


text:
using System.Collections.Generic;
using System.IO;
using ReadyGamerOne.Global;
#if UNITY_EDITOR
using UnityEditor;
using Directory = UnityEngine.Windows.Directory;
using FileUtil = ReadyGamerOne.Utility.FileUtil;
#endif
using UnityEngine;

namespace ReadyGamerOne.EditorExtension
{

//#pragma warning disable CS0414

    public class ClassGeneraterTool
#if UNITY_EDITOR
        : IEditorTools
#endif
    {
#if UNITY_EDITOR

#pragma warning disable 414
        static string Title = "自动类生成";
#pragma warning restore 414

        private static string generateDir;
        private static string sourDir;
        private static string nameSpace;
        private static bool HasParentClass = true;
        private static string parentClass;
        private static bool IsGenericParent = false;
        private static bool IsGenericParamSelf = true;
        private static string GenericParam;

        static void OnToolsGUI(string rootNs, string viewNs, string constNs, string dataNs, string autoDir,
            string scriptDir)
        {
            EditorGUILayout.LabelField("生成目录", generateDir);
            if (GUILayout.Button("设置生成目录"))
                generateDir = EditorUtility.OpenFolderPanel("选择生成目录", Application.dataPath, "");
            EditorGUILayout.Space();

            EditorGUILayout.LabelField("资源目录", sourDir);
            if (GUILayout.Button("设置资源目录"))
                sourDir = EditorUtility.OpenFolderPanel("选择资源目录", Application.dataPath, "");
            EditorGUILayout.Space();

            nameSpace = EditorGUILayout.TextField("命名空间", nameSpace);

            HasParentClass = EditorGUILayout.Toggle("是否具有父类", HasParentClass);
            if (HasParentClass)
            {
                parentClass = EditorGUILayout.TextField("父类名", parentClass);
                IsGenericParent = EditorGUILayout.Toggle("父类是否是泛型类", IsGenericParent);
                if (IsGenericParent)
                {
                    IsGenericParamSelf = EditorGUILayout.Toggle("泛型参数是不是本类名", IsGenericParamSelf);
                    if (!IsGenericParamSelf)
                        GenericParam = EditorGUILayout.TextField("泛型参数", GenericParam);
                }
            }

            EditorGUILayout.Space();
            if (GUILayout.Button("开始生成", GUILayout.Height(3 * EditorGUIUtility.singleLineHeight)))
            {
                if (!Directory.Exists(generateDir))
                {
                    Debug.LogError("生成目录不存在：" + generateDir);
                    return;
                }

                if (!Directory.Exists(sourDir))
                {
                    Debug.LogError("资源目录不存在：" + sourDir);
                    return;
                }

                if (string.IsNullOrEmpty(nameSpace))
                {
                    Debug.LogError("命名空间不能为空");
                    return;
                }

                if (HasParentClass)
                {
                    if (string.IsNullOrEmpty(parentClass))
                    {
                        Debug.LogError("父类不能为空");
                        return;
                    }
                    else
                    {
                        if (IsGenericParent && !IsGenericParamSelf && string.IsNullOrEmpty(GenericParam))
                        {
                            Debug.LogError("泛型参数不能为空");
                            return;
                        }
                    }
                }

                Create();

            }

        }

        private static void Create()
        {
            List<string> fileNames = new List<string>();

            FileUtil.SearchDirectory(sourDir, fileInfo =>
            {
                if (fileInfo.FullName.EndsWith(".meta"))
                    return;
                var pureName = Path.GetFileNameWithoutExtension(fileInfo.FullName);
                if (fileNames.Contains(pureName))
                {
                    Debug.LogError("发现同名文件:"+fileNames);
                    return;
                }
                fileNames.Add(pureName);
            },true);

            foreach (var file in fileNames)
            {
                string parent = null;
                if (HasParentClass)
                {
                    parent = parentClass;
                    if (IsGenericParent)
                    {
                        if (IsGenericParamSelf)
                            parent += "<" + file + ">";
                        else
                            parent += "<" + GenericParam + ">";
                    }
                }
                FileUtil.CreateClassFile(file,nameSpace,generateDir,parent,partical:true);
            }

            AssetDatabase.Refresh();

            Debug.Log("生成完成");
        }

#endif
    }
}


text:
fileFormatVersion: 2
guid: a4fcd84f52116014ba27aced2b520b30
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:


text:
using System;
using System.Collections.Generic;
using System.IO;
using FileUtil = ReadyGamerOne.Utility.FileUtil;
using ReadyGamerOne.Global;
using ReadyGamerOne.MemorySystem;
using ReadyGamerOne.Utility;
using ReadyGamerOne.View;
using UnityEditor;
using UnityEngine;

#if UNITY_EDITOR
#endif

namespace ReadyGamerOne.EditorExtension
{
//#pragma warning disable CS0414
    public class QuickStartTool
#if UNITY_EDITOR
        : IEditorTools
#endif
    {
        public static void RegisterUi(Type outType)
        {
            AbstractPanel.RegisterPanels(outType);
        }

#if UNITY_EDITOR
#pragma warning disable 414
        private static string Title = "快速启动工具";
#pragma warning restore 414
        private static List<string> autoClassName = new List<string>();
        private static Dictionary<string, string> otherResPathDic = new Dictionary<string, string>();
        private static Dictionary<string, string> otherResFileNameDic = new Dictionary<string, string>();

        private static Dictionary<string, string> allResPathDic = new Dictionary<string, string>();
        private static Dictionary<string, string> allResFileNameDic = new Dictionary<string, string>();

        private static bool createGameMgr = true;
        private static bool createPanelClasses = true;

        private static bool createGlobalVar = false;
        private static bool createMessage = false;
        private static bool createSceneNameClass = false;
        private static ResourceManagerType _resourceManagerType;

        static void OnToolsGUI(string rootNs, string viewNs, string constNs, string dataNs, string autoDir,
            string scriptNs)
        {
            EditorGUILayout.Space();
            EditorGUILayout.Space();

            createPanelClasses = EditorGUILayout.Toggle("是否自动生成Panel类型", createPanelClasses);
            if (createPanelClasses)
                EditorGUILayout.LabelField("自动生成Panel文件路径", Application.dataPath + "/" + rootNs + "/" + viewNs);
            EditorGUILayout.Space();

            createGameMgr = EditorGUILayout.Toggle("是否生成GameMgr", createGameMgr);
            if (createGameMgr)
            {
                _resourceManagerType = (ResourceManagerType) EditorGUILayout.EnumPopup("资源管理类型", _resourceManagerType);
                EditorGUILayout.LabelField("自动生成" + rootNs + "Mgr文件路径",
                    Application.dataPath + "/" + rootNs + "/" + scriptNs);

            }
            EditorGUILayout.Space();

            createMessage = EditorGUILayout.Toggle("是否生成空Message常量类", createMessage);
            if (createMessage)
                EditorGUILayout.LabelField("生成Message.cs路径",
                    Application.dataPath + "/" + rootNs + "/" + constNs + "/Message.cs");
            EditorGUILayout.Space();
            createGlobalVar = EditorGUILayout.Toggle("是否重写GlobalVar类", createGlobalVar);
            if (createGlobalVar)
                EditorGUILayout.LabelField("生成GlobalVar.cs路径",
                    Application.dataPath + "/" + rootNs + "/Global/GlobalVar.cs");
            EditorGUILayout.Space();
            createSceneNameClass = EditorGUILayout.Toggle("是否生成SceneName类", createSceneNameClass);
            if (createSceneNameClass)
                EditorGUILayout.LabelField("生成SceneName.cs路径",
                    Application.dataPath + "/" + rootNs + "/" + constNs + "/" + autoDir + "/SceneName.cs");
            EditorGUILayout.Space();

            if (GUILayout.Button("开启自动生成", GUILayout.Height(2 * EditorGUIUtility.singleLineHeight)))
            {
                #region 文件夹的创建

                var resourceDir = Application.dataPath + "/Resources";
                var rootDir = Application.dataPath + "/" + rootNs;
                if (!Directory.Exists(resourceDir))
                {
                    Directory.CreateDirectory(resourceDir);
                    return;
                }

                FileUtil.CreateFolder(rootDir);
                FileUtil.CreateFolder(rootDir + "/" + constNs);
                FileUtil.CreateFolder(rootDir + "/" + constNs + "/" + autoDir);

                #endregion

                autoClassName.Clear();
                otherResPathDic.Clear();
                otherResFileNameDic.Clear();
                allResPathDic.Clear();
                allResFileNameDic.Clear();

                foreach (var fullName in Directory.GetFileSystemEntries(resourceDir))
                {
                    if (Directory.Exists(fullName))
                    {
                        //如果是文件夹
                        OprateDir(new DirectoryInfo(fullName));
                    }
                    else
                    {
                        //是文件
                        OprateFile(new FileInfo(fullName));
                    }
                }

                #region 特殊类的生成

                if (createPanelClasses && autoClassName.Contains("Panel"))
                    CreatePanelFile(Application.dataPath + "/Resources/ClassPanel", viewNs, constNs, rootNs, autoDir);

                #endregion

                #region 定向生成特殊小文件

                if (createGameMgr)
                {
                    CreateMgr(rootNs, constNs, scriptNs, autoDir);
                }

                if (createGlobalVar)
                    FileUtil.CreateClassFile("GlobalVar", rootNs + ".Global", rootDir + "/Global",
                        "ReadyGamerOne.Global.GlobalVar",
                        "这里写当前项目需要的全局变量，调用GlobalVar时，最好调用当前这个类");

                if (createMessage)
                    FileUtil.CreateClassFile("Message", rootNs + "." + constNs, rootDir + "/" + constNs,
                        helpTips: "自定义的消息写到这里");

                if (createSceneNameClass)
                    CreateSceneNameClass(rootNs, constNs, autoDir);

                #endregion

                AssetDatabase.Refresh();
                Debug.Log("生成结束");
            }

            #region 显示生成详情

            EditorGUILayout.Space();
            if (autoClassName.Count > 0)
            {
                var str = "生成的类型有：";
                foreach (var name in autoClassName)
                {
                    str += "\n" + name + "Name" + "\t\t" + name + "Path";
                }

                EditorGUILayout.HelpBox(str, MessageType.Info);
            }

            #endregion
        }
//        foreach (var name in names)
//        {
//            if(name.GetAfterLastChar('/')=="self")
//                continue;
//            otherClassBody += "\t\tpublic const string "+name.Trim().GetAfterLastChar('/')+" = " +
//                              "Path.Combine(Application.streamingAssetsPath, @\""+ name.Trim() +"\");\n";
//        }

        /// <summary>
        /// 创建场景名文件
        /// </summary>
        /// <param name="rootNs"></param>
        /// <param name="constNs"></param>
        /// <param name="autoDirName"></param>
        private static void CreateSceneNameClass(string rootNs, string constNs, string autoDirName)
        {
            var dic = new Dictionary<string, string>();
            foreach (EditorBuildSettingsScene S in EditorBuildSettings.scenes)
            {
                var path = S.path;
                var name = Path.GetFileNameWithoutExtension(path);
                dic.Add(name, name);
            }

            FileUtil.CreateConstClassByDictionary(
                "SceneName",
                Application.dataPath + "/" + rootNs + "/" + constNs + "/" + autoDirName,
                rootNs + "." + constNs,
                dic);
        }

        /// <summary>
        /// 创建GameMgr
        /// </summary>
        /// <param name="rootNs"></param>
        /// <param name="constNs"></param>
        /// <param name="scriptNs"></param>
        /// <param name="autoDirName"></param>
        private static void CreateMgr(string rootNs, string constNs, string scriptNs, string autoDirName)
        {
            var safePartDir = Application.dataPath + "/" + rootNs + "/" + scriptNs;
            var autoPartDir = safePartDir + "/" + autoDirName;
            FileUtil.CreateFolder(safePartDir);
            FileUtil.CreateFolder(autoPartDir);
            var fileName = rootNs + "Mgr";

            var safePartPath = safePartDir + "/" + fileName + ".cs";
            var autoPartPath = autoPartDir + "/" + fileName + ".cs";

            #region AutoPart

            if (File.Exists(autoPartPath))
                File.Delete(autoPartPath);

            var stream = new StreamWriter(autoPartPath);

            stream.Write("using ReadyGamerOne.Script;\n");
            if(_resourceManagerType==ResourceManagerType.AssetBundle)
                stream.Write("using "+rootNs+"."+constNs+";\n");

            stream.Write("using ReadyGamerOne.MemorySystem;\n");

            stream.Write("namespace " + rootNs + "." + scriptNs + "\n" +
                         "{\n" +
                         "\tpublic partial class " + fileName + " : AbstractGameMgr<" + fileName + ">\n" +
                         "\t{\n");

            switch (_resourceManagerType)
            {
                case ResourceManagerType.Resource:
                    stream.Write("\t\tprotected override IResourceLoader ResourceLoader => ResourcesResourceLoader.Instance;\n" +
                                 "\t\tprotected override IAssetConstUtil AssetConstUtil => Utility.AssetConstUtil.Instance;\n");
                    break;
                case ResourceManagerType.AssetBundle:
                    stream.Write("\t\tprotected override IResourceLoader ResourceLoader => AssetBundleResourceLoader.Instance;\n" +
                                 "\t\tprotected override IHotUpdatePath PathData => HotUpdatePathData.Instance;\n"+
                                 "\t\tprotected override IOriginAssetBundleUtil OriginBundleData => OriginBundleUtil.Instance;\n");
                    break;
            }

            stream.Write("\t\tpartial void OnSafeAwake();\n");

            stream.Write("\t\tprotected override void OnStateIsNull()\n" +
                         "\t\t{\n" +
                         "\t\t\tbase.OnStateIsNull();\n" +
                         "\t\t\tOnSafeAwake();\n");

            stream.Write("\t\t}\n" +
                         "\t}\n" +
                         "}\n");

            stream.Dispose();
            stream.Close();

            #endregion

            #region SafePart

            if (File.Exists(safePartPath))
                return;

            stream = new StreamWriter(safePartPath);

            var usePanel = autoClassName.Contains("Panel");
            var useAudio = autoClassName.Contains("Audio");
            if (useAudio || usePanel)
            {
                stream.Write("using ReadyGamerOne.EditorExtension;\n");
                stream.Write("using " + rootNs + "." + constNs + ";\n");
            }

            if (usePanel)
                stream.Write("using ReadyGamerOne.View;\n" +
                             "using ReadyGamerOne.Script;\n");

            stream.Write("namespace " + rootNs + "." + scriptNs + "\n" +
                         "{\n" +
                         "\tpublic partial class " + fileName + "\n" +
                         "\t{\n");
            if (usePanel)
            {
                stream.Write("\t\tpublic StringChooser startPanel = new StringChooser(typeof(PanelName));\n");
            }

            if (useAudio)
            {
                stream.Write("\t\tpublic StringChooser startBgm = new StringChooser(typeof(AudioName));\n");
            }

            stream.Write("\t\tpartial void OnSafeAwake()\n" +
                         "\t\t{\n");

            if (usePanel)
                stream.Write("\t\t\tPanelMgr.PushPanel(startPanel.StringValue);\n");
            if (useAudio)
                stream.Write("\t\t\tAudioMgr.Instance.PlayBgm(startBgm.StringValue);\n");

            stream.Write("\t\t\t//do any thing you want\n" +
                         "\t\t}\n" +
                         "\t}\n" +
                         "}\n");

            stream.Dispose();
            stream.Close();

            #endregion
        }

        /// <summary>
        /// 创建Panel类
        /// </summary>
        /// <param name="panelPrefabDir"></param>
        /// <param name="viewNs"></param>
        /// <param name="constNs"></param>
        /// <param name="rootNs"></param>
        /// <param name="autoDirName"></param>
        /// <returns></returns>
        private static bool CreatePanelFile(string panelPrefabDir, string viewNs, string constNs, string rootNs,
            string autoDirName)
        {
            if (!Directory.Exists(panelPrefabDir))
            {
                Debug.LogWarning("panelPrefabDir is not exist ! ");
                return false;
            }

            foreach (var fullPath in Directory.GetFiles(panelPrefabDir))
            {
                if (fullPath.EndsWith(".meta"))
                    continue;
                if (!fullPath.EndsWith(".prefab"))
                    continue;

                var prefabName = Path.GetFileNameWithoutExtension(fullPath);
                var fileName = prefabName;
                var className = prefabName;
                var safePartDir = Application.dataPath + "/" + rootNs + "/" + viewNs;
                var autoPartDir = safePartDir + "/" + autoDirName;
//                Debug.Log(safePartDir);
                FileUtil.CreateFolder(safePartDir);
                FileUtil.CreateFolder(autoPartDir);
                var safePartPath = safePartDir + "/" + fileName + ".cs";
                var autoPartPath = autoPartDir + "/" + fileName + ".cs";

                #region AutoPart

                if (File.Exists(autoPartPath))
                    File.Delete(autoPartPath);

                var stream = File.CreateText(autoPartPath);

                stream.Write("using ReadyGamerOne.View;\n" +
                             "using " + rootNs + "." + constNs + ";\n" +
                             "namespace " + rootNs + "." + viewNs + "\n" +
                             "{\n" +
                             "\tpublic partial class " + className + " : AbstractPanel\n" +
                             "\t{\n" +
                             "\t\tpartial void OnLoad();\n" +
                             "\n" +
                             "\t\tprotected override void Load()\n" +
                             "\t\t{\n" +
                             "\t\t\tCreate(PanelName." + className + ");\n" +
                             "\t\t\tOnLoad();\n" +
                             "\t\t}\n" +
                             "\t}\n" +
                             "}\n");

                stream.Flush();
                stream.Dispose();
                stream.Close();

                #endregion

                #region SafePart

                if (File.Exists(safePartPath))
                    continue;

                stream = new StreamWriter(safePartPath);

                stream.Write("namespace " + rootNs + "." + viewNs + "\n" +
                             "{\n" +
                             "\tpublic partial class " + fileName + "\n" +
                             "\t{\n" +
                             "\t\tpartial void OnLoad()\n" +
                             "\t\t{\n" +
                             "\t\t\t//do any thing you want\n" +
                             "\t\t}\n" +
                             "\t}\n" +
                             "}\n");

                stream.Dispose();
                stream.Close();

                #endregion
            }

            return true;
        }

        /// <summary>
        /// 遍历Resources目录的时候操作文件的函数
        /// </summary>
        /// <param name="fileInfo"></param>
        private static void OprateFile(FileInfo fileInfo)
        {
//            Debug.Log(fileInfo.FullName);
            if (fileInfo.FullName.EndsWith(".meta"))
            {
//                Debug.Log("跳过。meta");
                return;
            }

            var loadPath = fileInfo.FullName.GetAfterSubstring("Resources\\")
                .GetBeforeSubstring(Path.GetExtension(fileInfo.FullName));
            var fileName = Path.GetFileNameWithoutExtension(fileInfo.FullName);
            var varName = FileUtil.FileNameToVarName(fileName);

            if (allResPathDic.ContainsKey(varName))
            {
                Debug.LogWarning("相同Key: " + varName);
                Debug.LogWarning("已存：" + allResPathDic[varName]);
                Debug.LogWarning("现在：" + loadPath);
            }
            else
            {
                allResPathDic.Add(varName, loadPath);
                allResFileNameDic.Add(varName, fileName);

                otherResPathDic.Add(varName, loadPath);
                otherResFileNameDic.Add(varName, fileName);
            }
        }

        /// <summary>
        /// 遍历Resources目录时，操作目录的函数
        /// </summary>
        /// <param name="dirInfo"></param>
        /// <param name="rootNs"></param>
        /// <param name="constNs"></param>
        /// <param name="autoDir"></param>
        private static bool OprateDir(DirectoryInfo dirInfo)
        {
            var dirName = dirInfo.FullName.GetAfterLastChar('\\');

            if (dirName == "Resources")
                return true;

            if (dirName.StartsWith("Global"))
                return false;

            if (dirName.StartsWith("Class"))
            {
                autoClassName.Add(dirName.GetAfterSubstring("Class"));

                FileUtil.SearchDirectory(dirInfo.FullName,
                    fileInfo =>
                    {
                        if (!fileInfo.FullName.EndsWith(".meta"))
                        {
                            var fileName = Path.GetFileNameWithoutExtension(fileInfo.FullName);
                            var varName = FileUtil.FileNameToVarName(fileName);
                            var loadPath = fileInfo.FullName.GetAfterSubstring("Resources\\")
                                .GetBeforeSubstring(Path.GetExtension(fileInfo.FullName));

                            if (allResPathDic.ContainsKey(varName))
                            {
                                Debug.LogWarning("出现同名资源文件：" + fileInfo);
                            }
                            else
                            {
                                allResPathDic.Add(varName, loadPath);
                                allResFileNameDic.Add(varName, fileName);
                            }
                        }
                    },true);
            }

            return true;
        }
#endif
    }
}


text:
fileFormatVersion: 2
guid: c2bb2f04646c439f910a9b7e75ae09e8
timeCreated: 1576225410


text:
fileFormatVersion: 2
guid: 689bb3f5b270c874493faffca0b82131
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:


text:
using System;
using System.Collections.Generic;
using UnityEngine.Assertions;

namespace ReadyGamerOne.Common
{
    /// <summary>
    /// 模板对象池
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class ObjPool<T>where T:class
    {

        private Queue<T> objCache;

        private event Func<T> onInit;

        private event Action<T> onGet;

        private event Action<T> onRelease;

        public ObjPool(Func<T> onInit, Action<T> onRelease=null,Action<T> onGet=null)
        {
            this.objCache=new Queue<T>();

            this.onInit += onInit;

            if (onGet != null)
                this.onGet += onGet;

            if (onRelease != null)
                this.onRelease += onRelease;
        }

        /// <summary>
        /// 获取对象
        /// </summary>
        /// <returns></returns>
        public T GetObj()
        {
            T obj=null;
            if (objCache.Count <= 0)
            {
                obj = this.onInit();
                if(obj==null)
                    throw new Exception("对象池对象初始化失败");
            }
            else
            {
                obj = objCache.Dequeue();
            }

            this.onGet?.Invoke(obj);
            return obj;
        }

        /// <summary>
        /// 释放对象
        /// </summary>
        /// <param name="obj"></param>
        public void ReleaseObj(T obj)
        {
            Assert.IsTrue(obj!=null);
            this.onRelease?.Invoke(obj);
            objCache.Enqueue(obj);
        }

        public void Clear()
        {
            objCache.Clear();
        }
    }

    /// <summary>
    /// 实现Poolable接口物品的对象池
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class PoolableObjectPool<T> :
        ObjPool<T>
        where T : class, IPoolable, new()
    {
        public PoolableObjectPool() : base(()=>new T(),obj=>obj.OnRecycleToPool() , obj=>obj.OnGetFromPool())
        {
        }
    }
}


text:
fileFormatVersion: 2
guid: 94a4d3d89dde4e6f994682bdd0b55af4
timeCreated: 1587739829


text:
using UnityEngine;

namespace ReadyGamerOne.Common
{
    /// <summary>
    /// MonoBehavior单例类
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class MonoSingleton<T>:
        MonoBehaviour
        where T:MonoSingleton<T>
    {
        private static T _instance;

        public static T Instance {
            get {

                //如果有实例，直接返回
                if (_instance != null)
                    return _instance;

                //如果没有实例，尝试从场景中找一个
                _instance = FindObjectOfType(typeof(T)) as T;

                if (_instance != null)
                    return _instance;

                //实在没有，创建
                var obj = new GameObject
                {
                    hideFlags = HideFlags.DontSave,
                    name = typeof(T).Name
                };

                var script  = (T)obj.AddComponent(typeof(T));
                script.OnStateIsNull();

                return _instance;
            }
        }

        private void Awake()
        {
            if (gameObject.activeSelf == false||enabled==false)
                return;

            if (_instance == null)
            {
                OnStateIsNull();
            }
            else if (_instance == this)
            {
                OnStateIsSelf();
            }
            else
            {
                OnStateIsOthers();
            }
        }

        protected virtual void OnStateIsNull()
        {
            _instance = this as T;
        }

        protected virtual void OnStateIsSelf()
        {

        }

        protected virtual void OnStateIsOthers()
        {

        }

        protected virtual void Start()
        {

        }

        protected virtual void Update()
        {

        }

        protected virtual void OnDestroy()
        {

        }
    }
}


text:
using System;

namespace ReadyGamerOne.Common
{
    /// <summary>
    /// 安全单例
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class SafeSingleton<T>
        where T : class, new()
    {
        private static bool isInsciated = false;

        public SafeSingleton()
        {
            if (isInsciated)
                throw new Exception("这是安全单例，不可多次初始化");
            isInsciated = true;
        }
    }
}


text:
using System;
using UnityEngine;
namespace ReadyGamerOne.Common
{
    /// <summary>
    /// MonoBehavior全局单例泛型类
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class GlobalMonoSingleton<T> : MonoSingleton<T>
        where T : GlobalMonoSingleton<T>
    {
        protected override void OnStateIsNull()
        {
            base.OnStateIsNull();
            if (this.transform.parent)
            {
                throw new Exception($"全局单例物体必须是场景根物体:{name},parent【{transform.parent.name}】");
            }
            DontDestroyOnLoad(this.gameObject);
        }

        /// <summary>
        /// 全局单例，如果重复，直接干掉
        /// </summary>
        protected override void OnStateIsOthers()
        {
            base.OnStateIsOthers();
            GameObject.DestroyImmediate(this.gameObject);
        }
    }
}


text:
using ReadyGamerOne.Common;
using UnityEngine;

public class Trigger2DMessage : MonoBehaviour
{
	//public LayerMask testLayers;
	public string message;
	public int index;
	public bool args;
	void OnTriggerEnter2D(Collider2D col)
	{
		Debug.Log("触碰");
		//if (col.IsTouchingLayers(this.testLayers))
		{
			CEventCenter.BroadMessage(this.message,index,args);

			//this.enabled = false;

			//gameObject.SetActive(false);
		}
	}
}


text:
fileFormatVersion: 2
guid: 81862908eecbaea46aca80c3742ce2e4
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:


text:
namespace ReadyGamerOne.Common
{
    public interface IPoolable
    {
        /// <summary>
        /// 对象池回收Person时调用
        /// </summary>
        void OnRecycleToPool();

        /// <summary>
        /// 对象池加载Person时调用
        /// </summary>
        void OnGetFromPool();
    }
}


text:
namespace ReadyGamerOne.Common
{
    /// <summary>
    /// 与预制体对应的物体
    /// </summary>
    public interface IResourcableObject:
        IUnityGameObject
    {
        /// <summary>
        /// 预制体或资源路径
        /// </summary>
        string ResKey { get; }
        /// <summary>
        /// 实例化物体
        /// </summary>
        void OnInstanciateObject();
        /// <summary>
        /// 销毁物体
        /// </summary>
        void Destroy();
        /// <summary>
        /// 激活物体
        /// </summary>
        void EnableObject();
        /// <summary>
        /// 失活物体
        /// </summary>
        void DisableObject();
    }
}


text:
fileFormatVersion: 2
guid: 54e457dffa22447d8b37452845ac0f39
timeCreated: 1623133248


text:
using System;

namespace ReadyGamerOne.Common
{
    [AttributeUsage(AttributeTargets.Class)]
    public class ScriptableSingletonInfoAttribute:Attribute
    {
        public string assetName;

        public ScriptableSingletonInfoAttribute(string assetName = null)
        {
            this.assetName = assetName;
        }
    }
}


text:
namespace ReadyGamerOne.Common
{
    /// <summary>
    /// 此接口表示类具有ID
    /// </summary>
    public interface IHasStringId
    {
        string ID { get; }
    }
}


text:
fileFormatVersion: 2
guid: a2f7540b1afb4ffb9b15b1f79147b8ab
timeCreated: 1575995300


text:
using System;
using System.Collections.Generic;

namespace ReadyGamerOne.Common
{
    /// <summary>
    /// 事件中心
    /// 监听分发事件
    /// </summary>CallBack
    public static class CEventCenter
    {
        #region Private

        /// <summary>
        /// 消息字典
        /// </summary>
        private static Dictionary<string, List<Delegate>> listeners =
            new Dictionary<string, List<Delegate>>();

        private static List<Delegate> onceActions = new List<Delegate>();

        #endregion

        /// <summary>
        /// 添加监听事件
        /// </summary>
        /// <param name="type"></param>
        /// <param name="listener"></param>
        public static void AddListener(string type, Action listener)
        {
            AddListener(type, listener, false);
        }
        public static void AddListener(string type, Action listener, bool callOnce)
        {
            if (!listeners.ContainsKey(type))
                listeners.Add(type, new List<Delegate>());
            listeners[type].Add(listener);
            if (callOnce)
                onceActions.Add(listener);
        }
        public static void AddListener<T>(string type, Action<T> listener)
        {
            AddListener<T>(type, listener, false);
        }
        public static void AddListener<T>(string type, Action<T> listener, bool callOnce)
        {
            if (!listeners.ContainsKey(type))
                listeners.Add(type, new List<Delegate>());
            listeners[type].Add(listener);
            if (callOnce)
                onceActions.Add(listener);
        }
        public static void AddListener<T, U>(string type, Action<T, U> listener)
        {
            AddListener<T, U>(type, listener, false);
        }
        public static void AddListener<T, U>(string type, Action<T, U> listener, bool callOnce)
        {
            if (!listeners.ContainsKey(type))
                listeners.Add(type, new List<Delegate>());
            listeners[type].Add(listener);
            if (callOnce)
                onceActions.Add(listener);
        }
        /// <summary>
        /// 移出监听事件
        /// </summary>
        /// <param name="type"></param>
        /// <param name="listener"></param>
        public static void RemoveListener(string type, Action listener)
        {
            if (listeners.ContainsKey(type))
                listeners[type].Remove(listener);
        }
        public static void RemoveListener<T>(string type, Action<T> listener)
        {
            if (listeners.ContainsKey(type))
                listeners[type].Remove(listener);
        }
        public static void RemoveListener<T, U>(string type, Action<T, U> listener)
        {
            if (listeners.ContainsKey(type))
                listeners[type].Remove(listener);
        }

        public static void BroadMessage(string type)
        {
            if (listeners.ContainsKey(type))
            {
                var list = listeners[type];
                for (var i = 0; i < list.Count; i++)
                {
                    var f = list[i] as Action;
                    if (null != f)
                    {
                        f();
                        if (onceActions.Contains(f))
                        {
                            list.Remove(f);
                            onceActions.Remove(f);
                        }
                    }
                }
            }
        }
        public static void BroadMessage<T>(string type, T arg1)
        {
            if (listeners.ContainsKey(type))
            {
                var list = listeners[type];
                for (var i = 0; i < list.Count; i++)
                {
                    var f = list[i] as Action<T>;
                    if (null != f)
                    {
                        f(arg1);
                        if (onceActions.Contains(f))
                        {
                            list.Remove(f);
                            onceActions.Remove(f);
                        }
                    }
                }
            }
        }
        public static void BroadMessage<T, U>(string type, T arg1, U arg2)
        {
            if (listeners.ContainsKey(type))
            {
                var list = listeners[type];
                for (var i = 0; i < list.Count; i++)
                {
                    var f = list[i] as Action<T, U>;
                    if (null != f)
                    {
                        f(arg1, arg2);
                        if (onceActions.Contains(f))
                        {
                            list.Remove(f);
                            onceActions.Remove(f);
                        }
                    }
                }
            }
        }
        public static void Clear()
        {
            listeners.Clear();
            onceActions.Clear();
        }
    }
}


text:
fileFormatVersion: 2
guid: 4b8e03f90fde4f418fbedc631c6ddbdb
timeCreated: 1587739691


text:
fileFormatVersion: 2
guid: ca2cfed511914e4bab2569bfdd409eb3
timeCreated: 1579181696


text:
fileFormatVersion: 2
guid: 8ddda19eb78b4632a797c19f090834a7
timeCreated: 1576120579


text:
namespace ReadyGamerOne.Common
{

    // 实现普通的单例模式
    // where 限制模板的类型, new()指的是这个类型必须要能被实例化
    public abstract class Singleton<T> where T : new() {
        private static T _instance;
        private static object mutex = new object();
        public static T Instance {
            get {
                if (_instance == null) {
                    lock (mutex) {//线程安全性
                        if (_instance == null) {
                            _instance = new T();
                        }
                    }
                }
                return _instance;
            }
        }
    }
}


text:
fileFormatVersion: 2
guid: dbc5f26ad8e34fd08a016d52b73d7f2e
timeCreated: 1565052586


text:
fileFormatVersion: 2
guid: 9e602b5ad6abe3b488d8e1cdfcf24f77
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:


text:
fileFormatVersion: 2
guid: 8db6db7db146d634c8d73833da2956f3
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:


text:
fileFormatVersion: 2
guid: a67dfbe9077c46cbb59b2e88462e7168
timeCreated: 1579182048


text:
using System;
using ReadyGamerOne.Utility;

#if UNITY_EDITOR
using Directory = UnityEngine.Windows.Directory;
#endif

#if UNITY_EDITOR
using UnityEditor;
#endif

using UnityEngine;

namespace ReadyGamerOne.Common
{
    public class ScriptableSingleton<T>
        :ScriptableObject
        where T:ScriptableSingleton<T>
    {
        private static string _assetName;

        private static string AssetName
        {
            get
            {

                if (string.IsNullOrEmpty(_assetName))
                {
                    var type = typeof(T);
                    var attribute = type.GetAttribute<ScriptableSingletonInfoAttribute>();
                    if (null == attribute || string.IsNullOrEmpty(attribute.assetName))
                        _assetName = type.Name;
                    else
                        _assetName = attribute.assetName;
                }

                return _assetName;
            }
        }

        private static T _instance;
        public static T Instance
        {
            get
            {
                if (!_instance)
                {
                    _instance = Resources.Load<T>("GlobalAssets/"+AssetName);
                }
#if UNITY_EDITOR
                if (!_instance)
                {
                    var path = "Assets/Resources/GlobalAssets";
                    if (!Directory.Exists(path))
                        Directory.CreateDirectory(path);
                    _instance = CreateInstance<T>();
                    AssetDatabase.CreateAsset(_instance, path+"/"+AssetName+".asset");
                }
#endif
                if (_instance == null)
                {
                    throw new Exception("初始化失败");
                }

                return _instance;
            }
        }
    }
}


text:
fileFormatVersion: 2
guid: b0e7f42510658ca459f0f12b428b314c
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:


text:
using UnityEngine;

namespace ReadyGamerOne.Common
{
    public interface IUnityGameObject
    {
        GameObject gameObject { get; }
        Transform transform { get; }
        Vector3 position { get; set; }
        Vector3 localPosition { get; set; }
    }
}


text:
fileFormatVersion: 2
guid: b502cdbcc667dce419bfb328dac18458
folderAsset: yes
DefaultImporter:
  externalObjects: {}
  userData:
  assetBundleName:
  assetBundleVariant:


text:
fileFormatVersion: 2
guid: 0264958cc64273c46a257dd5bc018605
folderAsset: yes
DefaultImporter:
  externalObjects: {}
  userData:
  assetBundleName:
  assetBundleVariant:


text:
fileFormatVersion: 2
guid: 7970120b28b7fc040bcf888bbe4e0062
TextScriptImporter:
  externalObjects: {}
  userData:
  assetBundleName:
  assetBundleVariant:


text:
# Unity-Logs-Viewer

Please note with new unity you can view device logs on editor console window, check the new attach menu in editor console window.

Using this tool you can easily check your editor console logs inside the game itself!
All what you have to do is to make a circle gesture using your mouse (click and drag) or your finger (touch and drag) on the mobile screen to show all these logs!

You can download also from unity asset store
https://www.assetstore.unity3d.com/en/#!/content/12047

#### To setup log viewer do the following
    1 - create reporter from menu (Reporter->Create) at first scene your game start.
    2 - then set the ” Scrip execution order ” in (Edit -> Project Settings ) of Reporter.cs to be the highest.

### Version 1.8
    - Add Copy to clipboard.
	- Merge Fix for Unity 2019.
	- Fix ReporterModificationProcessor is annoying.
	- Fix waste ram.

### Version 1.7
    - Add Save logs button( thanks for Ahmed Shbli ).
    - Fix deprecated code for new unity.
    - Fix Warnings.
    - Fix loading scene from asset bundle error.


text:
fileFormatVersion: 2
guid: 600c02144c4813244abd262cbcbe8825
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: -12000
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:


text:
fileFormatVersion: 2
guid: 7482c424dbd8a48489e4b2091f858a69
folderAsset: yes
DefaultImporter:
  userData:


text:
using System.Collections.Generic;

public class MultiKeyDictionary<T1, T2, T3> : Dictionary<T1, Dictionary<T2, T3>>
{
	new public Dictionary<T2, T3> this[T1 key]
	{
		get
		{
			if (!ContainsKey(key))
				Add(key, new Dictionary<T2, T3>());

			Dictionary<T2, T3> returnObj;
			TryGetValue(key, out returnObj);

			return returnObj;
		}
	}

	public bool ContainsKey(T1 key1, T2 key2)
	{
		Dictionary<T2, T3> returnObj;
		TryGetValue(key1, out returnObj);
		if (returnObj == null)
			return false;

		return returnObj.ContainsKey(key2);
	}
}