在android-sdk的包中有一个tools工具文件夹,可以双击打开monitor.bat或则是ddms.bat,就会打开一个叫AndroidDeviceMonitor的调试工具,有一个LogCat就是捕捉输出的日志的,他会捕捉android上一切的活动,所以需要新建一个Filter设置Tags为Unity,就可以只看Unity游戏的相关日志了。
在手机上安装好打包后的游戏(开发模式),开启开发者模式的Usb调试,用数据线连接上电脑,然后打开游戏,切换到你游戏的Filter,你就可以看到日志的输出了。
AssetBundle通过Load方法加载资源的时候,是从内存的镜像文件加载成内存的对象,当AssetBundle通过UnLoad(false)卸载资源的时候已经加载出来的资源对象是不会被释放的(只释放镜像内存),需要程序去管理起来了,通过Resource.UnLoad(prefab)卸载掉。
[Test] [UnityPlatform (RuntimePlatform.WindowsPlayer)]//单个 public void TestMethod1() { Assert.AreEqual(Application.platform, RuntimePlatform.WindowsPlayer); } [Test] [UnityPlatform(exclude = new[] { RuntimePlatform.WindowsEditor })]//多个 public void TestMethod2() { Assert.AreEqual(Application.platform, RuntimePlatform.WindowsEditor); }
UnityPlatform指定在哪些平台才会运行这些测试代码。
[Test] public void LogAssertExample() { //指定一些Log的信息 LogAssert.Expect(LogType.Log, "Log message"); //如果输出的信息不是这一句,就会测试不通过 Debug.Log("Log message");//可以通过 Debug.LogError("Error message"); //如果没有下面这一条,上面这一条就会导致测试不通过了 LogAssert.Expect(LogType.Error, "Error message"); }
看注释。
[Test] public void GameObject_CreatedWithGiven_WillHaveTheName() { //一条龙直接往下的 var go = new GameObject("MyGameObject"); Assert.AreEqual("MyGameObject", go.name); } [UnityTest] public IEnumerator TestMono() { //需要等待 yield return new MonoBehaviourTest<MonoSSS>(); } public class MonoSSS : MonoBehaviour,IMonoBehaviourTest { public bool IsTestFinished { get { if (index == 100) return true; return false; } } int index = 0; void Update() { index++; } }
Test的测试方法是挺简单的,一般就是将对应需要测试的类实例化一下,然后调用你要测试的方法,最后Assert断言验证数据的正确性。
UnityTest测试的是一个MonoBehaviour的脚本,MonoBehaviourTest返回的是一个IEnumerator,然后等待MonoBehaviour测试的完成。
怎么识别MonoBehaviour脚本测试的完成,就是实现IMonoBehaviourTest接口的IsTestFinished,返回true就表示测试完了。
Test指定的测试,指定是void的方法,就是一条龙往下的测试代码,中间不需要等待的逻辑性代码,例如计算一些数据,工具类转化数据等等。
UnityTest,指定的是返回IEnumertor的方法,就是协程返回,这样就可以用来表示一些加载完资源后测试代码,加载网络消息的测试方法等等可以拥有等待状态。
Unity Test Runner是集成在Unity里面的单元测试,基于NUnit单元测试。使用方法:
在Unity菜单Window->TestRunner打开窗口。
可以看到分为两种模式,一种是PlayMode,一种是EditMode。
PlayMode是一种运行时测试的,就是你运行测试的时候会自动播放游戏的。如果说需要测试MonoBehaviour的脚本,一定得是PlayMode的。
EditMode就是在编辑器当中就可以运行的了,一些普通的脚本啊,一些扩展编辑器的脚本啊等等。
点击Create Test Assembly Folder,创建一个测试的程序集文件夹,会有一个Assembly Definition文件在文件夹里面,可以指定平台。
随后进入到文件夹里面,Create Test Script in current folder就可以用了,点击它,创建一个测试脚本。
using UnityEngine.TestTools; using NUnit.Framework; using System.Collections; public class NewTestScript { [Test] public void NewTestScriptSimplePasses() { // Use the Assert class to test conditions. } // A UnityTest behaves like a coroutine in PlayMode // and allows you to yield null to skip a frame in EditMode [UnityTest] public IEnumerator NewTestScriptWithEnumeratorPasses() { // Use the Assert class to test conditions. // yield to skip a frame yield return null; } }
可以发现使用到了UnityEngine.TestTools和NUnit.Framework。
这里面有两种特性,Test和UnityTest指定的不同的测试方法,在Unity Test Runner中可以看到了
双击就可以测试对应里面的测试代码了。
将这里生成的cs文件和工具箱中的protobuf-net.dll文件一起导入到Unity中,新建一个测试脚本开始序列化和反序列化测试。
using UnityEngine; using item; using System.IO; using ProtoBuf; public class Test : MonoBehaviour { void Start () { Item i = new Item(); i.Type = 1;i.num = 10;i.SubType = 2; ItemList list = new ItemList(); list.item.Add(i); byte[] data = Serialize(list); Debug.LogError(data.Length);//8 ItemList ll = Deserialize<ItemList>(data); Debug.LogError(ll.item[0].num);//10 } byte[] Serialize(object o) { using (MemoryStream ms = new MemoryStream()) { Serializer.Serialize(ms, o); byte[] result = new byte[ms.Length]; ms.Read(result, 0, result.Length); return result; } } T Deserialize<T>(byte[] data) { using (MemoryStream ms = new MemoryStream(data)) { T t = Serializer.Deserialize<T>(ms); return t; } } }
这里使用的Serializer是ProtoBuff命名空间里面的,主要使用它序列化数据在前面加上协议头(就是命令),然后传输过去给服务端,服务端根据协议头找到对应的结构(同一个.proto生成),反序列化(去除)数据
同样的,客户端也是根据服务端的协议头,找到对应的结构,在通过统一接口ProtoBuf反序列化取出数据。
百度搜索"ProtoBuff C#工具",下载编译好的工具集,当然的也可以自己去git上下载源码自己编译。
一、编写.proto文件,就是用来定义数据结构存储什么东西的。
package kongjian;//生成cs文件的命名空间 //import "other.proto";//可以引入其他的定义文件,两个结构其实可以分开两个文件的 message Item { required int32 Type = 1;//required表示 必须 optional int32 SubType = 2;//optional表示 可选 required int32 num = 3; } message ItemList { repeated Item item = 1;//表示数组,解析后为List }
二、利用工具解析为中间文件(.protodesc)再解析为c#类脚本(.cs)
一个工具是protoc.exe,一个是protogen.exe,我的是进入到工具目录中操作的
三、看看解析成的cs文件类容
namespace item { [global::System.Serializable, global::ProtoBuf.ProtoContract(Name=@"Item")] public partial class Item : global::ProtoBuf.IExtensible { public Item() {} private int _Type; [global::ProtoBuf.ProtoMember(1, IsRequired = true, Name=@"Type", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)] public int Type { get { return _Type; } set { _Type = value; } } private int _SubType = default(int); [global::ProtoBuf.ProtoMember(2, IsRequired = false, Name=@"SubType", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)] [global::System.ComponentModel.DefaultValue(default(int))] public int SubType { get { return _SubType; } set { _SubType = value; } } private int _num; [global::ProtoBuf.ProtoMember(3, IsRequired = true, Name=@"num", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)] public int num { get { return _num; } set { _num = value; } } private global::ProtoBuf.IExtension extensionObject; global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing) { return global::ProtoBuf.Extensible.GetExtensionObject(ref extensionObject, createIfMissing); } } [global::System.Serializable, global::ProtoBuf.ProtoContract(Name=@"ItemList")] public partial class ItemList : global::ProtoBuf.IExtensible { public ItemList() {} private readonly global::System.Collections.Generic.List<Item> _item = new global::System.Collections.Generic.List<Item>(); [global::ProtoBuf.ProtoMember(1, Name=@"item", DataFormat = global::ProtoBuf.DataFormat.Default)] public global::System.Collections.Generic.List<Item> item { get { return _item; } } private global::ProtoBuf.IExtension extensionObject; global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing) { return global::ProtoBuf.Extensible.GetExtensionObject(ref extensionObject, createIfMissing); } } }
四、同理的,利用同一个.proto文件,可以生成不同平台使用,但是数据结构对应上的客户端文件和服务端文件,这就方便了命令数据结构的对应了。
using UnityEngine; public class TestFocus : MonoBehaviour { bool isPaused = true; void OnApplicationFocus(bool hasFocus) { isPaused = !hasFocus; } void OnApplicationPause(bool pauseStatus) { isPaused = pauseStatus; } }
得到焦点(就是其他应用切换回来的时候),先调用Pause(false)再Focus(true)
失去焦点(切换到其他应用),先调用Focus(false)再Pause(true)
Android弹出键盘,会调用Focus(false);Home键不会调用Focus(false),但是会调用Pause(true)