MVC原理的基础上扩展为AMPVC框架,直接上图上代码
1.IElement接口
public interface IElement
{
/// <summary>
/// 发送通知
/// </summary>
/// <param name="type"></param>
/// <param name="eventType"></param>
/// <param name="p_data"></param>
void SendNotify(Tool.Type type, Tool.EventType eventType, params object[] p_data);
void SendNotify(Tool.Type type, params object[] p_data);
/// <summary>
/// 命令执行
/// </summary>
/// <param name="eventType"></param>
/// <param name="p_data"></param>
void OnExeCute(Tool.EventType eventType, params object[] p_data);
void OnExeCute(params object[] p_data);
}
public class Element : MonoBehaviour,IElement
{
public virtual void OnExeCute(Tool.EventType eventType, params object[] p_data)
{
}
public void OnExeCute(params object[] p_data)
{
}
public void SendNotify(Type type, Tool.EventType eventType, params object[] p_data)
{
this.RetrieveEntity(type).OnExeCute(eventType, p_data);
}
public void SendNotify(Type type, params object[] p_data)
{
this.RetrieveEntity(type).OnExeCute(p_data);
}
}
2.App
public class AppGame : Element
{
private void Awake()
{
#region 注册model代理
(new TestProxy()).Register(Type.TEST_PROXY);
#endregion
#region --- 绑定controller
gameObject.Bind<TestController>(Type.TEST);
#endregion
}
// Start is called before the first frame update
void Start()
{
Application.runInBackground = true;
Application.targetFrameRate = -1;
this.LoadInitConfig();
}
}
public enum Type
{
None,
#region View
TEST_VIEW,
#endregion
#region Controller
TEST,
#endregion
#region model Proxy
TEST_PROXY,
#endregion
}
public enum SocketType
{
None,
HEART, ///心跳检测
}
public enum EventType
{
None,
#region //View 点击事件
Click_Test_Button,
#endregion
#region View function type //View
/// <summary>
/// 测试点击事件反馈事件
/// </summary>
View_Test_Click_Event,
#endregion
#region controller function type //controller
/// <summary>
/// 框架测试
/// </summary>
Command_Test_Click_Event,
}
///容器
public static class AppIOC
{
/// <summary>
/// view、controller、proxy
/// </summary>
private static Dictionary<Type, IElement> GameEventHandlerMap = new Dictionary<Type, IElement>();
/// <summary>
/// 本地配置资源
/// </summary>
private static Dictionary<string, IModel> configModelMap = new Dictionary<string, IModel>();
//设备
private static Dictionary<string, IFacility> devModeMap = new Dictionary<string, IFacility>();
private static void AddHandler(this IElement value, Type type)
{
if (!GameEventHandlerMap.ContainsKey(type)) GameEventHandlerMap.Add(type, value);
}
private static void RemoveHandler(this IElement value, Type type)
{
if (GameEventHandlerMap.ContainsKey(type)) GameEventHandlerMap.Remove(type);
}
public static void Bind<T>(this GameObject gameObject,Type type) where T : BaseController
{
if (gameObject.GetComponent<T>() == null)
gameObject.AddComponent<T>().RegisterController(type);
else
gameObject.GetComponent<T>().RegisterController(type);
}
public static void Register(this IElement value, Type type)
{
AddHandler(value, type);
}
public static void Remove(this IElement value, Type type)
{
RemoveHandler(value, type);
}
public static IElement RetrieveEntity(this IElement value, Type type)
{
if (!GameEventHandlerMap.ContainsKey(type)) return null;
return GameEventHandlerMap[type];
}
public static void AddConfigModel(string key,IModel value)
{
if (!configModelMap.ContainsKey(key))
configModelMap.Add(key, value);
}
public static IModel RetrieveModel(string value)
{
if (!configModelMap.ContainsKey(value)) return null;
return configModelMap[value];
}
//模型注册
public static void OnRegisterFacility(this IFacility value)
{
if (!devModeMap.ContainsKey(value.DevID))
devModeMap.Add(value.DevID, value);
}
public static void OnRemoveFacility(this IFacility value)
{
if (devModeMap.ContainsKey(value.DevID)) devModeMap.Remove(value.DevID);
}
}
3.IModel
public interface IModel
{
}
public class BaseModel : Element
{
}
4.IProxy
public interface IProxy
{
/// <summary>
/// Called by the Model when the Proxy is registered
/// </summary>
void RegisterProxy();
/// <summary>
/// Called by the Model when the Proxy is removed
/// </summary>
void RemoveProxy();
/// <summary>
/// proxy model accept to from net data
/// </summary>
/// <param name="values"></param>
void AcceptNetInfo(string values);
void AcceptNetInfo(Tool.EventType eventType, params object[] p_data);
}
public class BaseProxy : IProxy,IElement
{
public BaseProxy() { }
public BaseProxy(string value){ }
/// <summary>
/// 注册 Proxy
/// </summary>
public virtual void RegisterProxy()
{
}
/// <summary>
/// 注销Proxy
/// </summary>
public virtual void RemoveProxy()
{
}
/// <summary>
/// 接收服务数据
/// </summary>
/// <param name="values"></param>
public virtual void AcceptNetInfo(string values)
{
}
public virtual void AcceptNetInfo(Tool.EventType eventType, params object[] p_data)
{
}
public void OnExeCute(Tool.EventType eventType, params object[] p_data)
{
AcceptNetInfo(eventType, p_data);
}
/// <summary>
/// 接收到的数
/// </summary>
/// <param name="p_data"></param>
public void OnExeCute(params object[] p_data)
{
AcceptNetInfo(p_data[0].ToString());
}
public void SendNotify(Tool.Type type, Tool.EventType eventType, params object[] p_data)
{
this.RetrieveEntity(type).OnExeCute(eventType, p_data);
}
public void SendNotify(Tool.Type type, params object[] p_data)
{
this.RetrieveEntity(type).OnExeCute(p_data);
}
/// <summary>
/// 是否有数据判断
/// </summary>
/// <param name="rows"></param>
/// <returns></returns>
protected bool IsOnRow(IEnumerable<Row> rows)
{
int count = rows.Count(r => !r.IsEmpty());
if (count == 0)
return false;
return true;
}
}
5.IView
(注:可以拆成View 和 Meditor)
public interface IView
{
/// <summary>
/// 注册View
/// </summary>
void RegisterView();
void RemoveView();
//属性数据更新
void OnUpdate(params object[] p_data);
}
public class BaseView : Element,IView
{
/// <summary>
/// Used for locking
/// </summary>
protected readonly object m_syncRoot = new object();
protected virtual void Awake()
{
RegisterView();
}
/// <summary>
/// 注册 View
/// </summary>
public virtual void RegisterView() { }
/// <summary>
/// 移除View
/// </summary>
public virtual void RemoveView(){ }
/// <summary>
/// 数据更新
/// </summary>
/// <param name="p_data"></param>
public virtual void OnUpdate(params object[] p_data){ }
public sealed override void OnExeCute(Tool.EventType eventType, params object[] p_data)
{
HandleNotification(eventType, p_data);
}
/// <summary>
/// 接收从控制层传来的数据
/// </summary>
/// <param name="eventType"></param>
/// <param name="p_data"></param>
protected virtual void HandleNotification(Tool.EventType eventType, params object[] p_data)
{
}
}
public interface IFacility
{
string DevID { get;}
/// <summary>
/// 设备初始化
/// </summary>
void OnInit();
/// <summary>
/// 点击事件
/// </summary>
void OnClick();
/// <summary>
/// 行为
/// </summary>
/// <param name="value"></param>
void OnMotion(params object[] value);
}
public class Facility : BaseView ,IFacility
{
public string Id;
public string DevID { get { return Id; } }
[HideInInspector]
public GameObject obj;
protected override void Awake()
{
base.Awake();
}
protected void RegisterDevView(Facility facility)
{
facility.OnRegisterFacility();
}
public sealed override void RemoveView()
{
this.OnRemoveFacility();
}
protected virtual void Start()
{
obj = this.gameObject;
}
public virtual void OnInit()
{
}
public virtual void OnClick()
{
}
public virtual void OnMotion(params object[] value)
{
}
}
//设置动画
public void SetTrrigerAnimator(Animator _animator, string takeName, string stateName, UnityAction callback)
{
AnimationClip[] AnimationClips = _animator.runtimeAnimatorController.animationClips;
float _time = 0;
for (int i = 0; i < AnimationClips.Length; i++)
{
if (AnimationClips[i].name == takeName)
{
_time = AnimationClips[i].length;
}
}
_animator.SetTrigger(stateName);
StartCoroutine(AnimatorPlayFinished(_time, callback));
}
public void SetTrrigerAnimator(Animator _animator, string takeName, UnityAction callback)
{
AnimationClip[] AnimationClips = _animator.runtimeAnimatorController.animationClips;
float _time = 0;
for (int i = 0; i < AnimationClips.Length; i++)
{
if (AnimationClips[i].name == takeName)
{
_time = AnimationClips[i].length;
}
}
_animator.SetTrigger(takeName);
StartCoroutine(AnimatorPlayFinished(_time, callback));
}
private IEnumerator AnimatorPlayFinished(float time, UnityAction callback)
{
yield return new WaitForSeconds(time + 1);
if (callback != null) callback.Invoke();
}
}
6.IController
public interface IController
{
/// <summary>
/// 注册Controller
/// </summary>
/// <param name="type"></param>
void RegisterController(Type type);
void RemoveController(Type type);
}
public class BaseController : Element,IController
{
public void RegisterController(Type type)
{
this.Register(type);
}
public void RemoveController(Type type)
{
this.Remove(type);
}
public sealed override void OnExeCute(Tool.EventType eventType, params object[] p_data)
{
DoCommand(eventType, p_data);
}
protected virtual void DoCommand(Tool.EventType eventType, params object[] p_data)
{
}
}
7.IServer
public interface IServer
{
/// <summary>
/// 服务get请求
/// </summary>
/// <param name="url"></param>
/// <param name="actionResult"></param>
void Get(string url, Action<bool, string> actionResult);
void Get(string url, string data, Action<bool, string> actionResult);
/// <summary>
/// Post请求
/// </summary>
/// <param name="url"></param>
/// <param name="data"></param>
/// <param name="actionResult"></param>
void Post(string url,string data, Action<bool, string> actionResult);
}
public class HttpServer :MonoSingleton<HttpServer>, IServer
{
public virtual void Get(string url, Action<bool, string> actionResult)
{
StartCoroutine(_Get(url, actionResult));
}
public virtual void Get(string url, string data, Action<bool, string> actionResult)
{
}
public virtual void Post(string url, string data, Action<bool, string> actionResult)
{
StartCoroutine(_Post(url, data, actionResult));
}
/// <summary>
/// GET请求
/// </summary>
/// <param name="url">请求地址</param>
/// <param name="action">请求发起后处理回调结果的委托</param>
/// <returns></returns>
IEnumerator _Get(string url, System.Action<bool, string> actionResult)
{
using (UnityWebRequest quest = UnityWebRequest.Get(url))
{
yield return quest.SendWebRequest();
string msg = "";
if (quest.isNetworkError || quest.isHttpError)
{
msg = quest.error;
}
else
{
msg = quest.downloadHandler.text;
}
if (actionResult != null)
{
actionResult.Invoke(quest.isHttpError, msg);
}
}
}
/// <summary>
/// 向服务器提交post请求
/// </summary>
/// <param name="url">服务器请求目标地址</param>
/// <param name="formData">form表单参数</param>
/// <returns></returns>
IEnumerator _Post(string url, string formData, System.Action<bool, string> actionResult)
{
using (UnityWebRequest quest = UnityWebRequest.Post(url, formData))
{
//发送数据
setWebRequest(quest, formData);
yield return quest.SendWebRequest();
string text = "";
if (!(quest.isNetworkError || quest.isHttpError))
{
text = quest.downloadHandler.text;
}
else
{
text = quest.error;
}
if (actionResult != null)
{
actionResult.Invoke(quest.isHttpError, text);
}
}
}
}
public class SocketClient : Element
{
public static SocketClient Instance;
/// <summary>
/// Saved WebSocket instance
/// </summary>
protected static WebSocket webSocket;
public static WebSocket WebSocket
{
get
{
return webSocket;
}
}
public static bool IsOnScoket = false;
private Coroutine _pingCor, _clientPing, _serverPing;
private bool lockReconnect = false; //表示是否在连接过程中
private string heart = "{\"type\": \"WEB_HEART\"}";
private float timer = 5;
private void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(this.gameObject);
}
else if (Instance != this)
{
Destroy(this.gameObject);
return;
}
}
private void Update()
{
if (IsOnScoket)
{
IsOnScoket = false;
OnConnect();
}
}
private void OnApplicationQuit()
{
OnClose();
}
public void OnConnect()
{
try
{
webSocket = new WebSocket(new System.Uri(ConfigManager.serverUrl));
#if !UNITY_WEBGL
//this.webSocket.StartPingThread = true;
webSocket.StartPingThread = true;
#endif
AddHandle();
// Start connecting to the server
webSocket.Open();
}
catch (Exception e)
{
Debug.Log("WebSocket连接异常:" + e.Message);
ReConnect();
}
}
void OnClose()
{
if (webSocket != null)
{
webSocket.Close();
webSocket = null;
}
}
public void OnSend(string data)
{
if (webSocket == null || string.IsNullOrEmpty(data)) return;
if (webSocket.IsOpen) webSocket.Send(data);
}
void AddHandle()
{
RemoveHandle();
// Subscribe to the WS events
webSocket.OnOpen += OnOpen;
webSocket.OnMessage += OnMessageReceived;
webSocket.OnClosed += OnClosed;
webSocket.OnError += OnError;
}
void RemoveHandle()
{
webSocket.OnOpen -= OnOpen;
webSocket.OnMessage -= OnMessageReceived;
webSocket.OnClosed -= OnClosed;
webSocket.OnError -= OnError;
}
/// <summary>
/// 重连
/// </summary>
void ReConnect()
{
if (this.lockReconnect)
return;
this.lockReconnect = true;
StartCoroutine(SetReConnect());
}
//心跳检测
private void HeartCheck()
{
if (_clientPing != null)
{
StopCoroutine(_clientPing);
_clientPing = null;
}
if (_serverPing != null)
{
StopCoroutine(_serverPing);
_serverPing = null;
}
_clientPing = StartCoroutine(ClientPing());
}
//重连WebSocket
private IEnumerator SetReConnect()
{
Debug.Log("正在重连websocket");
yield return new WaitForSeconds(timer);
OnConnect();
lockReconnect = false;
}
// 这里发送一个心跳,后端收到后,返回一个心跳消息
// onmessage拿到返回的心跳就说明连接正常
private IEnumerator ClientPing()
{
yield return new WaitForSeconds(timer);
this.OnSend(heart);
_serverPing = StartCoroutine(ServerPing());
}
// 如果超过一定时间还没重置,说明后端主动断开了
// 如果onclose会执行reconnect,我们执行ws.close()就行了.如果直接执行reconnect 会触发onclose导致重连两次
private IEnumerator ServerPing()
{
yield return new WaitForSeconds(timer);
webSocket.Close();
}
//发送心跳
private IEnumerator HeartPing()
{
while (true)
{
yield return new WaitForSeconds(timer);
this.OnSend(heart);
}
}
#region 回调
void OnOpen(WebSocket ws)
{
//SendNotify(Tool.Type.SOCKET_PROXY,Tool.EventType.Proxy_Socket_Connected_Event ,ws.Context);
//停止发送心跳
if (_pingCor != null)
{
StopCoroutine(_pingCor);
_pingCor = null;
}
//发送心跳
_pingCor = StartCoroutine(HeartPing());
// 心跳检测重置
HeartCheck();
}
public void OnMessageReceived(WebSocket ws, string message)
{
if (message.Equals("") || message == null) return;
SendNotify(Tool.Type.SOCKET_PROXY,message);
// 拿到任何消息都说明当前连接是正常的
HeartCheck(); // 如果获取到消息,心跳检测重置
}
void OnClosed(WebSocket ws, System.UInt16 code, string message)
{
SendNotify(Tool.Type.SOCKET_PROXY, Tool.EventType.Proxy_Socket_Closed_Event,message);
webSocket = null;
ReConnect();
}
void OnError(WebSocket ws, string error)
{
if (error != null)
Debug.Log("websocket连接异常:" + error);
webSocket = null;
ReConnect();
}
#endregion
}
8.Manager
- 配置管理类
public static class ConfigManager
{
public static void LoadInitConfig(this IElement element)
{
#region //初始化本地配置文件增加处,若有需要的存储,则创建对应Model存入APPIOC.configModelMap容器
}
}
- 工具管理类
//单例类
public class MonoSingleton<T> : MonoBehaviour where T :MonoSingleton<T>
{
#region --单例模式
private static T _instance;
public static T Instance
{
get
{
if(MonoSingleton<T>._instance == null)
MonoSingleton<T>._instance = (T)UnityEngine.Object.FindObjectOfType(typeof(T));
return _instance;
}
}
void Awake()
{
MonoSingleton<T>._instance = (T)this;
Init();
}
#endregion
protected virtual void Init()
{
}
}
public class ExcelManager
{
private static ExcelManager _instance;
public static ExcelManager Instance
{
get
{
if (_instance == null) _instance = new ExcelManager();
return _instance;
}
}
public enum ExcelBookIndex
{
None = -1,
}
//Excel读取
public static void OnLoadXlsx(string path,System.Action<Dictionary<int, IEnumerable<Row>>> action)//xxx.xlsx
{
System.Action<bool, string, byte[]> actionAccept = (IsOn, values, bytes) =>
{
LogLevel.Debug.Log("IsOn = {0} + ", IsOn);
if (IsOn) LogLevel.Debug.Log("{0}={1}", "error", values);
else LoadGuideData(bytes,action);
};
LogLevel.Debug.Log("path = {0}", path);
Net.HttpLoad.Instance.Get(path, actionAccept);
}
static void LoadGuideData(byte[] bytes, System.Action<Dictionary<int, IEnumerable<Row>>> action)
{
Debug.Log("bytes.Length = " + bytes.Length);
//bytes就是加载Excel中文件内容流
if (bytes.Length == 0)
return;
//通过插件的WorkBook类转换得到一个列表,这个列表的大小就表示的是Excel中表的个数。
var book = new WorkBook(bytes);
if (book.Count == 0)
return;
Dictionary<int, IEnumerable<Row>> simulationDic = new Dictionary<int, IEnumerable<Row>>();
for (int i = 0; i < book.Count; i++)
{
simulationDic.Add(i, book[i]);
}
if (action != null) action.Invoke(simulationDic);
}
///文本文档
public static void OnLoadText(string path,System.Action<Dictionary<int, IEnumerable<Row>>> action)
{
}
}
- 事件监听
https://blog.51cto.com/myselfdream/2956754
- 日志管理
public enum LogLevel
{
Debug,
Info,
Warning,
Exception,
Error
}
public static class LogManager
{
public static void Log (this LogLevel level, string templte, params object[] args)
{
string msg = args == null || args.Length == 0 ? templte : string.Format(templte, args);
msg = $"[{level}] Frame={Time.frameCount} Time={Time.time} -- {msg}";
switch (level)
{
case LogLevel.Debug:
case LogLevel.Info:
Debug.Log(msg);
break;
case LogLevel.Warning:
Debug.LogWarning(msg);
break;
case LogLevel.Exception:
Debug.LogException(new System.Exception(msg));
break;
case LogLevel.Error:
Debug.LogError(msg);
break;
}
}
}
9.Test测试脚本
public class TestModel
{
public string TestName { get; set; }
}
public class TestProxy : BaseProxy
{
private TestModel testModel;
public TestProxy()
{
testModel = new TestModel();
}
public TestProxy(string value) : base(value)
{
}
public void OnUpdate(string value)
{
testModel.TestName = value;
}
public TestModel getTestModel()
{
return testModel;
}
}
public class TestView:BaseView{
protected void Start(){
SendNotify(Type.TEST, Tool.EventType.Command_Test_Click_Event, "xxx");
}
protected override void HandleNotification(EventType eventType, params object[] p_data){
switch (eventType)
{
case EventType.View_Test_Click_Event:
Debug.Log(p_data[0].ToString());
break;
}
}
}
public class TestController :BaseController
{
private TestProxy testProxy;
protected override void Awake()
{
testProxy = (TestProxy)this.RetrieveEntity(Type.TEST_PROXY);
}
protected override void DoCommand(Tool.EventType eventType, params object[] p_data)
{
switch (eventType)
{
case Tool.EventType.Command_Test_Click_Event:
UnityAction<string> call = p_data[0] as UnityAction<string>;
//保存数据
testProxy.OnUpdate("run");
//返回数据
call(testProxy.getTestModel().TestName);
SendNotify(Type.TEST_VIEW,Tool.EventType.View_Test_Click_Event, testProxy.getTestModel());
break;
}
}
}