本人是从事Unity客户端开发工作的,从本文开始将会记录一些在实际项目中用到的一些通用框架和功能组件,整理完善一套比较适用的基础框架(最近有空闲时间,将以前的项目的代码重构一下,如有不合适的地方请大神们多多指教)
Single单例
之前在设计模式的学习中学习了单例模式,在实际开发中,我们也将经常访问一些全局唯一的数据或者某些操作的入口都是固定的。结合C#的泛型,我们可以设计泛型单例,让其子类直接能够实现单例模式,而不是每一块单例类都需要重新定义。
- 考虑可能会存在一些初始化的情况,因此设计一个IManager接口,根据实际情况来配合泛型使用
public interface IManager
{
bool Init();
}
- 通用泛型单例基类,让其继承IManager接口,留下默认的虚方法进行初始化,如若有特殊的初始化需求可在子类中重写实现。如此,继承Singleton泛型单例的类即可直使用静态对象Instance,继而就能提供全局唯一的访问点,实现单例模式。
/// <remarks>普通单例泛型</remarks>
public abstract class Singleton<T> : IManager where T : new()
{
private static T instance;
public static T Instance
{
get
{
if (instance == null)
{
instance = new T();
}
return instance;
}
}
public virtual bool Init()
{
return true;
}
}
- 在Unity开发中,有和游戏对象GameObject绑定使用的单例需求,因此在设计泛型单例基类时还多了一个MonoSingleton,除了实现单例模式之外,继承自MonoSingleton的子类还需要继承MonoBehaviour的功能。
using UnityEngine;
/// <remarks>继承自Mono的泛型单例</remarks>
public abstract class MonoSingleton<T> : MonoBehaviour, IManager where T : UnityEngine.Component
{
private static T instance;
public static T Instance
{
get
{
if (instance == null)
{
GameObject obj = new GameObject("[" + typeof(T).ToString() + "]");
instance = obj.AddComponent<T>();
GameObject.DontDestroyOnLoad(obj);
if (instance != null)
{
Helper.Log("Singleton init result:[" + typeof(T).ToString() + "]");
}
else
{
Helper.LogError("Instance is null : " + typeof(T).ToString());
}
}
return instance;
}
}
public virtual bool Init()
{
return true;
}
public T GetInstance()
{
return Instance;
}
}
如上代码所示,有个特别注意的地方:为避免在游戏过程中场景切换导致对象被销户,我们需要在基类创建对象时加上一句GameObject.DontDestroyOnLoad(obj);
来保证该单例对象存在于游戏的整个生命周期。
定时器
实现定时器功能有几个关键地方:
1、统一的入口,全局唯一(典型的单例)
2、定时器的计时器Timer,包含定时时间,循环次数以及执行的方法等。其结构也十分简单如下:
3、考虑定时器(Timer)的添加和移除,设计时将采用LinkedList数据结构来存储timer:
- 数组和List、ArrayList集合都有一个重大的缺陷,就是从数组的中间位置删除或插入一个元素需要付出很大的代价,其原因是数组中处于被删除元素之后的所有元素都要向数组的前端移动。Timer肯定会涉及到频繁的添加、删除,使用这种线性存储结构对定时将不是很友好
- LinkedList(底层是由链表实现的)基于链表的数据结构,很好的解决了数组删除插入效率低的问题,且不用动态的扩充数组的长度。启动定时器后主要获取相应的Timer对象,就能很高效的实现移除的操作
using System;
using System.Collections.Generic;
/// <remarks>定时器组件,设置延迟触发</remarks>
public class Timer
{
/// <remarks>记录定时器原始延时数据</remarks>
private float delay;
/// <remarks>
/// 默认状态为1
/// >1时为指定的循环次数,
/// =-1时为无限次重复
/// </remarks>
private short repetTimes;
/// <remarks>真实的剩余时间</remarks>
private float residueTime;
/// <remarks>剩余重复次数</remarks>
private int residueRepetTimes;
public LinkedListNode<Timer> ListNode;
private event System.Action task;
public Timer(float delay, short repetTimes, System.Action task)
{
this.delay = delay;
this.repetTimes = repetTimes;
this.task = task;
this.residueRepetTimes = this.repetTimes;
this.residueTime = this.delay;
}
public void ResetTime()
{
this.residueTime = this.delay;
this.residueRepetTimes = this.repetTimes;
}
public bool Update(float deltaTime)
{
this.residueTime -= deltaTime;
if (this.residueTime <= 0f)
{
try
{
this.task();
}
catch (Exception e)
{
Helper.LogError(e.ToString());
}
if (this.repetTimes > 0)
{
this.residueRepetTimes--;
if (this.residueRepetTimes <= 0)
{
return true;
}
}
else if (this.repetTimes == -1)
{
this.residueTime = this.delay;
}
else
{
return true;
}
}
return false;
}
}
在Timer的刷新逻辑中,如果定时器已经执行完了所有操作返回true,在上层刷新逻辑中,当Timer的刷新返回为true,完成了该组定时器的所有操作移出存储结构单元,因为采用的是链表结构LinkedList,因此这类移除操作是十分高效的。
/// <summary>
/// 定时器刷新 核心逻辑
/// </summary>
private void updateTimers()
{
if (this.timers == null || this.timers.Count <= 0)
{
return;
}
var i = this.timers.First;
while (null != i)
{
var next = i.Next;
var value = i.Value;
if (value.Update(Time.deltaTime))
{
if (value.ListNode != null)
{
this.timers.Remove(value.ListNode);
value.ListNode = null;
}
}
i = next;
}
}
慢慢重构,整套流程重构完成之后将上传所有的源码文件。