前言:最近做项目,每次新建一个项目都有重新去写一些工具类,有些东西写过了,就想这去之前的项目里面找,可是总是找了好一会才找到,实在是有些费劲;于是我总结了几个常用的工具类,以后做项目的时候直接导入工程使用就行了。
文末分享了一个UnityStriptsTools.unitypackage供大家使用。
一,单例
挂载到游戏物体上的单例实现
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 单例
/// </summary>
public class Singleton<T> : MonoBehaviour where T: Singleton<T>
{
private static T _instance;
public static T Ins
{
get
{
return _instance;
}
}
protected virtual void Awake()
{
_instance = (T)this;
}
protected virtual void OnDestory()
{
_instance = null;
}
}
使用示例:这样就其他脚本中反问时,就不需要通过对应的GameObject.GetComponent<>() 来获取了,直接类名.Ins
这样可以了
示例代码:(记得要挂载到游戏物体上哦)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 单例使用示例
/// 外部访问直接 --> SingletonTest.Ins.Show();
/// </summary>
public class SingletonTest : Singleton<SingletonTest>
{
void Start()
{
//外部访问示例
//SingletonTest.Ins.Show();
}
/// <summary>
/// 外部单例访问测试
/// </summary>
public void Show()
{
}
}
不继承MonoBehaviour
的写法
public class Singleton<T> where T : new()
{
private static T instance;
public static T Instance
{
get
{
if(instance == null)
instance = Activator.CreateInstance<T>();
return Singleton<T>.instance;
}
}
protected Singleton()
{
}
}
二,对象池
通过一个公用脚本来统一管理需要动态加载的物体(如:子弹),这样用起来会方便很多,大量重复调用时也不有频繁生成销毁的销耗。
使用方法:将脚本挂载到一个空物体上,将其作为回收游戏对象的父物体,然后可以结合上面的单例一起使用,详情代码请看注释。
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
/// <summary>
/// 对象池
/// </summary>
public class PoolManager : MonoBehaviour//Singleton<PoolManager>
{
//最大容量 --> 你需要根据实际情况来调整这个数值
private int maxSize = 50;
private List<GameObject> objectPool;
#region 1.使用单例工具类 --> 需要删除"MonoBehaviour"并打开注释 "Singleton<PoolManager>"
//protected override void Awake()
//{
// base.Awake();
// InitPool();
//}
#endregion
#region 2.自给自足,单例初始化
private static PoolManager _instance;
public static PoolManager Ins
{
get
{
return _instance;
}
}
protected virtual void Awake()
{
_instance = (PoolManager)this;
InitPool();
}
protected virtual void OnDestory()
{
_instance = null;
}
#endregion
/// <summary>
/// 初始化对象池
/// </summary>
void InitPool()
{
objectPool = new List<GameObject>();
}
/// <summary>
/// 处理游戏体的所有子物体 -- 注意通过对象池生成的物体不要使用这个方法回收
/// </summary>
/// <param name="tr"></param>
public void Dispose(Transform tr)
{
Transform[] ch = new Transform[tr.childCount];
for (int i = 0; i < ch.Length; i++)
{
ch[i] = tr.GetChild(i);
}
for (int i = 0; i < ch.Length; i++)
{
ch[i].gameObject.SetActive(false);
ch[i].SetParent(transform);
objectPool.Add(ch[i].gameObject);
ch[i] = null;
}
GameObject uselessObj;
while (objectPool.Count > maxSize)
{
uselessObj = objectPool[0];
objectPool.RemoveAt(0);
Destroy(uselessObj);
}
Resources.UnloadUnusedAssets();
}
/// <summary>
/// 加载预制体
/// 参数示例:(transform, "Prefabs/GameObject")
/// </summary>
/// <param name="parent">父物体</param>
/// <param name="path">加载路径</param>
/// <returns></returns>
public GameObject LoadPrefab(Transform parent, string path)
{
string[] nameArr = path.Split('/');
string name = nameArr[nameArr.Length - 1];
GameObject obj = LoadPrefab(path, name);
obj.transform.SetParent(parent);
SetLoaclScale(obj);
obj.gameObject.SetActive(true);
return obj;
}
/// <summary>
/// 加载预制体
/// </summary>
/// <param name="parent">父物体</param>
/// <param name="path">加载路径</param>
/// <returns></returns>
GameObject LoadPrefab(string path, string name)
{
GameObject obj = null;
for (int i = 0; i < objectPool.Count; i++)
{
if (objectPool[i].name == name)
{
obj = objectPool[i];
objectPool.RemoveAt(i);
break;
}
}
if (obj == null)
{
obj = Instantiate(Resources.Load<GameObject>(path));
obj.name = name;
}
return obj;
}
/// <summary>
/// 设置本地缩放
/// </summary>
/// <param name="prefab"></param>
private void SetLoaclScale(GameObject prefab)
{
RectTransform rect = prefab.GetComponent<RectTransform>();
if (rect)
{
rect.anchoredPosition3D = Vector3.zero;
rect.offsetMin = Vector2.zero;
rect.offsetMax = Vector2.zero;
rect.localScale = Vector3.one;
}
else
{
//prefab.transform.localPosition = Vector3.zero;
prefab.transform.localScale = Vector3.one;
}
}
/// <summary>
/// 删除游戏体到对象池
/// </summary>
/// <param name="obj"></param>
public void Dispose(GameObject obj)
{
obj.SetActive(false);
obj.transform.SetParent(transform);
objectPool.Add(obj);
}
/// <summary>
/// 清空对象池
/// </summary>
public void ClearPool()
{
for (int i = 0; i < objectPool.Count; i++)
{
Destroy(objectPool[i]);
}
objectPool.Clear();
}
}
三,公共方法类
记录了几个常用方法:List,Dictionary 转字符串,加载Json,XML文件, 加载Sprite,SpriteAtlas,Material,Texture资源,概率随机数。
可根据实际使用进行增删改。
using System.Collections.Generic;
using System.Text;
using System.Xml;
//using LitJson;
using UnityEngine;
using UnityEngine.U2D;
/// <summary>
/// 公共方法类
/// </summary>
public static class CommandMethod
{
#region List,Dictionary 转字符串
// 需要转换什么类型,转换为什么格式自己重载就可以了
// ** 建议不要写成泛型的方式,为避免装箱拆箱。**
public static string ListToJson(int[] data)
{
StringBuilder content = new StringBuilder();
content.Append("[");
for (int i = 0; i < data.Length; i++)
{
if (i == data.Length - 1)
content.Append(data[i] + "]");
else
content.Append(data[i] + ",");
}
return content.ToString();
}
public static string ListToJson(List<string> list)
{
StringBuilder content = new StringBuilder();
content.Append("[");
for (int i = 0; i < list.Count; i++)
{
content.Append(i + ":" + list[i]);
}
content.Append("]");
return content.ToString();
}
public static string DicToJson(Dictionary<string, string> dictionary)
{
StringBuilder content = new StringBuilder();
content.Append("{");
foreach (var key in dictionary.Keys)
{
content.Append(key + ":" + dictionary[key] + ",");
}
content.Append("}");
return content.ToString();
}
#endregion
#region 加载Json,XML文件
/// <summary>
/// 加载Json文件 --> 需要在工程中添加Litjson.dll文件,否则会报错
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
//public static JsonData LoadJson(string path)
//{
// TextAsset asset = Resources.Load<TextAsset>(path);
// if (!asset)
// {
// Debug.LogError("加载Json文件 错误,路径是:" + path);
// return null;
// }
// // 将读取到的字符串转换为JsonData 返回去
// return JsonMapper.ToObject(asset.text);
//}
/// <summary>
/// 加载XMl文件
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public static string LoadXML(string path)
{
XmlDocument Xdoc = new XmlDocument();
Debug.Log("当前目录是:" + path); //Application.dataPath);
//加载XML 文件
Xdoc.Load(path + ".xml");
//获取跟节点
XmlElement root = Xdoc.DocumentElement;
Debug.Log("根元素是:" + root.Name);
// data : 下一个子节点的名称
//XmlNode dataNode = root.SelectSingleNode("data"); //获取根节点下的子节点
//Debug.Log("节点名称"+dataNode.Name);
StringBuilder content = new StringBuilder();
// 读取到的字符串
for (int i = 0; i < root.ChildNodes.Count; i++)
{
content.Append(root.ChildNodes[i].InnerText + ",");
}
return content.ToString();
}
#endregion
#region 概率随机数
/// <summary>
/// 指定概率随机数
/// 参数示例[10,20,30] -> 解释为数组索引0.1.2产生概率分别为 17%, 33%,50%
/// </summary>
/// <param name="rate">几率数组(%)</param>
/// <returns></returns>
public static int RandArr(int[] rate)
{
// 求和
int total = 1; // 随机数区间 [0,10) 左闭右开,所以初始值设置为1
for (int i = 0; i < rate.Length; i++)
{
total += rate[i];
}
// 求随机
int r = Random.Range(1, total);
int t = 0;
// 计算返回随机数组索引
for (int i = 0; i < rate.Length; i++)
{
t += rate[i];
if (r < t)
{
return i;
}
}
return 0;
}
#endregion
#region 加载Sprite,SpriteAtlas,Material,Texture资源
/// <summary>
/// 加载Sprite
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public static Sprite LoadSprite(string path)
{
return Resources.Load<Sprite>(path);
}
/// <summary>
/// 已加载图集记录
/// </summary>
private static Dictionary<string, SpriteAtlas> m_UISpriteAtlasDic = new Dictionary<string, SpriteAtlas>();
/// <summary>
/// 加载指定路径图集中的指定名称图片
/// </summary>
/// <param name="_atlasPath"></param>
/// <param name="_spriteName"></param>
/// <returns></returns>
public static Sprite LoadSprite(string _atlasPath, string _spriteName)
{
Sprite tempSprite = null;
SpriteAtlas tempAtlas = GetSpriteAtlas(_atlasPath);
Debug.Log(_atlasPath + ".." + (tempAtlas == null));
if (tempAtlas != null) tempSprite = tempAtlas.GetSprite(_spriteName);
return tempSprite;
}
/// <summary>
/// 获取一个图集
/// </summary>
/// <param name="atlasName"></param>
/// <returns></returns>
static SpriteAtlas GetSpriteAtlas(string atlasName)
{
if (m_UISpriteAtlasDic.ContainsKey(atlasName))
{
if (m_UISpriteAtlasDic[atlasName] == null)
m_UISpriteAtlasDic[atlasName] = Resources.Load<SpriteAtlas>(atlasName);
}
else
{
m_UISpriteAtlasDic.Add(atlasName, Resources.Load<SpriteAtlas>(atlasName));
}
return m_UISpriteAtlasDic[atlasName];
}
/// <summary>
/// 加载材质球
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public static Material LoadMaterial(string path)
{
return Resources.Load<Material>(path);
}
/// <summary>
/// 加载贴图
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public static Texture LoadTexture(string path)
{
return Resources.Load<Texture>(path);
}
#endregion
}
四,切换场景不销毁
使用方法:将脚本挂载到,切换场景时不销毁的物体上即可。
using UnityEngine;
/// <summary>
/// 加载时不销毁的对象
/// </summary>
public class DontDestroy : MonoBehaviour
{
private void Awake()
{
DontDestroyOnLoad(gameObject);
}
}
五,自我销毁
定时炸弹,挂载到不同游戏物体上,通过Inspector面板赋值Delay,经过Delay时间后自动销毁。
using UnityEngine;
using System.Collections;
/// <summary>
/// 自我销毁
/// </summary>
public class SelfDestroying : MonoBehaviour
{
// 自动销毁到计时
public float Delay;
void Awake()
{
StartCoroutine(SelfDestroy(Delay));
}
private IEnumerator SelfDestroy(float delay)
{
yield return new WaitForSeconds(delay);
Destroy(gameObject);
}
}
六,进度条
切换场景时的进度条,需要显示%多少的文本,就给m_PassageText赋值,不需要则可以不用管,或者删除相关代码。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class LoadIng : MonoBehaviour
{
// 进度条
public Slider m_slider;
// 进度文本 --> 不需要就删除
public Text m_PassageText;
void Start()
{
//m_slider = this.transform.GetComponent<Slider>();
// 记得修改要加载的场景名称
StartCoroutine(StartLoadingSecne("Main"));
}
private IEnumerator StartLoadingSecne(string sceneName)
{
AsyncOperation op = SceneManager.LoadSceneAsync(sceneName);
op.allowSceneActivation = false;
int curProgressValue = 0;
while (curProgressValue == 90 || op.progress == 0.9) //!op.isDone
{
//progress 的取值范围在0.1 - 1之间, 但是它不会等于1
//也就是说progress可能是0.9的时候就直接进入新场景了
if (curProgressValue < (op.progress * 100))
{
curProgressValue++;
}
SetLoading(curProgressValue);
yield return new WaitForEndOfFrame();
}
// 模拟加载到100%
while (curProgressValue < 100)
{
curProgressValue++;
SetLoading(curProgressValue);
yield return new WaitForEndOfFrame();
}
op.allowSceneActivation = true;
yield return op;
}
void SetLoading(float value)
{
m_slider.value = value / 100f;
if (m_PassageText)
{
m_PassageText.text = "Loading..." + value / 100f + "%";
}
}
}
七,为Image添加监听
为Image,Text 这种没有Button组件的UI添加监听事件,自己继承EventTrigger,实现了一个工具类,使用方法往后看:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class EventTriggerListener : EventTrigger
{
public delegate void VoidDelegate(GameObject go);
public VoidDelegate onClick;
public VoidDelegate onSelect;
public VoidDelegate onBegainDrag;
//public VoidDelegate onDown;
//public VoidDelegate onUp;
public VoidDelegate onPress;
public VoidDelegate DesPress;
static public EventTriggerListener Get(GameObject go)
{
EventTriggerListener listener = go.GetComponent<EventTriggerListener>();
if (listener == null) listener = go.AddComponent<EventTriggerListener>();
return listener;
}
public override void OnPointerClick(PointerEventData eventData)
{
if (onClick != null)
{
onClick(gameObject);
}
}
public override void OnSelect(BaseEventData eventData)
{
if (onSelect != null) onSelect(gameObject);
}
public override void OnBeginDrag(PointerEventData eventData)
{
if (onBegainDrag != null) onBegainDrag(gameObject);
}
public override void OnPointerDown(PointerEventData eventData)
{
if (onPress != null)
{
onPress(gameObject);
}
}
public override void OnPointerUp(PointerEventData eventData)
{
if (DesPress != null)
{
DesPress(gameObject);
}
}
}
使用实例:通过EventTriggerListener给 可交互的UI组件添加点击事件,示例代码如下:
using UnityEngine;
using UnityEngine.UI;
public class EventListenerTest : MonoBehaviour
{
public Text text;
public Image image;
void Start()
{
//添加监听
EventTriggerListener.Get(text.gameObject).onClick = OnButtonClick;
EventTriggerListener.Get(image.gameObject).onClick = OnButtonClick;
//取消监听
// Destroy(EventTriggerListener.Get(image.gameObject).GetComponent<EventTriggerListener>());
}
void OnButtonClick(GameObject go)
{
Debug.Log("OnButtonClick ..." + go.name);
//根据点击不同按钮做指定的事情
switch (go.name)
{
case "Back":
Debug.Log("OnButtonClick ... Back");
break;
case "GoOn":
Debug.Log("OnButtonClick ... GoOn");
break;
}
}
}
八,时间工具类
这个没什么可说的了,根据自己的需求增删改就行了。
using System;
using UnityEngine;
/// <summary>
/// 时间工具类
/// </summary>
public static class TimerTools
{
/// <summary>
/// 获取当前的时间戳(秒)
/// </summary>
/// <returns></returns>
public static long GetNowTime()
{
TimeSpan cha = (DateTime.Now - TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)));
long t = (long)cha.TotalSeconds;
return t;
}
/// <summary>
/// 获取两个时间戳时间间隔(单位秒)
/// </summary>
/// <param name="timestapMax">时间戳1(10位)</param>
/// <param name="timestapMin">时间戳2(10位)</param>
/// <returns></returns>
public static int GetSecondInterval(long timestapMax, long timestapMin)
{
//Debug.Log("时间戳间隔: " + timestapMax + " " + timestapMin);
int resultTime = (int)Mathf.Round((float)((timestapMax - timestapMin)));
return resultTime;
}
/// <summary>
/// 计时时间转 00:00格式
/// </summary>
/// <param name="time">转换时间</param>
/// <param name="showHour">显示小时</param>
/// <returns></returns>
public static string TimerIntToString(int time, bool showHour = false)
{
if (showHour)
{
return (time / 3600 > 9 ? "" : "0") + time / 3600 + ":" + ((time % 3600) / 60 > 9 ? "" : "0") +
(time % 3600) / 60 + ":" + (time % 60 > 9 ? "" : "0") + time % 60;
}
else
{
return (time / 60 > 9 ? "" : "0") + time / 60 + ":" + (time % 60 > 9 ? "" : "0") + time % 60;
}
}
/// <summary>
/// 两个日期是否是同一天
/// </summary>
/// <param name="t1"></param>
/// <param name="t2"></param>
/// <returns></returns>
public static bool IsOneDay(string DateTime1, string DateTime2)
{
DateTime t1 = Convert.ToDateTime(DateTime1);
DateTime t2 = Convert.ToDateTime(DateTime2);
Debug.Log(t1 + ".." + t2 + "---" + t1.Day + ".." + t2.Day);
return (t1.Year == t2.Year && t1.Month == t2.Month && t1.Day == t2.Day);
}
#region 时间戳 和 DateTime 相互转换
/// <summary>
/// DateTime转换为Unix时间戳
/// </summary>
/// <param name="dateTime"></param>
/// <returns></returns>
public static long DateTimeToUnix(DateTime dateTime)
{
DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1)); // 当地时区
// 相差秒数
long timeStamp = (long)(dateTime - startTime).TotalSeconds;
Console.WriteLine();
return timeStamp;
}
/// <summary>
/// Unix时间戳转换为 DateTime
/// </summary>
/// <param name="unixTimeStamp"></param>
/// <returns></returns>
public static string UnixToDateTime(long unixTimeStamp)
{
//long unixTimeStamp = 1478162177;
DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1)); // 当地时区
DateTime dt = startTime.AddSeconds(unixTimeStamp);
Debug.Log(dt.ToString("yyyy/MM/dd HH:mm:ss:ffff"));
return dt.ToString("yyyy/MM/dd HH:mm:ss:ffff");
}
#endregion
}
所有代码的package:下载链接
文件目录:
积分不足的同学,请扫描主页左侧二维码或者V信搜索“开发同学留步”,回复关键字“工具类”即可获得。