text:
using System;
using System.Collections;
using System.Collections.Generic;
using ReadyGamerOne.Common;
using ReadyGamerOne.View.Effects;
using UnityEngine;
using UnityEngine.UI;

namespace ReadyGamerOne.View
{
    /// <summary>
    /// 总UI管理者
    /// </summary>
    public static class PanelMgr
    {
        public static event Action onBeforeClear;

        public static IStackPanel CurrentPanel
        {
            get
            {
                if (panelStack.Count == 0)
                    return null;
                return panelStack.Peek();
            }
        }

        #region 加载Panel

        /// <summary>
        /// 加载一个新面板
        /// </summary>
        /// <param name="name"></param>
        public static void PushPanel(string name)
        {
            if (panelStack.Count != 0)
                panelStack.Peek().Disable();

            var panel = AbstractPanel.GetPanel(name);
            panel.Enable();
            panelStack.Push(panel);
//            Debug.Log("入栈：" + panelStack.Count + " name:" + name);
        }

        /// <summary>
        /// 加载新面板，带动画
        /// </summary>
        /// <param name="panelName"></param>
        /// <param name="transition"></param>
        public static void PushPanel(string panelName, AbstractTransition transition)
        {
            var panel = AbstractPanel.GetPanel(panelName);
            transition.PushPanel(panel);
            panelStack.Push(panel);

//            Debug.Log("入栈：" + panelStack.Count + " name:" + panelName);
        }

        /// <summary>
        /// 加载新面板，带效果
        /// </summary>
        /// <param name="panelName"></param>
        /// <param name="abstractScreenEffect"></param>
        public static void PushPanel(string panelName, AbstractScreenEffect abstractScreenEffect)
        {
            var panel = AbstractPanel.GetPanel(panelName);
            abstractScreenEffect.OnBegin(CurrentPanel as AbstractPanel, panel);
            panelStack.Push(panel);
        }

        /// <summary>
        /// 加载新面板，都带
        /// </summary>
        /// <param name="panelName"></param>
        /// <param name="transition"></param>
        /// <param name="abstractScreenEffect"></param>
        public static void PushPanel(string panelName, AbstractTransition transition,
            AbstractScreenEffect abstractScreenEffect)

        {
            var panel = AbstractPanel.GetPanel(panelName);
            transition.onBegin += abstractScreenEffect.OnBegin;
            transition.PushPanel(panel);
            panelStack.Push(panel);
        }

        #region 加载Panel，同时广播一条消息
        public static void PushPanelWithMessage(string panelName, string message)
        {
            PushPanel(panelName);
            CEventCenter.BroadMessage(message);
        }

        public static void PushPanelWithMessage<T>(string panelName, string message, T arg1)
        {
            PushPanel(panelName);
            CEventCenter.BroadMessage<T>(message,arg1);
        }

        public static void PushPanelWithMessage<T1, T2>(string panelName, string message, T1 arg1, T2 arg2)
        {
            PushPanel(panelName);
            CEventCenter.BroadMessage(message, arg1, arg2);
        }

        #endregion

        #endregion

        #region 移除Panel

        /// <summary>
        /// 移除当前面板
        /// </summary>
        public static void PopPanel()
        {
            //Debug.Log("1出栈：" + this.panelStack.Count + " name:" +CurrentPanel.InternalPanelName);
            panelStack.Peek().Destory();
            //Debug.Log("2出栈：" + this.panelStack.Count + " name:" + CurrentPanel.InternalPanelName);
            panelStack.Pop();
            if (panelStack.Count > 0)
                panelStack.Peek().Enable();
        }

        #endregion

        /// <summary>
        /// 清空Panel栈
        /// </summary>
        public static void Clear()
        {
            onBeforeClear?.Invoke();
            while (panelStack.Count>0)
            {
                panelStack.Pop().Destory();
            }
        }

        #region Effects

        /// <summary>
        /// 淡出效果，全屏幕逐渐变为 Color颜色
        /// </summary>
        /// <param name="time"></param>
        /// <param name="color"></param>
        /// <param name="callBack"></param>
        public static Coroutine FadeOut(float time, Color color,Action callBack=null)
        {
            var image = GetEffectImage();
            image.color = new Color(color.r, color.g, color.b, 0);
            return image.StartCoroutine(ChangeAlpha(image, 1, time, callBack));
        }

        /// <summary>
        /// 淡入效果，全屏幕渐变为正常色
        /// </summary>
        /// <param name="time"></param>
        /// <param name="color"></param>
        /// <param name="callBack"></param>
        public static Coroutine FadeIn(float time,  Action callBack=null)
        {
            var image = GetEffectImage();
            return image.StartCoroutine(ChangeAlpha(image, 0, time, callBack));
        }

        #endregion

        #region Private

        private static Stack<IStackPanel> panelStack = new Stack<IStackPanel>();
        private static IEnumerator ChangeAlpha(Image image, int targetAlpha, float time, Action callBack)
        {
             //Time.deltaTime/inTime
             var rowColor = image.color;
             for (var timer = 0f; timer < time; timer += Time.deltaTime)
             {
                 if(targetAlpha==0)
                    image.color=new Color(rowColor.r,rowColor.g,rowColor.b,1-timer/time);
                 else
                     image.color=new Color(rowColor.r,rowColor.g,rowColor.b,timer/time);
                 yield return null;

             }
             callBack?.Invoke();
        }

        private static Image GetEffectImage()
        {
            var effectPanel = Global.GlobalVar.GCanvasObj.transform.Find("EffectPanel");
            if (effectPanel == null)
            {
                var panelObj = new GameObject("EffectPanel");
                panelObj.transform.SetParent(Global.GlobalVar.GCanvasObj.transform);
                panelObj.transform.localPosition = Vector3.zero;
                var image = panelObj.AddComponent<Image>();
                var rect = panelObj.GetComponent<RectTransform>();
                rect.sizeDelta = new Vector2(Screen.width, Screen.height);
                rect.ForceUpdateRectTransforms();

                return image;
            }

            if (effectPanel.gameObject.activeSelf == false)
                effectPanel.gameObject.SetActive(true);

            effectPanel.SetAsLastSibling();

            return effectPanel.GetComponent<Image>();
        }

        #endregion

    }
}


text:
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;

namespace ReadyGamerOne.View
{
    /// <inheritdoc />
    /// <summary>
    /// 可用栈盛放的栈窗口
    /// </summary>
    public abstract class AbstractPanel : AbstractUI, IStackPanel
    {
        #region Static

        internal static void RegisterPanels(Type outType)
        {
            foreach (var type in Assembly.GetAssembly(outType).GetTypes())
            {
                if (type.IsSubclassOf(typeof(AbstractPanel)))
                {
                    RegisterPanel(type);
                }
            }
        }
        private static void RegisterPanel(Type panelType)
        {
            Assert.IsTrue(panelType.IsSubclassOf(typeof(AbstractPanel)));
            if(panelDic.ContainsKey(panelType.Name))
            {
                Debug.LogWarning("注意Project中是不是有同名类继承了AbstractPanel");
                return;
            }

            var p = Activator.CreateInstance(panelType) as AbstractPanel;
            panelDic.Add(panelType.Name, p);
        }

        private static Dictionary<string, AbstractPanel> panelDic =
            new Dictionary<string, AbstractPanel>();
        internal static AbstractPanel GetPanel(string name)
        {
            if(!panelDic.ContainsKey(name))
                Debug.LogError("你好像没有在GameMgr_RegisterUiPanel中注册这个Panel类："+name);
            return panelDic[name];
        }

        #endregion

        public sealed override void DestroyThis(PointerEventData eventData = null)
        {
            base.DestroyThis(eventData);
        }

        #region IStackPanel

        public virtual void Enable()
        {
            if (m_TransFrom == null)
            {
//                Debug.Log("Load");
                Load();
            }
            this.Show();
        }

        public virtual void Disable()
        {
            this.Hide();
        }

        public virtual void Destory()
        {
//            Debug.Log("Clear"+GetType().Name);
            this.Disable();
            view.Clear();
            this.DestroyThis();
        }

        #endregion

        protected abstract void Load();

        #region 在子类可以通过view["path"]访问所有节点

        protected override void Create(string path)
        {
//            Debug.Log("Create: "+GetType());
            base.Create(path);
            load_all_object(m_TransFrom.gameObject,"");
        }

        private Dictionary<string, GameObject> view = new Dictionary<string, GameObject>();

        void load_all_object(GameObject root, string path) {
            foreach (Transform tf in root.transform) {
                if (this.view.ContainsKey(path + tf.gameObject.name)) {
                    Debug.LogWarning("发现同名UI节点:" + path + tf.gameObject.name + "!");
                    continue;
                }

//                Debug.Log("加载UI物体： " + path + tf.gameObject.name);
                this.view.Add(path + tf.gameObject.name, tf.gameObject);
                load_all_object(tf.gameObject, path + tf.gameObject.name + "/");
            }

        }

        protected Transform GetTransform(string path)
        {
            if (!view.ContainsKey(path))
                throw new Exception("不包含这个路径：" + path);
            return view[path].transform;
        }
        protected T GetComponent<T>(string path) where T : Component
        {
            if (!view.ContainsKey(path))
                throw new Exception("不包含这个路径：" + path);
            return view[path].GetComponent<T>();
        }

        #endregion

        #region 添加UnityUI回调

        protected void add_button_listener(string view_name, UnityAction onclick)
        {
//            Debug.Log(view_name);
            Button bt = view[view_name].GetComponent<Button>();
            if (bt == null) {
                Debug.LogWarning("UI_manager add_button_listener: not Button Component!");
                return;
            }

            bt.onClick.AddListener(onclick);
        }

        protected void add_slider_listener(string view_name, UnityAction<float> on_value_changle)
        {
            Slider s = view[view_name].GetComponent<Slider>();
            if (s == null) {
                Debug.LogWarning("UI_manager add_slider_listener: not Slider Component!");
                return;
            }

            s.onValueChanged.AddListener(on_value_changle);
        }

        #endregion

    }
}

mime_type: text/plain


text:
using System;
using ReadyGamerOne.Global;
using ReadyGamerOne.MemorySystem;
using ReadyGamerOne.Script;
using UnityEngine;
using UnityEngine.EventSystems;
using Object = UnityEngine.Object;

namespace ReadyGamerOne.View
{
    /// <summary>
    /// 窗口基类
    /// </summary>
    public abstract class AbstractUI
    {
        public Transform m_TransFrom = null; //位置
        private string m_sResName = ""; //资源名
        private bool m_bIsVisible = false; //是否可见

        /// <summary>
        /// 类对象初始化
        /// 添加对象使用必须的事件监听
        /// Create时调用
        /// </summary>
        protected virtual void InitWindow()
        {
            MainLoop.Instance.AddUpdateFunc(Update);
        }

        /// <summary>
        /// 类对象释放
        /// Destroy时调用
        /// </summary>
        protected virtual void RealseWindow()
        {
            MainLoop.Instance.RemoveUpdateFunc(Update);
        }

        /// <summary>
        /// 游戏事件注册
        /// </summary>
        protected virtual void OnAddListener()
        {
        }

        /// <summary>
        /// 游戏事件注销
        /// </summary>
        protected virtual void OnRemoveListener()
        {
        }

        /// <summary>
        /// 更新
        /// </summary>
        protected virtual void Update()
        {

        }

        /// <summary>
        /// 是否可见
        /// </summary>
        /// <returns></returns>
        public bool IsVisable()
        {
            return m_bIsVisible;
        }

        /// <summary>
        /// 创建窗体
        /// </summary>
        /// <returns></returns>
        protected virtual void Create(string path)
        {
            m_sResName = path;

            if (m_TransFrom)
            {
                Debug.LogError("Window Create Error Exist");
                return;
            }

            if (string.IsNullOrEmpty(m_sResName))
            {
                Debug.LogError("Windows Create Error ResName is empty");
                return;
            }

            if (GlobalVar.GCanvasObj == null)
                throw new Exception("你忘了初始化GlobalVar.GCanvasObj");

            var canvas = Global.GlobalVar.GCanvasObj.transform;

            if (null == canvas)
                throw new Exception("画布获取失败");

            var obj = ResourceMgr.InstantiateGameObject(m_sResName, canvas);

            if (obj == null)
            {
                Debug.LogError("ResourceMgr.InstantiateGameObject 返回 null 资源名：" + m_sResName);
                return;
            }

            m_TransFrom = obj.transform;

            m_TransFrom.gameObject.SetActive(false);

            InitWindow();
        }

        /// <summary>
        /// 销毁窗体
        /// </summary>
        public virtual void DestroyThis(PointerEventData eventData = null)
        {
            if (m_TransFrom)
            {
                OnRemoveListener();
                RealseWindow();
                Object.Destroy(m_TransFrom.gameObject);
                m_TransFrom = null;
            }
        }

        /// <summary>
        /// 显示
        /// </summary>
        public void Show()
        {
            if (m_TransFrom && m_TransFrom.gameObject.activeSelf == false)
            {
                m_TransFrom.gameObject.SetActive(true);
                m_bIsVisible = true;
                OnAddListener();
            }
        }

        /// <summary>
        /// 隐藏
        /// </summary>
        public void Hide()
        {
            if (m_TransFrom && m_TransFrom.gameObject.activeSelf == true)
            {
                OnRemoveListener();
                m_TransFrom.gameObject.SetActive(false);
            }

            m_bIsVisible = false;

        }

    }
}


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


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


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


text:
namespace ReadyGamerOne.View
{
    public interface IStackPanel
    {
        //        string panelName { get; set; }
//        string InternalPanelName { get; }
        void Enable();
        void Disable();
        void Destory();
    }
}


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


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


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


text:
using System;
using ReadyGamerOne.Script;

namespace ReadyGamerOne.View.Effects
{
    public abstract class AbstractTransition
    {
        protected TransionType type;
        public float time;
        private float delay;
        public event Action<AbstractPanel, AbstractPanel> onBegin;
        public event Action<AbstractPanel, AbstractPanel> onEnd;

        protected AbstractTransition(TransionType type,float time,float delay)
        {
            this.delay = delay;
            this.time = time;
            this.type = type;
        }

        public void PushPanel(AbstractPanel newPanel)
        {
            var panelBefore = PanelMgr.CurrentPanel as AbstractPanel;
            MainLoop.Instance.ExecuteLater(() =>
            {
                OnBegin(panelBefore, newPanel);
                if (onBegin != null)
                    onBegin(panelBefore, newPanel);
                MainLoop.Instance.ExecuteLater(() =>
                {
                    OnEnd(panelBefore, newPanel);
                    if (onEnd != null)
                        onEnd(panelBefore, newPanel);
                }, this.time);
            }, this.delay);
        }

        protected abstract void OnBegin(AbstractPanel panelBefore,AbstractPanel newPanel);
        protected abstract void OnEnd(AbstractPanel panelBefore,AbstractPanel newPanel);
    }
}


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


text:
namespace ReadyGamerOne.View.Effects
{
    public enum TransionType
    {
        PageDown,
        PageRight,
        PageUp,
        PageLeft
    }

}


text:
using ReadyGamerOne.Global;
using ReadyGamerOne.Script;
using UnityEngine;

namespace ReadyGamerOne.View.Effects
{
    public class ScollTransition :AbstractTransition
    {
        public ScollTransition(TransionType type, float delay, float time) : base(type,time,delay)
        {
        }

        protected override void OnBegin(AbstractPanel panelBefore,AbstractPanel newPanel)
        {
            var speedX = Screen.width / this.time;
            var speedY = Screen.height / this.time;

            newPanel.Enable();

            switch (type)
            {
                case TransionType.PageDown:
                    newPanel.m_TransFrom.position = GlobalVar.GCanvasButton;
                    MainLoop.Instance.UpdateForSeconds(
                        () =>
                        {
                            if (panelBefore != null)
                                panelBefore.m_TransFrom.Translate(Vector3.up * speedY*Time.deltaTime, Space.World);
                            newPanel.m_TransFrom.Translate(Vector3.up * speedY*Time.deltaTime, Space.World);
                        },
                        this.time);
                    break;
                case TransionType.PageRight:
                    newPanel.m_TransFrom.position = GlobalVar.GCanvasRight;
                    MainLoop.Instance.UpdateForSeconds(
                        () =>
                        {
                            if (panelBefore != null)
                                panelBefore.m_TransFrom.Translate(Vector3.left * speedX*Time.deltaTime, Space.World);
                            newPanel.m_TransFrom.Translate(Vector3.left * speedX*Time.deltaTime, Space.World);
                        },
                        this.time);
                    break;
                case TransionType.PageUp:
                    newPanel.m_TransFrom.position = GlobalVar.GCanvasTop;
                    MainLoop.Instance.UpdateForSeconds(
                        () =>
                        {
                            if (panelBefore != null)
                                panelBefore.m_TransFrom.Translate(Vector3.down * speedY*Time.deltaTime, Space.World);
                            newPanel.m_TransFrom.Translate(Vector3.down * speedY*Time.deltaTime, Space.World);
                        },
                        this.time);
                    break;
                case TransionType.PageLeft:
                    newPanel.m_TransFrom.position = GlobalVar.GCanvasLeft;
                    MainLoop.Instance.UpdateForSeconds(
                        () =>
                        {
                            if (panelBefore != null)
                                panelBefore.m_TransFrom.Translate(Vector3.right * speedX*Time.deltaTime, Space.World);
                            newPanel.m_TransFrom.Translate(Vector3.right * speedX*Time.deltaTime, Space.World);
                        },
                        this.time);
                    break;
            }
        }

        protected override void OnEnd(AbstractPanel panelBefore,AbstractPanel newPanel)
        {
            newPanel.m_TransFrom.position = GlobalVar.GCanvasObj.transform.position;
            if(panelBefore!=null&&panelBefore.IsVisable())
                panelBefore.Disable();
        }

    }
}


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


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


text:
namespace ReadyGamerOne.View.Effects
{
    public abstract class AbstractScreenEffect
    {
        protected float time;

        public AbstractScreenEffect(float time)
        {
            this.time = time;
        }
        public abstract void OnBegin(AbstractPanel panelBefore, AbstractPanel newPanel);
        public abstract void OnEnd(AbstractPanel panelbefore, AbstractPanel newPanel);
    }
}


text:
using System;
using ReadyGamerOne.Global;
using ReadyGamerOne.Script;
using UnityEngine;
using UnityEngine.Assertions;

namespace ReadyGamerOne.View.Effects
{
    public class FadeInOut : AbstractScreenEffect
    {
        public override void OnBegin(AbstractPanel panelBefore, AbstractPanel newPanel)
        {
            if (panelBefore != null)
                this.time *= 0.5f;
            PanelFadeOut(panelBefore,this.time,()=>PanelFadeIn(newPanel,this.time));

        }

        public override void OnEnd(AbstractPanel panelbefore, AbstractPanel newPanel)
        {
        }

        public FadeInOut(float time) : base(time)
        {
        }

        private void PanelFadeOut(AbstractPanel panelBefore, float time,Action endCall)
        {
            CanvasGroup cg;
            if (panelBefore != null)
            {
                cg = panelBefore.m_TransFrom.GetComponent<CanvasGroup>();
                if ( cg== null)
                    cg=panelBefore.m_TransFrom.gameObject.AddComponent<CanvasGroup>();
                MainLoop.Instance.UpdateForSeconds(() => { cg.alpha -= Time.deltaTime / time; }, time,
                    () =>
                    {
                        cg.alpha = 1;
                        if(panelBefore.IsVisable())
                            panelBefore.Disable();
                        endCall();
                    });
            }
        }

        private void PanelFadeIn(AbstractPanel newPanel, float time)
        {
            Assert.IsTrue(newPanel!=null);

            if(newPanel.IsVisable()==false)
                newPanel.Enable();

            CanvasGroup cg;
            cg = newPanel.m_TransFrom.GetComponent<CanvasGroup>();

            if ( cg== null)
                cg=newPanel.m_TransFrom.gameObject.AddComponent<CanvasGroup>();
            cg.alpha = 0;
            MainLoop.Instance.UpdateForSeconds(() =>
                {
                    if(!newPanel.IsVisable())
                        return;
                    cg.alpha += Time.deltaTime / time;
                },
                time,
                () =>
                {
                    if(!newPanel.IsVisable())
                        return;
                    newPanel.m_TransFrom.position = GlobalVar.GCanvasObj.transform.position;
                });

        }

    }
}


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


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


text:
fileFormatVersion: 2
guid: 563fd757e69744ccbed13aa687dc2584
timeCreated: 1576225068


text:
fileFormatVersion: 2
guid: c7040b41bb474cc3a048cf27cc068328
timeCreated: 1576225076


text:
namespace ReadyGamerOne.Utility
{
    public interface IDrawGizmos
    {
        void OnDrawGizmos();
    }
}


text:
fileFormatVersion: 2
guid: 8db7b7c897b94d1691100b41e0d155b5
timeCreated: 1570194876


text:
using UnityEngine;

namespace ReadyGamerOne.Utility
{
    public static class TextureUtil
    {
        public static Texture2D CreateTexture2D(RenderTexture rt)
        {
            var before = RenderTexture.active;
            var width = rt.width;
            var height = rt.height;

            var ans = new Texture2D(width, height, TextureFormat.ARGB32, false);

            RenderTexture.active = rt;

            ans.ReadPixels(new Rect(0, 0, width, height), 0, 0);
            ans.Apply();

            RenderTexture.active = before;

            return ans;
        }
    }
}


text:
using System;
using System.Collections.Generic;
using System.Reflection;
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine.Assertions;
using UnityEngine.Windows;
#endif
using UnityEngine;

namespace ReadyGamerOne.Utility
{
#if UNITY_EDITOR
    public class EditorUtil:Editor
    {

        /// <summary>
        /// 显示一块可以接收拖拽物体的区域
        /// </summary>
        /// <param name="meg">tips</param>
        /// <param name="height">高度</param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static List<T> GetDragObjectsFromArea<T>(string meg = null,float height=35f)
            where T: UnityEngine.Object
        {
            Event aEvent;
            aEvent = Event.current;

            GUI.contentColor = Color.white;
            List<T> list = new List<T>();

            var dragArea = GUILayoutUtility.GetRect(0f, height, GUILayout.ExpandWidth(true));

            GUIContent title = new GUIContent(meg);
            if (string.IsNullOrEmpty(meg))
            {
                title = new GUIContent("Drag Object here from Project view to get the object");
            }
            GUIStyle s = new GUIStyle()
            {
                fontSize=15,
                alignment = TextAnchor.MiddleCenter
            };

            GUI.Box(dragArea,title,s);

            switch (aEvent.type)
            {
                case EventType.DragUpdated:
                case EventType.DragPerform:
                    if (!dragArea.Contains(aEvent.mousePosition))
                    {
                        break;
                    }

                    DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
                    if (aEvent.type == EventType.DragPerform)
                    {
                        DragAndDrop.AcceptDrag();

                        for (int i = 0; i < DragAndDrop.objectReferences.Length; ++i)
                        {

                            var temp = DragAndDrop.objectReferences[i] as T;
                            if(temp!=null)
                                list.Add(temp);
                        }
                    }

                    Event.current.Use();
                    break;
                default:
                    break;
            }

            return list;
        }
        public static List<T> GetDragObjectsFromArea<T>(Rect dragArea)
            where T : UnityEngine.Object
        {
            Event aEvent;
            aEvent = Event.current;

            GUI.contentColor = Color.white;
            List<T> list = new List<T>();
            switch (aEvent.type)
            {
                case EventType.DragUpdated:
                case EventType.DragPerform:
                    if (!dragArea.Contains(aEvent.mousePosition))
                    {
                        break;
                    }

                    DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
                    if (aEvent.type == EventType.DragPerform)
                    {
                        DragAndDrop.AcceptDrag();

                        for (int i = 0; i < DragAndDrop.objectReferences.Length; ++i)
                        {

                            var temp = DragAndDrop.objectReferences[i] as T;
                            if (temp != null)
                                list.Add(temp);
                        }
                    }

                    Event.current.Use();
                    break;
                default:
                    break;
            }

            return list;
        }

        /// <summary>
        /// 根据Type里的public static 字段填充arrProp
        /// </summary>
        /// <param name="arrProp"></param>
        /// <param name="type"></param>
        public static void InitSerializedStringArray(SerializedProperty arrProp, Type type)
        {
            arrProp.arraySize = 0;
            FieldInfo[] fieldInfos = type.GetFields(BindingFlags.Public | BindingFlags.Static);
            for (int i = 0; i < fieldInfos.Length; i++)
            {
                arrProp.InsertArrayElementAtIndex(i);
                arrProp.GetArrayElementAtIndex(i).stringValue = fieldInfos[i].GetValue(null) as string;
            }
        }

        /// <summary>
        /// 根据list填充arrProp
        /// </summary>
        /// <param name="arrProp"></param>
        /// <param name="strs"></param>
        public static void InitSerializedStringArray(SerializedProperty arrProp, IList<string> strs)
        {
            arrProp.arraySize = 0;
            for (int i = 0; i < strs.Count; i++)
            {
                arrProp.InsertArrayElementAtIndex(i);
                arrProp.GetArrayElementAtIndex(i).stringValue = strs[i];

            }
        }

        /// <summary>
        /// 安全的创建ScriptableObject资源
        /// </summary>
        /// <param name="defaultName"></param>
        /// <typeparam name="T"></typeparam>
        public static void CreateAsset<T>(string defaultName)
            where T:ScriptableObject
        {
            var strs = Selection.assetGUIDs;

            var path = AssetDatabase.GUIDToAssetPath(strs[0]);

            if (path.Contains("."))
            {
                path=path.Substring(0, path.LastIndexOf('/'));
            }

            if (!Directory.Exists(path))
                Directory.CreateDirectory(path);

            var name = path + "/"+defaultName;
            while (File.Exists(name + ".asset"))
                name += "(1)";

            AssetDatabase.CreateAsset(CreateInstance<T>(), name + ".asset");
            AssetDatabase.Refresh();

            Selection.activeObject = AssetDatabase.LoadAssetAtPath<T>(name + ".asset");
        }

        public static Type GetType(string fullTypeName)
        {
            Assert.IsFalse(string.IsNullOrEmpty(fullTypeName));
            return Type.GetType(fullTypeName);
        }
    }
#endif

}


text:
fileFormatVersion: 2
guid: 21a33949f0674b0cace820fb6c4fef22
timeCreated: 1606576049


text:
fileFormatVersion: 2
guid: f085acbc596c4c8da63e8b2451bbcc82
timeCreated: 1565440710


text:
fileFormatVersion: 2
guid: fbefcdc349a44f3bb98ece8749644401
timeCreated: 1588646587


text:
fileFormatVersion: 2
guid: b7363acd403c4231af558c620b9733d3
timeCreated: 1606199739


text:
using System;
using UnityEngine;

namespace ReadyGamerOne.Utility
{
    public static class GUILayoutUtil
    {
        public static float Slider(string text, float value, float left, float right, GUIStyle style=null,string format="0.00",float sliderLength=100)
        {
            GUILayout.BeginHorizontal();
            GUILayout.Label($"{text}\t【", style);
            value =Convert.ToSingle(GUILayout.TextField($"{value.ToString(format)}",30, style));
            value = Mathf.Clamp(value, left, right);
            GUILayout.Label("】", style);
            value = GUILayout.HorizontalSlider(value, left, right, GUILayout.Height(style.fontSize),GUILayout.Width(sliderLength));
            GUILayout.EndHorizontal();
            return value;
        }
    }
}


text:
fileFormatVersion: 2
guid: 2f84f19c33c746be9780458170a77e88
timeCreated: 1569671285


text:
fileFormatVersion: 2
guid: 0e5294be01984535b367de969e25d704
timeCreated: 1565691435


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

namespace ReadyGamerOne.Utility
{
    public static class FileUtil
    {
        #region 目录操作

        /// <summary>
        /// 当前可执行文件路径
        /// </summary>
        public static string CurrentRunDirectory => Environment.CurrentDirectory;
        /// <summary>
        /// 获取父级目录
        /// </summary>
        /// <param CharacterName="pathNum"></param>
        /// <returns></returns>
        public static string GetRunDirectoryInParentPath(int pathNum=3)
        {
            //    /Server/Server/bin/Debug/server.exe
            DirectoryInfo pathInfo = Directory.GetParent(CurrentRunDirectory);
            while (pathNum > 0 && pathInfo.Parent != null)
            {
                var info = pathInfo.Parent;
                pathNum--;
            }
            //返回一个完整的文件夹路径
            return pathInfo.FullName;
        }
        /// <summary>
        /// 创建文件夹
        /// </summary>
        /// <param CharacterName="path"></param>
        /// <returns></returns>
        public static string CreateFolder(string path)
        {
            //如果目录存在则创建
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }
            return path;

        }
        /// <summary>
        /// 创建目录
        /// </summary>
        /// <param CharacterName="path"></param>
        /// <param CharacterName="foldName"></param>
        /// <returns></returns>
        public static string CreateFolder(string path,string foldName)
        {
            var fullPath = path + "//" + foldName;
            if (!Directory.Exists(fullPath))
                Directory.CreateDirectory(fullPath);
            return fullPath;
        }

        /// <summary>
        /// 获取目录名
        /// </summary>
        /// <param name="dirFullPath"></param>
        /// <returns></returns>
        public static string GetDirName(string dirFullPath)
        {
            return new DirectoryInfo(dirFullPath).Name;
        }

        /// <summary>
        /// 获取父目录名
        /// </summary>
        /// <param name="dirFullPath"></param>
        /// <returns></returns>
        public static string GetParentDirName(string dirFullPath)
        {
            return Directory.GetParent(dirFullPath).Name;
        }

        #endregion

        /// <summary>
        /// 创建文件（初始化文本）
        /// </summary>
        /// <param CharacterName="path"></param>
        /// <param CharacterName="fileName"></param>
        /// <param CharacterName="info"></param>
        public static void CreateFile(string path,string fileName,string info=null)
        {
            CreateFolder(path);
            var fileInfo = new FileInfo(path + "//" + fileName);
            if (fileInfo.Exists)
                fileInfo.Delete();
            var streamWriter = fileInfo.CreateText();

            if(!string.IsNullOrEmpty(info))
                streamWriter.WriteLine(info);

            streamWriter.Close();
            streamWriter.Dispose();
        }
        public static void CreateFile(string fullPath, byte[] data)
        {
            //Create the Directory if it does not exist
            if (!Directory.Exists(Path.GetDirectoryName(fullPath)))
            {
                Directory.CreateDirectory(Path.GetDirectoryName(fullPath));
            }
            try
            {
                File.WriteAllBytes(fullPath, data);
            }
            catch (Exception e)
            {
                Debug.LogWarning("Failed To Save Data to: " + fullPath.Replace("/", "\\"));
                Debug.LogWarning("Error: " + e.Message);
            }
        }

        /// <summary>
        /// 向文件中添加信息
        /// </summary>
        /// <param CharacterName="path"></param>
        /// <param CharacterName="fileName"></param>
        /// <param CharacterName="info"></param>
        public static void AddInfoToFile(string path,string fileName,string info)
        {
            CreateFolder(path);

            var fileInfo = new FileInfo(path + "//" + fileName);

            var streamWriter = !fileInfo.Exists ? fileInfo.CreateText() : fileInfo.AppendText();

            streamWriter.WriteLine(info);

            streamWriter.Close();

            streamWriter.Dispose();

        }

        /// <summary>
        /// 读取文件
        /// </summary>
        /// <param CharacterName="path"></param>
        /// <param CharacterName="fileName"></param>
        /// <returns></returns>
        public static string LoadFile(string path,string fileName)
        {
            if (!FileExist(path, fileName))
                return null;

            if (!path.EndsWith("/"))
                path = path + "/";

            var streamReader = new StreamReader(path + fileName);

            var arr = new ArrayList();

            while (true)
            {
                var line = streamReader.ReadLine();
                if (line == null)
                    break;
                arr.Add(line);
            }
            streamReader.Close();
            streamReader.Dispose();
            string ans = "";
            foreach (var str in arr)
                ans += str;
            return ans;
        }

        /// <summary>
        /// 判断文件是否存在
        /// </summary>
        /// <param CharacterName="path"></param>
        /// <param CharacterName="fileName"></param>
        /// <returns></returns>
        public static bool FileExist(string path,string fileName)
        {
            if (!Directory.Exists(path))
                return false;
            if (!File.Exists(path + "//" + fileName))
                return false;
            return true;
        }

        /// <summary>
        /// 根据字典生成常量类
        /// </summary>
        /// <param name="className">常量类名</param>
        /// <param name="generateDir">生成目录</param>
        /// <param name="nameSpace">常量类命名空间</param>
        /// <param name="content">常量名和值组成的字典</param>
        public static void CreateConstClassByDictionary(string className, string generateDir, string nameSpace,
            Dictionary<string, string> content)
        {
            var constClassFullPath = generateDir + "/"+className+".cs";
            if (!Directory.Exists(generateDir))
                Directory.CreateDirectory(generateDir);
            if(File.Exists(constClassFullPath))
                File.Delete(constClassFullPath);
            var stream = new StreamWriter(constClassFullPath);

            var ns = string.IsNullOrEmpty(nameSpace) ? "DefaultNameSpace" : nameSpace;

            stream.Write("namespace "+ns+"\n" +
                         "{\n" +
                         "\tpublic partial class "+className+ "\n" +
                         "\t{\n");

            foreach (var kv in content)
            {
                stream.Write("\t\tpublic const string "+kv.Key+" = @\""+kv.Value+"\";\n");
            }

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

        public static string FileNameToVarName(string fileName)
        {
            var ans = fileName;
            ans = ans.Replace(" ", "");
            ans = ans.Replace("`", "");
            ans = ans.Replace("-", "_");
            return ans;
        }

        /// <summary>
        /// 创建一个常量类，常量类里存放的是指定目录下的文件名，用同名变量保存
        /// </summary>
        /// <param name="className">常量文件名</param>
        /// <param name="generateDir">生成目录</param>
        /// <param name="dirWhichContainsFiles">包含目标文件的目录</param>
        /// <param name="nameSpace">常量类的命名空间</param>
        /// <param name="onOperateFile">操作文件的委托</param>
        /// <param name="deepSearch">是否递归搜索</param>
        public static void ReCreateFileNameConstClassFromDir(string className, string generateDir,string dirWhichContainsFiles, string nameSpace,Action<FileInfo,StreamWriter> onOperateFile=null,bool deepSearch=false)
        {
            var classFilePath = generateDir + "/"+className+".cs";
            if(File.Exists(classFilePath))
                File.Delete(classFilePath);
            var stream = new StreamWriter(classFilePath);

            var ns = string.IsNullOrEmpty(nameSpace) ? "DefaultNameSpace" : nameSpace;

            stream.Write("namespace "+ns+"\n" +
                         "{\n" +
                         "\tpublic partial class "+className+ "\n" +
                         "\t{\n");

            if(null==onOperateFile)
                SearchDirectory(dirWhichContainsFiles, fileInfo =>
                {
                    if (!fileInfo.Name.EndsWith(".meta"))
                    {
                        var fileName = Path.GetFileNameWithoutExtension(fileInfo.Name);
                        var name = FileNameToVarName(fileName);
                        stream.Write("\t\tpublic const string "+name+" = @\""+fileName+"\";\n");
                    }
                },deepSearch);
            else
                SearchDirectory(dirWhichContainsFiles,fileInfo=>onOperateFile(fileInfo,stream),deepSearch);

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

        /// <summary>
        /// 遍历目录，对其中文件执行操作
        /// </summary>
        /// <param name="startDirPath">起始目录</param>
        /// <param name="onOperateFile">操作文件的函数</param>
        /// <param name="deepSearch">是否递归搜索，这样会进入每一个子目录里面递归搜索</param>
        /// <param name="onoperateDir">操作目录的函数</param>
        /// <exception cref="Exception"></exception>
        public static void SearchDirectory(
            string startDirPath,
            Action<FileInfo> onOperateFile,
            bool deepSearch = false,
            Func<DirectoryInfo,bool> onoperateDir=null)
        {
            if (!Directory.Exists(startDirPath))
                throw new Exception("起始文件夹不存在");

            bool ifSearch = true;
            if(onoperateDir!=null && Directory.Exists(startDirPath))
                ifSearch = onoperateDir.Invoke(new DirectoryInfo(startDirPath));

            foreach (var info in Directory.GetFileSystemEntries(startDirPath))
            {
                var fullPath = info;
                if (Directory.Exists(fullPath))
                {
                    ifSearch = ifSearch && deepSearch;
                    if (!ifSearch)
                        continue;
                    SearchDirectory(fullPath,onOperateFile,deepSearch,onoperateDir);
                }
                else //否则就是文件
                {
                    onOperateFile?.Invoke(new FileInfo(fullPath));
                }
            }
        }

        /// <summary>
        /// 创建空的类文件
        /// </summary>
        /// <param name="className">类名</param>
        /// <param name="nameSpace">命名空间</param>
        /// <param name="generateDir">生成目录</param>
        /// <param name="parentClass">父类名</param>
        /// <param name="helpTips">注释</param>
        /// <param name="fileContent">类内容</param>
        /// <param name="autoOverwrite">是否自动覆盖重写</param>
        public static void CreateClassFile(string className, string nameSpace, string generateDir,
            string parentClass = null, string helpTips = null, string fileContent = null,
            bool autoOverwrite = false, bool partical = false, string usingStatements = null,bool isAbstract=false,
            string otherClassBody=null)
        {
            CreateFolder(generateDir);
            var fullPath = generateDir + "/" + className + ".cs";
            if (File.Exists(fullPath))
            {
                if (autoOverwrite)
                    File.Delete(fullPath);
                else
                    return;
            }

            var stream = File.CreateText(fullPath);

            if (string.IsNullOrEmpty(usingStatements) == false)
                stream.Write(usingStatements);

            stream.Write("namespace " + nameSpace + "\n" +
                         "{\n");

            if (!string.IsNullOrEmpty(helpTips))
                stream.Write("\t/// <summary>\n" +
                             "\t/// " + helpTips + "\n" +
                             "\t/// </summary>\n");

            stream.Write("\tpublic ");
            if (partical)
                stream.Write("partial ");
            if (isAbstract)
                stream.Write("abstract ");

            stream.Write("class " + className);

            if (!string.IsNullOrEmpty(parentClass))
                stream.Write(" : " + parentClass);
            stream.Write("\n" +
                         "\t{\n");
            if (!string.IsNullOrEmpty(fileContent))
                stream.Write(fileContent);

            stream.Write("\t}\n");
            if (!string.IsNullOrEmpty(otherClassBody))
                stream.Write(otherClassBody);
            stream.Write("}\n");

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

    }
}


text:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using UnityEngine.Assertions;
using Debug = UnityEngine.Debug;

namespace ReadyGamerOne.Utility
{
    public class NetUtil
    {
        /// <summary>
        /// 获取空闲端口
        /// </summary>
        /// <returns></returns>
        public static int GetUdpPort()
        {
            int port = -1;
            for (var i = 8000; i <= 12000; i++)
            {
                if (IsPortOccupiedFunc(i) == false)
                {
                    port = i;
                    break;
                }
            }
            return port;
        }

        /// <summary>
        /// 判断指定端口号是否被占用
        /// </summary>
        /// <param name="port"></param>
        /// <returns></returns>
        private static bool IsPortOccupiedFunc(int port)
        {
            bool result = false;
            try
            {
                var p = new Process
                {
                    StartInfo = new ProcessStartInfo("netstat", "-an")
                    {
                        CreateNoWindow = true,
                        UseShellExecute = false,
                        WindowStyle = ProcessWindowStyle.Hidden,
                        RedirectStandardOutput = true
                    }
                };
                p.Start();
                string output = p.StandardOutput.ReadToEnd().ToLower();
                string ip1 = "127.0.0.1";
                string ip2 = "0.0.0.0";
                System.Net.IPAddress[] addressList = Dns.GetHostEntry(Dns.GetHostName()).AddressList;
                var ipList = new List<string> {ip1, ip2};
                ipList.AddRange(addressList.Select(t => t.ToString()));

                if (ipList.Any(t => output.IndexOf(t + ":" + port.ToString(), StringComparison.Ordinal) >= 0))
                {
                    result = true;
                }
            }
            catch (Exception ex)
            {
                UnityEngine.Debug.Log("NetUtil.IsPortOccupedFun1 port :" + ex);
            }
            return result;
        }

        /// <summary>
        /// 获取页面html
        /// </summary>
        /// <param name="url">请求的地址</param>
        /// <param name="encoding">编码方式</param>
        /// <returns></returns>
        public static void HttpGetPageHtmlAsync(string url, string encoding,Action<string> onFinish)
        {
            using (var webClient = new WebClient())
            {
                var encode = Encoding.GetEncoding(encoding);
                webClient.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36");
                webClient.Credentials = CredentialCache.DefaultCredentials;//获取或设置用于向Internet资源的请求进行身份验证的网络凭据
                webClient.DownloadDataAsync(new Uri(url));
                webClient.DownloadDataCompleted += (sender, args) =>
                {
                    onFinish(encode.GetString(args.Result));
                };
            }
        }

        /// <summary>
        /// 从html中通过正则找到ip信息(只支持ipv4地址)
        /// </summary>
        /// <param name="pageHtml"></param>
        /// <returns></returns>
        public static string GetIpFromHtml(string pageHtml)
        {
            //验证ipv4地址
            string reg = @"(?:(?:(25[0-5])|(2[0-4]\d)|((1\d{2})|([1-9]?\d)))\.){3}(?:(25[0-5])|(2[0-4]\d)|((1\d{2})|([1-9]?\d)))";
            string ip = "";
            Match m = Regex.Match(pageHtml, reg);
            if (m.Success)
            {
                ip = m.Value;
            }
            return ip;
        }

        /// <summary>
        /// 获取当前Ip
        /// </summary>
        /// <param name="url"></param>
        /// <param name="encoding"></param>
        /// <returns></returns>
        public static void GetCurrentIpv4Async(
            Action<string> onUseIp,
            string url="http://www.net.cn/static/customercare/yourip.asp",
            string encoding="gbk")
        {
            HttpGetPageHtmlAsync(url, encoding,
                content =>
                {
                    onUseIp(GetIpFromHtml(content));
                });
        }

        /// <summary>
        /// 获取本机所有ip地址
        /// </summary>
        /// <param name="netType">"InterNetwork":ipv4地址，"InterNetworkV6":ipv6地址</param>
        /// <returns>ip地址集合</returns>
        public static List<string> GetLocalIpAddress(AddressFamily netType)
        {
            string hostName = Dns.GetHostName();     //获取主机名称
            IPAddress[] addresses = Dns.GetHostAddresses(hostName); //解析主机IP地址

            List<string> IPList = new List<string>();

            for (int i = 0; i < addresses.Length; i++)
            {
                if (addresses[i].AddressFamily == netType)
                {
                    IPList.Add(addresses[i].ToString());
                }
            }
            return IPList;
        }

        private class UdpSetupWorker
        {
            private int curIndex = 0;
            private List<string> ipList;
            private Socket socket;
            private int localPort;
            private IPEndPoint targetIpEndPoint;
            public bool IsFinished { get; private set; } = false;
            public UdpSetupWorker(IPEndPoint targetIpEndPoint, int localPort)
            {
                this.localPort = localPort;
                this.targetIpEndPoint = targetIpEndPoint;
                ipList=GetLocalIpAddress(AddressFamily.InterNetwork);
            }

            public void Start(byte[] initBytes, Action recvThread, Action<string, Socket, Thread> onGetSuitableIp)
            {
                if (curIndex >= ipList.Count)
                {
                     IsFinished = true;
                     onGetSuitableIp(null,null,null);
                     return;
                }

                var curIp = ipList[curIndex++];
                var curPort = NetUtil.GetUdpPort();
                Assert.IsNull(this.socket);

                this.socket=new Socket(
                 AddressFamily.InterNetwork,
                 SocketType.Dgram,
                 ProtocolType.Udp);
                //绑定本地端口
                var localPoint = new IPEndPoint(IPAddress.Parse(curIp), curPort);
                this.socket.Bind(localPoint);

                try
                {
                 this.socket.BeginSendTo(initBytes, 0, initBytes.Length, SocketFlags.None,
                     targetIpEndPoint,
                     (iar) =>
                     {
                         bool error = false;
                         try
                         {
                             this.socket.EndSendTo(iar);
                         }
                         catch
                         {
                             // Debug.Log($"[CallBack][CurIndex:{curIndex-1}][{curIp}:{curPort}]{e}");
                             error = true;
                         }
                         finally
                         {
                             if (!error)
                             {
                                 IsFinished = true;
                                 var thread = new Thread(new ThreadStart(recvThread));
                                 onGetSuitableIp(curIp, this.socket, thread);
                                 thread.Start();
                             }
                             else
                             {
                                 socket.Dispose();
                                 socket.Close();
                                 socket = null;
                                 Start(initBytes, recvThread, onGetSuitableIp);
                             }
                         }
                     }, null);
                }
                catch (Exception e)
                {
                    Debug.LogWarning($"[BeginSendTo][CurIndex:{curIndex-1}][{curIp}:{curPort}]{e}");
                    Start(initBytes,recvThread,onGetSuitableIp);
                }
            }
        }
        public static void SetupUdp(IPEndPoint targetIpEndPoint, int localPort,Action recvThread, Action<string, Socket, Thread> onSetUpUdp, byte[] initBytes=null)
        {
            new UdpSetupWorker(targetIpEndPoint, localPort).Start(initBytes ?? new byte[0], recvThread, onSetUpUdp);
        }
    }
}


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


text:
using UnityEngine;

namespace ReadyGamerOne.Utility
{
    public static class GizmosUtil
    {
        public static void DrawSign(Vector3 pos,float signalSize=1.0f)
        {
            Gizmos.DrawLine(pos + Vector3.left * signalSize, pos + Vector3.right * signalSize);
            Gizmos.DrawLine(pos + Vector3.up * signalSize, pos + Vector3.down * signalSize);
        }

        public static void DrawRect(Rect rect,Vector3 pos)
        {
            var p = pos + new Vector3(rect.position.x, rect.position.y, 0);
            var h = rect.height;
            var w = rect.width;

            Gizmos.DrawLine(p, p + new Vector3(w, 0, 0));
            Gizmos.DrawLine(p, p + new Vector3(0, h, 0));
            Gizmos.DrawLine(p+new Vector3(w,h,0), p + new Vector3(w, 0, 0));
            Gizmos.DrawLine(p+new Vector3(w,h,0), p + new Vector3(0, h, 0));
        }

        public static void DrawRect(Rect rect)=>DrawRect(rect,Vector3.zero);

        public static void DrawBoxCollider2D(BoxCollider2D collider2D)
        {
            if (collider2D == null)
            {
                Debug.LogError("BoxCollider2D 为空");
                return;
            }

            var offset = collider2D.offset;
            var size = collider2D.size;
            var scale = collider2D.transform.localScale;
            var startPos = collider2D.transform.position;

            var c = startPos + new Vector3(scale.x * offset.x, scale.y * offset.y);

            var height = size.y * scale.y;
            var width = size.x * scale.x;

            var lt = c + new Vector3(-width / 2, height / 2);
            Gizmos.DrawLine(lt, lt + new Vector3(width, 0, 0));
            Gizmos.DrawLine(lt + new Vector3(0, -height, 0), lt + new Vector3(width, -height, 0));
            Gizmos.DrawLine(lt, lt + new Vector3(0, -height, 0));
            Gizmos.DrawLine(lt + new Vector3(width, 0, 0), lt + new Vector3(width, -height, 0));
        }
    }
}


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


text:
using System.Text;

namespace ReadyGamerOne.Utility
{
    public static class BitUtil
    {
        public static string ToString(byte[] data, int length, int start=0)
        {
            var sb=new StringBuilder();
            for(var i=start;i<length;i++)
            {
                sb.Append(data[i].ToString());
            }

            return sb.ToString();
        }
    }
}


text:
using System;
using System.Collections.Generic;
using ReadyGamerOne.Script;
using UnityEngine;

namespace ReadyGamerOne.Utility
{
    /// <summary>
    /// 输入工具类——注意，使用此类，需要将MainLoop放在所有脚本执行顺序之前
    /// 封装双击，长按方法
    /// </summary>
    public static class InputUtil
    {
        #region Private

        private enum KeyState
        {
            Null,   //持续状态    无输入
            Down_1, //单帧状态    第一次按下
            Down_,  //持续状态    第一次按下不松开
            Up_1,   //单帧状态    第一次松开
            Up_,    //持续状态    第一次按下并松开
            Down_2, //单帧状态    第二次按下
            Down_Down_, //持续状态    两次按下，第二下不松开
            Up_2,   //单帧状态    第二次松开
            Up_Up_  //持续状态    第二次松开
        }

        private class KeyInfo
        {
            public KeyState State;
            public float timeBetweenLastTwoClick;
            public float timeAfterLastUp;
            public float timeAfterLastDown;
            public float preTimeAfterLastDown;
        }

        static InputUtil()
        {
            MainLoop.Instance.AddUpdateFunc(FixedUpdate);
            keyDic.Add(KeyCode.U, new KeyInfo());
        }

        private static Dictionary<KeyCode, KeyInfo> keyDic = new Dictionary<KeyCode, KeyInfo>();
        private static Dictionary<int, KeyInfo> mouseDic = new Dictionary<int, KeyInfo>();

        private static void FixedUpdate()
        {
            foreach (var item in keyDic)
            {
                #region 处理状态变化

                #region 主动输入导致的状态变化

                if (Input.GetKeyDown(item.Key))
                {
                    switch (item.Value.State)
                    {
                        case KeyState.Null:
                            item.Value.State = KeyState.Down_1;
                            break;
                        case KeyState.Up_:
                        case KeyState.Up_1:
                        case KeyState.Up_2:
                        case KeyState.Up_Up_:
                            item.Value.State = KeyState.Down_2;
                            break;
                    }
                }
                else if (Input.GetKeyUp(item.Key))
                {
                    switch (item.Value.State)
                    {
                        case KeyState.Null:
                            throw new Exception("不该出现这个情况，异常按键：" + item.Key);
                        case KeyState.Down_:
                        case KeyState.Down_1:
                            item.Value.State = KeyState.Up_1;
                            break;
                        case KeyState.Down_2:
                        case KeyState.Down_Down_:
                            item.Value.State = KeyState.Up_2;
                            break;
                    }
                }

                #endregion

                #region 没有输入时，单帧状态切换到持续状态

                else
                {
                    switch (item.Value.State)
                    {
                        case KeyState.Down_1:
                            item.Value.State = KeyState.Down_;
                            break;
                        case KeyState.Down_2:
                            item.Value.State = KeyState.Down_Down_;
                            break;
                        case KeyState.Up_1:
                            item.Value.State = KeyState.Up_;
                            break;
                        case KeyState.Up_2:
                            item.Value.State = KeyState.Up_Up_;
                            break;
                    }
                }

                #endregion

                #endregion

                #region 执行本状态的逻辑

                switch (item.Value.State)
                {
                    case KeyState.Null:
                        item.Value.timeAfterLastUp = 0;
                        item.Value.timeBetweenLastTwoClick = 0;
                        break;
                    case KeyState.Down_1:
                        item.Value.preTimeAfterLastDown = item.Value.timeAfterLastDown;
                        item.Value.timeAfterLastDown = 0;
                        break;
                    case KeyState.Down_2:
                        item.Value.preTimeAfterLastDown = item.Value.timeAfterLastDown;
                        item.Value.timeAfterLastDown = 0;
                        item.Value.timeBetweenLastTwoClick = item.Value.timeAfterLastUp;
                        break;
                    case KeyState.Up_1:
                    case KeyState.Up_2:
                        item.Value.timeAfterLastUp = 0;
                        break;

                    case KeyState.Up_:
                    case KeyState.Up_Up_:
                    case KeyState.Down_:
                    case KeyState.Down_Down_:
                        item.Value.timeAfterLastUp += Time.deltaTime;
                        item.Value.timeAfterLastDown += Time.deltaTime;
                        break;
                }

                #endregion
            }

            foreach (var item in mouseDic)
            {
                #region 处理状态变化

                #region 主动输入导致的状态变化

                if (Input.GetMouseButtonDown(item.Key))
                {
                    switch (item.Value.State)
                    {
                        case KeyState.Null:
                            item.Value.State = KeyState.Down_1;
                            break;
                        case KeyState.Up_:
                        case KeyState.Up_1:
                        case KeyState.Up_2:
                        case KeyState.Up_Up_:
                            item.Value.State = KeyState.Down_2;
                            break;
                    }
                }
                else if (Input.GetMouseButtonUp(item.Key))
                {
                    switch (item.Value.State)
                    {
                        case KeyState.Null:
                            throw new Exception("不该出现这个情况，异常按键：" + item.Key);
                        case KeyState.Down_:
                        case KeyState.Down_1:
                            item.Value.State = KeyState.Up_1;
                            break;
                        case KeyState.Down_2:
                        case KeyState.Down_Down_:
                            item.Value.State = KeyState.Up_2;
                            break;
                    }
                }

                #endregion

                #region 没有输入时，单帧状态切换到持续状态

                else
                {
                    switch (item.Value.State)
                    {
                        case KeyState.Down_1:
                            item.Value.State = KeyState.Down_;
                            break;
                        case KeyState.Down_2:
                            item.Value.State = KeyState.Down_Down_;
                            break;
                        case KeyState.Up_1:
                            item.Value.State = KeyState.Up_;
                            break;
                        case KeyState.Up_2:
                            item.Value.State = KeyState.Up_Up_;
                            break;
                    }
                }

                #endregion

                #endregion

                #region 执行本状态的逻辑

                switch (item.Value.State)
                {
                    case KeyState.Null:
                        item.Value.timeAfterLastUp = 0;
                        item.Value.timeBetweenLastTwoClick = 0;
                        break;
                    case KeyState.Down_1:
                        item.Value.preTimeAfterLastDown = item.Value.timeAfterLastDown;
                        item.Value.timeAfterLastDown = 0;
                        break;
                    case KeyState.Down_2:
                        item.Value.preTimeAfterLastDown = item.Value.timeAfterLastDown;
                        item.Value.timeAfterLastDown = 0;
                        item.Value.timeBetweenLastTwoClick = item.Value.timeAfterLastUp;
                        break;
                    case KeyState.Up_1:
                    case KeyState.Up_2:
                        item.Value.timeAfterLastUp = 0;
                        break;

                    case KeyState.Up_:
                    case KeyState.Up_Up_:
                    case KeyState.Down_:
                    case KeyState.Down_Down_:
                        item.Value.timeAfterLastUp += Time.deltaTime;
                        item.Value.timeAfterLastDown += Time.deltaTime;
                        break;
                }

                #endregion
            }
        }

        #endregion

        /// <summary>
        /// 是否双击按下某个键
        /// </summary>
        /// <param name="key"></param>
        /// <param name="timeBetweenLastTwoClick"></param>
        /// <returns></returns>
        public static bool GetDoubleKeyDown(KeyCode key, float timeBetweenLastTwoClick)
        {
            if (!keyDic.ContainsKey(key))
            {
                keyDic.Add(key, new KeyInfo());
                return false;
            }

            var info = keyDic[key];
            var ans = info.State == KeyState.Down_2 && info.timeBetweenLastTwoClick <= timeBetweenLastTwoClick;
            return ans;
        }

        /// <summary>
        /// 是否双击按下某个鼠标键
        /// </summary>
        /// <param name="mouseButton"></param>
        /// <param name="timeBetweenLastTwoClick"></param>
        /// <returns></returns>
        public static bool GetMouseDoubleDown(int mouseButton, float timeBetweenLastTwoClick)
        {
            if (!mouseDic.ContainsKey(mouseButton))
            {
                mouseDic.Add(mouseButton, new KeyInfo());
                return false;
            }

            var info = mouseDic[mouseButton];
            var ans = info.State == KeyState.Down_2 && info.timeBetweenLastTwoClick <= timeBetweenLastTwoClick;
            return ans;
        }

        /// <summary>
        /// 是否双击松开某个键
        /// </summary>
        /// <param name="key"></param>
        /// <param name="timeBetweenLastTwoClick"></param>
        /// <returns></returns>
        public static bool GetDoubleKeyUp(KeyCode key, float timeBetweenLastTwoClick)
        {
            if (!keyDic.ContainsKey(key))
            {
                keyDic.Add(key, new KeyInfo());
                return false;
            }

            var info = keyDic[key];
            return info.State == KeyState.Up_2 && info.timeBetweenLastTwoClick <= timeBetweenLastTwoClick;
        }

        /// <summary>
        /// 获取鼠标双击抬起
        /// </summary>
        /// <param name="mouseButton"></param>
        /// <param name="timeBetweenLastTwoClick"></param>
        /// <returns></returns>
        public static bool GetMouseDoubleUp(int mouseButton, float timeBetweenLastTwoClick)
        {
            if (!mouseDic.ContainsKey(mouseButton))
            {
                mouseDic.Add(mouseButton, new KeyInfo());
                return false;
            }

            var info = mouseDic[mouseButton];

            return info.State == KeyState.Up_2 && info.timeBetweenLastTwoClick <= timeBetweenLastTwoClick;
        }

        /// <summary>
        /// 是否双击按住某个按键，并且距离最后一次按下时间间隔不足timeAfterLastDown
        /// </summary>
        /// <param name="key"></param>
        /// <param name="timeBetweenLastTwoClick"></param>
        /// <param name="timeAfterLastDown"></param>
        /// <returns></returns>
        public static bool GetDoubleKey(KeyCode key, float timeBetweenLastTwoClick, float timeAfterLastDown)
        {
            if (!keyDic.ContainsKey(key))
            {
                keyDic.Add(key, new KeyInfo());
                return false;
            }

            var info = keyDic[key];

            var ans = info.State == KeyState.Down_Down_ && info.timeBetweenLastTwoClick <= timeBetweenLastTwoClick
                && info.timeAfterLastDown<=timeAfterLastDown;

            return ans;
        }

        /// <summary>
        /// 是否双击按住某个鼠标键，并且距离最后一次按下时间间隔不足timeAfterLastDown
        /// </summary>
        /// <param name="key"></param>
        /// <param name="timeBetweenLastTwoClick"></param>
        /// <param name="timeAfterLastDown"></param>
        /// <returns></returns>
        public static bool GetDoubleMouse(int mouseButton, float timeBetweenLastTwoClick, float timeAfterLastDown)
        {
            if (!mouseDic.ContainsKey(mouseButton))
            {
                mouseDic.Add(mouseButton, new KeyInfo());
                return false;
            }

            var info = mouseDic[mouseButton];
            var ans = info.State == KeyState.Down_Down_ && info.timeBetweenLastTwoClick <= timeBetweenLastTwoClick
                                                        && info.timeAfterLastDown<=timeAfterLastDown;

            return ans;
        }

        /// <summary>
        /// 是否双击按住某个键
        /// </summary>
        /// <param name="key"></param>
        /// <param name="timeBetweenLastTwoClick"></param>
        /// <returns></returns>
        public static bool GetDoubleKey(KeyCode key, float timeBetweenLastTwoClick)
        {
            if (!keyDic.ContainsKey(key))
            {
                keyDic.Add(key, new KeyInfo());
                return false;
            }

            var info = keyDic[key];

            var ans = info.State == KeyState.Down_Down_ && info.timeBetweenLastTwoClick <= timeBetweenLastTwoClick;

            return ans;
        }

        /// <summary>
        /// 是否双击按住某个鼠标键
        /// </summary>
        /// <param name="mouseButton"></param>
        /// <param name="timeBetweenLastTwoClick"></param>
        /// <returns></returns>
        public static bool GetDoubleMouse(int mouseButton, float timeBetweenLastTwoClick)
        {
            if (!mouseDic.ContainsKey(mouseButton))
            {
                mouseDic.Add(mouseButton, new KeyInfo());
                return false;
            }

            var info = mouseDic[mouseButton];
            var ans = info.State == KeyState.Down_Down_ && info.timeBetweenLastTwoClick <= timeBetweenLastTwoClick;

            return ans;
        }

        /// <summary>
        /// 获取按键长按，只触发一次
        /// </summary>
        /// <param name="key"></param>
        /// <param name="time"></param>
        /// <returns></returns>
        public static bool GetLongKeyDown(KeyCode key, float time)
        {
            if (!keyDic.ContainsKey(key))
            {
                keyDic.Add(key, new KeyInfo());
                return false;
            }

            var info = keyDic[key];
            return Math.Abs(info.timeAfterLastDown - time) < 0.01f &&
                (info.State == KeyState.Down_ || info.State == KeyState.Down_Down_);
        }

        /// <summary>
        /// 获取鼠标长按，只触发一次
        /// </summary>
        /// <param name="mouseButton"></param>
        /// <param name="time"></param>
        /// <returns></returns>
        public static bool GetLongMouseDown(int mouseButton, float time)
        {
            if (!mouseDic.ContainsKey(mouseButton))
            {
                mouseDic.Add(mouseButton, new KeyInfo());
                return false;
            }

            var info = mouseDic[mouseButton];

            return Math.Abs(info.timeAfterLastDown - time) < 0.01f &&
                   (info.State == KeyState.Down_ || info.State == KeyState.Down_Down_);
        }

        /// <summary>
        /// 获取按键长按以后的抬起，只触发一次
        /// </summary>
        /// <param name="key"></param>
        /// <param name="time">长按时长</param>
        /// <returns></returns>
        public static bool GetLongKeyUp(KeyCode key, ref float time)
        {
            if (!keyDic.ContainsKey(key))
            {
                keyDic.Add(key, new KeyInfo());
                return false;
            }
            var info = keyDic[key];

            if (info.State != KeyState.Up_1 && info.State != KeyState.Up_2) return false;

            time = info.timeAfterLastDown;
            return true;

        }

        /// <summary>
        /// 获取鼠标长按以后的抬起，只触发一次
        /// </summary>
        /// <param name="key"></param>
        /// <param name="time">长按时长</param>
        /// <returns></returns>
        public static bool GetLongMouseUp(int mouseButton, ref float time)
        {
            if (!mouseDic.ContainsKey(mouseButton))
            {
                mouseDic.Add(mouseButton, new KeyInfo());
                return false;
            }

            var info = mouseDic[mouseButton];

            if (info.State != KeyState.Up_1 && info.State != KeyState.Up_2)
                return false;

            time = info.timeAfterLastDown;
            return true;
        }

        /// <summary>
        /// 获取按键长按，时间超过time后，持续触发
        /// </summary>
        /// <param name="key"></param>
        /// <param name="time"></param>
        /// <returns></returns>
        public static bool GetLongKey(KeyCode key, float time)
        {
            if (!keyDic.ContainsKey(key))
            {
                keyDic.Add(key, new KeyInfo());
                return false;
            }

            var info = keyDic[key];
            return info.timeAfterLastDown >= time &&
                   (info.State == KeyState.Down_ || info.State == KeyState.Down_Down_);
        }

        /// <summary>
        /// 获取按键长按，持续触发
        /// </summary>
        /// <param name="key"></param>
        /// <param name="time">从按下到现在的时间</param>
        /// <returns></returns>
        public static bool GetLongKey(KeyCode key, ref float time)
        {
            if (!keyDic.ContainsKey(key))
            {
                keyDic.Add(key, new KeyInfo());
                return false;
            }

            var info = keyDic[key];
            if (info.State == KeyState.Down_ || info.State == KeyState.Down_Down_)
            {
                time = info.timeAfterLastDown;
                return true;
            }

            return false;
        }

        /// <summary>
        /// 获取鼠标长按，持续触发
        /// </summary>
        /// <param name="mouseButton"></param>
        /// <param name="time"></param>
        /// <returns></returns>
        public static bool GetLongMouse(int mouseButton, float time)
        {
            if (!mouseDic.ContainsKey(mouseButton))
            {
                mouseDic.Add(mouseButton, new KeyInfo());
                return false;
            }

            var info = mouseDic[mouseButton];
            return info.timeAfterLastDown >= time &&
                   (info.State == KeyState.Down_ || info.State == KeyState.Down_Down_);
        }

        /// <summary>
        /// 获取鼠标长按，持续触发
        /// </summary>
        /// <param name="key"></param>
        /// <param name="time">从按下到现在的时间</param>
        /// <returns></returns>
        public static bool GetLongMouse(int mouseButton, ref float time)
        {
            if (!mouseDic.ContainsKey(mouseButton))
            {
                mouseDic.Add(mouseButton, new KeyInfo());
                return false;
            }

            var info = mouseDic[mouseButton];
            if (info.State == KeyState.Down_ || info.State == KeyState.Down_Down_)
            {
                time = info.timeAfterLastDown;
                return true;
            }

            return false;
        }

        /// <summary>
        /// 获取按键频繁按下
        /// </summary>
        /// <param name="key"></param>
        /// <param name="maxIntervalTime">两次按下不得超过的最大允许间隔</param>
        /// <returns></returns>
        public static bool GetFrequentKey(KeyCode key, float maxIntervalTime)
        {
            if (!keyDic.ContainsKey(key))
            {
                keyDic.Add(key, new KeyInfo());
                return false;
            }

            var info = keyDic[key];
            return Math.Abs(info.preTimeAfterLastDown) > 0.01f && info.timeAfterLastDown <= maxIntervalTime &&
                   info.preTimeAfterLastDown <= maxIntervalTime;
        }

        /// <summary>
        /// 获取鼠标键频繁按下
        /// </summary>
        /// <param name="mouseButton"></param>
        /// <param name="maxIntervalTime">两次按下不得超过的最大允许间隔</param>
        /// <returns></returns>
        public static bool GetFrequentMouse(int mouseButton, float maxIntervalTime)
        {
            if (!mouseDic.ContainsKey(mouseButton))
            {
                mouseDic.Add(mouseButton, new KeyInfo());
                return false;
            }

            var info = mouseDic[mouseButton];
            return Math.Abs(info.preTimeAfterLastDown) > 0.01f && info.timeAfterLastDown <= maxIntervalTime &&
                   info.preTimeAfterLastDown <= maxIntervalTime;
        }

        private static void ModifyKeyInfo(KeyCode key, KeyInfo info)
        {
            if (Input.GetKeyUp(key))
            {
                switch (info.State)
                {
                    case KeyState.Null:
                        throw new Exception("不该出现这个情况，异常按键：" + key);
                    case KeyState.Down_:
                    case KeyState.Down_1:
                        info.State = KeyState.Up_1;
                        break;
                    case KeyState.Down_2:
                    case KeyState.Down_Down_:
                        info.State = KeyState.Up_2;
                        break;
                }
            }
        }

    }
}


text:
fileFormatVersion: 2
guid: 6e8665a0a19a4190844b8fa0897daf9f
timeCreated: 1613359449


text:
fileFormatVersion: 2
guid: 52d3d078493947838512ef67d3445913
timeCreated: 1613404397


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

namespace ReadyGamerOne.Utility
{
    /// <summary>
    /// 枚举工具类
    /// 提供，枚举值，枚举名字，枚举Index之间的互相转化
    /// </summary>
    public static class EnumUtil
    {
        #region Private

        private class EnumInfo
        {
            public string EnumName;
            public List<int> values = new List<int>();
            public List<string> names = new List<string>();

            public EnumInfo(Type type)
            {
                this.EnumName = type.Name;
                foreach (var name in Enum.GetNames(type))
                {
                    names.Add(name);
                }

                foreach (var value in Enum.GetValues(type))
                {
                    values.Add((int)value);
                }
            }
        }

        private static Dictionary<Type, EnumInfo> _enumInfos = new Dictionary<Type, EnumInfo>();

        private static EnumInfo GetEnumInfo(Type type)
        {
            if (_enumInfos.ContainsKey(type))
                return _enumInfos[type];

            var info=new EnumInfo(type);
            _enumInfos.Add(type, info);
            return info;
        }

        #endregion

        public static int GetEnumIndex<T>(T enumVar)
        {
            var index = 0;
            foreach (var VARIABLE in GetEnumInfo(typeof(T)).names)
            {
                if (VARIABLE == enumVar.ToString())
                    return index;
                index++;
            }
            throw new Exception("获取枚举Index异常");
        }
        public static int GetEnumValue<T>(int index)
        {
            return GetEnumInfo(typeof(T)).values[index];
        }

        public static List<string> GetNames<T>()
        {
            return GetEnumInfo(typeof(T)).names;
        }

        public static List<int> GetValues<T>()
        {
            return GetEnumInfo(typeof(T)).values;
        }

        public static string GetEnumName<T>(int index)
        {
            return GetEnumInfo(typeof(T)).names[index];
        }

        public static void Clear()
        {
            _enumInfos.Clear();
        }
    }
}


text:
fileFormatVersion: 2
guid: 52ae382c77a94a01a44ba921ef77ba78
timeCreated: 1577168978


text:
namespace ReadyGamerOne.Utility
{
    public static class ObjectUtil
    {
        public static void SwapReference<T>(ref T a, ref T b)
            where T:class
        {
            var temp = a;
            a = b;
            b = temp;
        }
    }
}


text:
fileFormatVersion: 2
guid: a5d4de74cf894404aacf2cd9036d6983
timeCreated: 1615994811


text:
using UnityEngine;

namespace ReadyGamerOne.Utility
{
    public static class UnityAPI
    {
        private static bool s_IsLocked = false;
        public static bool IsLocked => s_IsLocked;
        public static void LockMouse(bool visible=false,bool lockPos=true)
        {
            s_IsLocked = true;
            Cursor.visible = visible;
            Cursor.lockState = lockPos ? CursorLockMode.Locked : CursorLockMode.None;
        }

        public static void FreeMouse()
        {
            s_IsLocked = false;
            Cursor.visible = true;
            Cursor.lockState = CursorLockMode.None;
        }
    }
}


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


text:
using System;
using UnityEngine;

namespace ReadyGamerOne.Utility
{
    public static class UiUtil
    {
        /// <summary>
        /// 世界坐标转UI坐标
        /// </summary>
        /// <param name="worldPos"></param>
        /// <param name="uiCanvas"></param>
        /// <param name="uiCamera"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static Vector2 WorldPointToAnchoredPosition(Vector3 worldPos,Canvas uiCanvas=null,  Camera uiCamera = null)
        {
            var camera = uiCamera ?? Camera.main;
            if (!camera)
                throw new Exception("Camera is null");

            var canvas = uiCanvas ?? ReadyGamerOne.Global.GlobalVar.GCanvasObj?.GetComponent<Canvas>();
            if (!canvas)
                throw new Exception("Canvas is null");

            //得到画布的尺寸
            var uisize = canvas.GetComponent<RectTransform>().sizeDelta;

            //将世界坐标转换为屏幕坐标
            var screenpos = camera.WorldToScreenPoint(worldPos);

            //转换为以屏幕中心为原点的屏幕坐标
            var screenpos2 = new Vector2(
                screenpos.x - (Screen.width / 2),
                screenpos.y - (Screen.height / 2));

            //得到UGUI的anchoredPosition
            return new Vector2(
                (screenpos2.x / Screen.width) * uisize.x,
                (screenpos2.y / Screen.height) * uisize.y);
        }
    }
}


text:
using UnityEngine;

namespace ReadyGamerOne.Utility
{
    public static class MathUtil
    {
        /// <summary>
        /// 正四面体四个顶点方向向量。一个顶点在x轴，一条棱垂直x轴
        /// </summary>
        public static readonly Vector3[] RegularTetrahedronVertexDir;
        static MathUtil()
        {
            var bottomY = -1 / (2 * Mathf.Sqrt(2));
            var halfSide = Mathf.Sqrt(3) / 2;
            RegularTetrahedronVertexDir = new[]
            {
                Vector3.up,
                new Vector3(1, bottomY, 0).normalized,
                new Vector3(-0.5f, bottomY, halfSide).normalized,
                new Vector3(-0.5f, bottomY, -halfSide).normalized
            };
        }
    }
}


text:
using System;
using UnityEngine;

namespace ReadyGamerOne.Utility
{
    public static class ConvertUtil
    {
        public static Vector3 String2Vector3(string str)
        {
            if (string.IsNullOrEmpty(str))
                throw new Exception("字符串为空");
            var s = str.Substring(1, str.Length - 2);
            if(s.Length<=3)
                throw new Exception("所含float数据个数小于三");
            var arr = s.Split(',');
            return new Vector3(Convert.ToSingle(arr[0]),Convert.ToSingle(arr[1]),Convert.ToSingle(arr[2]));

        }
    }
}


text:
fileFormatVersion: 2
guid: ea03cf12fdcd4e95ab7de34a2178dbf3
timeCreated: 1617546091


text:
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="VcsDirectoryMappings">
    <mapping directory="$PROJECT_DIR$/../../../../../.." vcs="Git" />
  </component>
</project>


text:
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="ChangeListManager">
    <list default="true" id="05412a6f-24f0-480a-b6ed-13427cea13c7" name="Default Changelist" comment="" />
    <option name="SHOW_DIALOG" value="false" />
    <option name="HIGHLIGHT_CONFLICTS" value="true" />
    <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
    <option name="LAST_RESOLUTION" value="IGNORE" />
  </component>
  <component name="Git.Settings">
    <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/../../../../../.." />
  </component>
  <component name="HighlightingSettingsPerFile">
    <setting file="file://$PROJECT_DIR$/../../../../PurificationPioneer/Script/Managers/PurificationPioneerMgr.cs" root0="SKIP_HIGHLIGHTING" />
    <setting file="file://$PROJECT_DIR$/MathUtil.cs" root0="SKIP_HIGHLIGHTING" />
  </component>
  <component name="ProjectId" id="1qbhakzSBE5o2qoktAdW8tCw9Nv" />
  <component name="ProjectLevelVcsManager" settingsEditedManually="true" />
  <component name="ProjectViewState">
    <option name="hideEmptyMiddlePackages" value="true" />
    <option name="showLibraryContents" value="true" />
  </component>
  <component name="PropertiesComponent">
    <property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
    <property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
    <property name="WebServerToolWindowFactoryState" value="false" />
    <property name="vue.rearranger.settings.migration" value="true" />
  </component>
  <component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
  <component name="TaskManager">
    <task active="true" id="Default" summary="Default task">
      <changelist id="05412a6f-24f0-480a-b6ed-13427cea13c7" name="Default Changelist" comment="" />
      <created>1617354265714</created>
      <option name="number" value="Default" />
      <option name="presentableId" value="Default" />
      <updated>1617354265714</updated>
      <workItem from="1617354278116" duration="12000" />
      <workItem from="1617354363798" duration="216000" />
    </task>
    <servers />
  </component>
  <component name="TypeScriptGeneratedFilesManager">
    <option name="version" value="3" />
  </component>
  <component name="UnityProjectConfiguration" hasMinimizedUI="false" />
  <component name="UnityUnitTestConfiguration" currentTestLauncher="NUnit" />
  <component name="Vcs.Log.Tabs.Properties">
    <option name="TAB_STATES">
      <map>
        <entry key="MAIN">
          <value>
            <State />
          </value>
        </entry>
      </map>
    </option>
    <option name="oldMeFiltersMigrated" value="true" />
  </component>
  <component name="VcsManagerConfiguration">
    <option name="CLEAR_INITIAL_COMMIT_MESSAGE" value="true" />
  </component>
</project>


text:
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="ContentModelStore">
    <e p="C:\Users\张泽龙\AppData\Local\JetBrains\Rider2020.3\extResources" t="IncludeRecursive" />
    <e p="C:\Users\张泽龙\AppData\Local\JetBrains\Rider2020.3\resharper-host\Local\Transient\Rider\v203\SolutionCaches\_.19.00" t="ExcludeRecursive" />
    <e p="C:\zerlenzhang\Projects\Unity\distributed-architecture-of-moba-game-server\PurificationPioneer\Assets\3rd\ReadyGamerOne\Utility\Utils" t="IncludeFlat" />
  </component>
</project>


text:
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="ProjectModuleManager">
    <modules>
      <module fileurl="file://$PROJECT_DIR$/.idea/.idea.Utils.dir/.idea/riderModule.iml" filepath="$PROJECT_DIR$/.idea/.idea.Utils.dir/.idea/riderModule.iml" />
    </modules>
  </component>
</project>


text:
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="RiderProjectSettingsUpdater">
    <option name="vcsConfiguration" value="2" />
  </component>
</project>


text:
<?xml version="1.0" encoding="UTF-8"?>
<module type="RIDER_MODULE" version="4">
  <component name="NewModuleRootManager">
    <content url="file://$MODULE_DIR$/../.." />
    <orderEntry type="sourceFolder" forTests="false" />
  </component>
</module>


text:
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="ContentModelUserStore">
    <attachedFolders />
    <explicitIncludes />
    <explicitExcludes />
  </component>
</project>


text:
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
</project>


text:
using UnityEngine;

namespace ReadyGamerOne.Utility
{
    public static class ColorExtension
    {
        public static bool EqualsTo(this Color self, Color other,float tolerance=0.01f)
        {
            return Mathf.Abs(self.r - other.r) <= tolerance
                   && Mathf.Abs(self.g - other.g) <= tolerance
                   && Mathf.Abs(self.b - other.b) <= tolerance
                   && Mathf.Abs(self.a - other.a) <= tolerance;
        }
    }
}


text:
using UnityEditor;
using UnityEngine;

namespace ReadyGamerOne.Utility
{
    /// <summary>
    /// Rect 拓展类，用于编辑器拓展
    /// </summary>
    public static class RectExtension
    {

        #region 上下左右四部分

       public static Rect GetUp(this Rect rect,float end=.5f,float start=.0f)
       {
           return new Rect(rect.position+new Vector2(0,rect.size.y*start),
               new Vector2(rect.size.x, rect.size.y* (end-start)));
       }

       public static Rect GetBottom(this Rect rect, float end = .5f, float start = .0f)
       {
           return new Rect(rect.position + new Vector2(0, rect.size.y * (1 - end)),
               new Vector2(rect.size.x, rect.size.y * (end - start)));
       }

       public static Rect GetRight(this Rect rect,float end = 0.5f,float start=0.0f)
       {
           return new Rect(rect.position + new Vector2(rect.size.x * (1-end), 0),
               new Vector2(rect.size.x * (end-start), rect.size.y));
       }

       public static Rect GetLeft(this Rect rect,float end =0.5f,float start=0.0f)
       {
           return new Rect(rect.position+new Vector2(rect.size.x*start,0),
               new Vector2(rect.size.x * (end-start), rect.size.y));
       }

        #endregion

        #region 获取指定行矩形区域
#if UNITY_EDITOR
        public static Rect GetRectAtIndex(this Rect rect, int index)
        {
            return new Rect(rect.position + new Vector2(0, index * EditorGUIUtility.singleLineHeight),
                new Vector2(rect.size.x, EditorGUIUtility.singleLineHeight));
        }

        public static Rect GetRectFromIndex(this Rect rect, int index)
        {
            return new Rect(rect.position+new Vector2(0,index*EditorGUIUtility.singleLineHeight),
                new Vector2(rect.width,rect.height-index*EditorGUIUtility.singleLineHeight));

        }

        public static Rect GetRectFromIndexWithHeight(this Rect rect, ref int index, float height)
        {

            var ans= new Rect(rect.position + new Vector2(0,  index * EditorGUIUtility.singleLineHeight),
                new Vector2(rect.size.x, height));

            index += Mathf.CeilToInt(height / EditorGUIUtility.singleLineHeight);

            return ans;
        }

        public static Rect GetRightPartAtIndex(this Rect rect, int index)
        {
            return rect.GetRectAtIndex(index).GetRight();
        }

        public static Rect GetLeftPartAtIndex(this Rect rect, int index)
        {
            return rect.GetRectAtIndex(index).GetLeft();
        }
#endif

        #endregion

    }
}


text:
using UnityEngine;

namespace ReadyGamerOne.Utility
{
    public static class BoxCollider2DExtension
    {
        public static void GetCorners(this BoxCollider2D collider2D,out Vector3[] corners)
        {
            var offset = collider2D.offset;
            var size = collider2D.size;
            var scale = collider2D.transform.localScale;
            var startPos = collider2D.transform.position;

            var c = startPos + new Vector3(scale.x * offset.x, scale.y * offset.y);

            var height = size.y * scale.y;
            var width = size.x * scale.x;

            var lt = c + new Vector3(-width / 2, height / 2);

            corners = new[]
            {
                lt + new Vector3(0, -height, 0),
                lt,
                lt + new Vector3(width, 0, 0),
                lt + new Vector3(width, -height, 0)
            };
        }
    }
}


text:
fileFormatVersion: 2
guid: c822b0dbda24458f8662c5d1b9cc1ea2
timeCreated: 1577429907


text:
fileFormatVersion: 2
guid: 3c3600657f3c4eb88fe1973927d77ee9
timeCreated: 1576742815


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


text:
fileFormatVersion: 2
guid: 9f49a4c2052a4b5ab9356de6296212f1
timeCreated: 1582850598


text:
fileFormatVersion: 2
guid: 50c35a08dd3a4264ad2a80766fd72929
timeCreated: 1564147449