用Unity做个游戏(三) - 日志相关

前言

本来是想接着写UI相关的东西的,不过上一篇提到了SFUtils这个类,干脆就先介绍下有关于日志方面的封装

目的

这个游戏目前的目标平台是PC和Mac,移动平台有网络同步效果方面的顾虑,之后再考虑。

当然这不是主要问题,主要问题是Unity日志实在是太难用了,直接使用Debug.Log()是最常用的方式,然而这样做只会把日志输出到UnityEditor的Console里,实在是有点难看,唯一的好处是双击某一行可以直接跳转到响应的代码行。不过作为外貌协会成员,还是想办法把这个尽量弄的优雅吧。

想法是统一把info,warning,error这些统一为一种日志,全部记录到文件里,这样的话我们翻之前的日志也比较方便,不会丢失。如果配合实时日志查看器的话(之前还开了个坑,不过最近估计没时间填了orz),就简直完美了~

设计

游戏中所有需要输出日志的地方全部使用SFUtils提供的方法。

方法说明
log(string[, int])输出日志,可选日志等级,数字越小越重要,默认为0
logWarning(string)记录一个警告,效果和Debug.LogWarning()相同
logError(string)记录一个错误,效果和Debug.LogError()相同
assert(bool[, string])提供和Debug.Assert()相同的功能,提示信息也会写到文件里

logWarning()相当于等级为-1的普通log,而logError()相当于等级为-2的普通log。

之后log方法会根据日志级别输出到Console(和默认的一样),并格式化输出到文件

1
logStr = " [INFO-" + level.ToString() + "] - " + logStr;

接下来,关键的一步,把上面的logStr追加到日志文件中

1
2
3
4
5
6
7
8
9
10
11
static void logToFile(string msg)
{
DateTime dt = DateTime.Now;
string output = dt.ToString("HH:mm:ss.ff ") + msg;
if (sm_logFileInfo != null && sm_logFileInfo.Exists)
{
var sw = sm_logFileInfo.AppendText();
sw.WriteLine(output);
sw.Close();
}
}

这样的话,我们每次输出日志都会写到同一个文件里了,所以我们要在每次游戏启动的时候新建一个日志文件,把每次运行区分开,以当前时间命名

1
2
3
4
5
6
7
8
9
static public void clearLogFile()
{
DateTime dt = DateTime.Now;
string path = Application.persistentDataPath + "/GameLog" + dt.ToString("yyyyMMddHHmmss") + ".txt";
sm_logFileInfo = new FileInfo(path);
var sw = sm_logFileInfo.CreateText();
sw.WriteLine("[BounceArena] - " + dt.ToString("G"));
sw.Close();
}

杂项

嗯,其实下面这些和日志没啥太大关系了,不过既然都在SFUtils这个类里面,那就顺便都介绍一下吧

获取Unix时间戳

Unity/C#默认连获取当前Unix时间戳的接口都没有0.0只能丰衣足食自己实现一套了

1
2
3
4
5
static public long getTimeStampNow()
{
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
return Convert.ToInt64(ts.TotalSeconds);
}

查找某GameObject下的子物体

根据给出的父节点和子物体的名字,找到这个子物体。需要注意的是,这个方法会消耗相当多的时间,最好不要在update里调用。如果有多个同名的子物体,只会返回第一个找到的结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static public GameObject findChildWithParent(GameObject parent, string childName)
{
Transform parentTrans = parent.transform;
foreach (Transform trans in parentTrans.GetComponentInChildren<Transform>())
{
if (trans.name == childName)
{
return trans.gameObject;
}
else
{
var child = SFUtils.findChildWithParent(trans.gameObject, childName);
if (child != null)
{
return child;
}
}
}
return null;
}

完整代码

上面贴出的代码片段由于篇幅限制只保留了关键部分,完整的代码可在我的github上找到