using UnityEngine; using UnityEngine.Events; public class CustomWait : CustomYieldInstruction { private UnityAction m_IntervalCallback; private float m_StartTime; private float m_LastTime; private float m_Interval; private float m_Time; public CustomWait(float time, float interval, UnityAction callback) { m_StartTime = Time.time; m_LastTime = Time.time; m_Interval = interval; m_Time = time; m_IntervalCallback = callback; } //判断协成是否继续等待 false为协成结束 public override bool keepWaiting { get { if (Time.time - m_StartTime >= m_Time) { return false; } else if (Time.time - m_LastTime >= m_Interval) { m_LastTime = Time.time; m_IntervalCallback(); } return true; } } } public class TestCustomWait : MonoBehaviour { IEnumerator Start () { yield return new CustomWait(10f, 1f, delegate () { Debug.LogError("自定义的协成等待"); }); } }
继承CustomYieldInstruction,实现keepWaiting接口,根据具体情况返回状态。
脚本挂得越多,执行效率就越低。Unity会需要遍历每一个脚本,然后通过反射调用每个脚本的方法。不可避免的,场景中不止一个脚本,有时候就会遇到执行先后的问题。通过Edit/Project Settings/Script Execution Order,配置具体脚本的执行索引,越小越先执行。
cube t:prefab 搜索名字为cube,类型为prefab的物体
20 t:sprite 搜索名字为20,类型为sprite的物体
l:paths 搜索标签为paths的物体
在手机上,Unity的主线程好像并不能够后台运行的,但是其他线程却可以的。例如接收socket网络包的线程是正常运作的,它把包一个一个的加到队列中,当切换回来游戏的时候,主线程运作就会把一个个网络包取出然后运行起来,这些包一股脑的一下子运行起来,如果有很多个协成特别是会冲突的协成就会一股脑的运行起来,从而造成各种图形错乱逻辑错误的问题。
using System.Collections; using System.Collections.Generic; using System.Threading; using UnityEngine; public class TestBackRunning : MonoBehaviour { Transform go; //模拟网络收包 Queue<Vector3> q = new Queue<Vector3>(); void Awake () { go = GameObject.Find("Cube").transform; Application.runInBackground = false; //设置为后台不运行,模拟手机上的情况 Thread t = new Thread(RunThread); t.Start(); } void RunThread() { //在一个线程中往队列里添加网路包 q.Enqueue( Vector3.up ); Thread.Sleep(5000); q.Enqueue( Vector3.down); Thread.Sleep(3000); q.Enqueue( Vector3.left ); Thread.Sleep(1000); q.Enqueue(Vector3.up); } IEnumerator Up() { Debug.LogError("运行上"); int frame = 10; for (int i = 0; i < frame; i++) { transform.position = new Vector3(transform.position.x, transform.position.y + 0.1f, transform.position.z); yield return new WaitForEndOfFrame(); } } IEnumerator Down() { Debug.LogError("运行下"); int frame = 20; for (int i = 0; i < frame; i++) { transform.position = new Vector3(transform.position.x, transform.position.y - 0.1f, transform.position.z); yield return new WaitForEndOfFrame(); } } IEnumerator Left() { Debug.LogError("运行左"); int frame = 10; for (int i = 0; i < frame; i++) { transform.position = new Vector3(transform.position.x + 0.1f, transform.position.y, transform.position.z); yield return new WaitForEndOfFrame(); } } private void Update() { while (q.Count > 0) { Vector3 dir = q.Dequeue(); if (dir == Vector3.up) { StartCoroutine(Up()); } else if (dir == Vector3.down) { StartCoroutine(Down()); } else if (dir == Vector3.left) { StartCoroutine(Left()); } } } }
在这个测试脚本中,使用一个线程往一个队列里面添加一些命令,然后在Update(主线程中)去取出包来执行,每一个命令都会运行一个协成并且是操作的同一个对象。运行游戏,然后切换到其他窗口,等几秒钟回来之后就会一下子调用三个协成,物体就会往左上方向走,这其实就是已经错乱了,本意是一个协成接一个协成运行的,现在一下子全部运行起来就不行了。
我所想到的方法就是,在后台运行时所需要执行的协成都不执行,都直接运行到协成的结束状态。回来的时候才允许调用协成动画(注意要延迟一帧设置这个允许状态,因为在回来那一帧中,所有的协成(后台运行时需要执行的)都是不允许调用的,一股脑太多的异步会造成混乱,所有都是直接到目标状态的话就变成一个队列的线性执行了,这样就不会造成混乱了)。当然,这样子做的话会造成时间上的不对应,但是也比回来时逻辑错乱的强得多。
private void OnApplicationPause(bool pause) { if (pause) { isShowCoroutine = false; //将当前播放的协成到最终状态,并停止协成的运作 switch (currentCorState) { case CoroutineState.Start: EndStartCoroutine(); break; case CoroutineState.HSZ_CHANGE: EndHSZChangCoroutine(); break; } } else { //可能是暂停了,回来的时候就继续执行,所以延迟设为true StartCoroutine(EnableShowCoroutine()); } } //恢复可以播放动画了 IEnumerator EnableShowCoroutine() { yield return null;//一帧之后在将允许播放协成切换回来 isShowCoroutine = true; ChangeCoroutineState( CoroutineState.OTHER ); } if (isShowCoroutine)//允许播放协成的情况下 { StartCoroutine(GiveCardAni(MyCardCount, cbSiceFirst, cbSiceSecond)); ChangeCoroutineState(CoroutineState.Start); } else { //不然直接设置到目标 EndStartCoroutine(false); }
using System.Collections; using UnityEngine; using UnityEngine.SceneManagement; public class ShapeFactory : MonoBehaviour { Scene poolScene; int loadedLevelBuildIndex = 0; void CreatePools() { //Scene是结构体 if (Application.isEditor) { //通过名字获取场景 poolScene = SceneManager.GetSceneByName(name); if (poolScene.isLoaded)//判断场景是否加载了 { //获取场景中所有的root物体 GameObject[] rootObjects = poolScene.GetRootGameObjects(); return; } } //动态创建一个新场景 poolScene = SceneManager.CreateScene(name); } IEnumerator LoadLevel(int levelBuildIndex) { enabled = false; if (loadedLevelBuildIndex > 0) { //异步卸载index场景 index为buildsetting中右边的数字 yield return SceneManager.UnloadSceneAsync(loadedLevelBuildIndex); } //异步以叠加的方式加载场景 yield return SceneManager.LoadSceneAsync(levelBuildIndex, LoadSceneMode.Additive); //设置这个场景为激活状态,表示实例化的物体放到这个场景下和使用这个场景的灯光设置 SceneManager.SetActiveScene( SceneManager.GetSceneByBuildIndex(levelBuildIndex) ); loadedLevelBuildIndex = levelBuildIndex; enabled = true; } public void SetInScene(GameObject go) { if (poolScene.isLoaded) { //将物体放到某个场景中 SceneManager.MoveGameObjectToScene(go, poolScene); } } }
TextMesh的3D文字会穿透过3D模型是因为使用的是默认的Font Material造成的,因为这个材质使用的是GUI/Text Shader所以会显示在上面,解决办法就是换一个材质和Shader了。
创建一个Shader:
//一个固定管线Shader 会编译为顶点着色Shader Shader "Custom/3D Text Shader" { Properties{ _MainTex("Font Texture", 2D) = "white" {} _Color("Text Color", Color) = (1,1,1,1) } SubShader{ Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" } //只要没有关闭ZTest深度测试,TestMesh就不会出现穿透现象 Lighting Off Cull Off ZWrite On Fog{ Mode Off } Blend SrcAlpha OneMinusSrcAlpha Pass{ Color[_Color] SetTexture[_MainTex]{ combine primary, texture * primary } } } }
创建一个材质使用这个Shader,将材质拖给Mesh Renderer使用,在材质上赋值正确的Font Texture就可以了。Font Texture可以将ttf字体的Character设置为Unicode得到。
今天遇到一个加载Streamed AssetBundle(关卡场景)后加载该场景,加载完成后,调用AssetBundle.Unload(false)卸载镜像文件时,声音文件也被卸载掉了。寻求各种办法都解决不了,最后的办法就是在场景还在是不卸载AssetBundle,在退出场景的时候再调用Assetbundle.Unload(true)卸载全部。
有些情况我们需要使用自定义的宏来做一些东西的开关,例如测试AssetBundle的加载等
private void Awake() { #if TEST_HONG //如果定义了这个宏,就会调用 Debug.LogError("hahaha"); #endif }
定义这个宏,打开File/Build Settings/PlayerSettings/Other Settings/Scripting Define Symbols下的输入框输入宏,用;好区分多个控,按回车键确定,注意你要在哪个平台定义
UNITY_EDITOR | 编辑器下执行的代码 |
UNITY_EDITOR_WIN | Windows平台下编辑器执行 |
UNITY_EDITOR_OSX | OSX平台编辑器执行 |
UNITY_STANDALONE_OSX | Mac主机平台 |
UNITY_STANDALONE_WIN | Win主机平台 |
UNITY_STANDALONE_LINUX | Linux主机平台 |
UNITY_IOS | 苹果手机 |
UNITY_IPHONE | 弃用. 使用UNITY_IOS替代 |
UNITY_ANDROID | 安卓平台调用 |
UNITY_WEBGL | WEBGL平台 |
UNITY_5 | Unity5++版本,所有5.x.x版本 |
UNITY_5_0 | Unity5.0+版本,所有5.0.x版本 |
UNITY_5_0_1 | Unity 5.0.1版本 |
有时候,我们需要public一个二维数组来赋值,就例如很多类型的声音,每种类型又有很多的子声音,这时候就需要二维数组来解决了,可是public的二维数组并不会显示在Inspector中,就不能拖拉式的赋值了。剩下就只能Resource了,或则远程了。
其实我们可以用一个类来充当数据结构,然后声明为可序列化的,里面的数据为数组,而在客户端中声明这个数据结构类为数组,这样就完美解决了。
[Serializable] public class MajiangSAudio { public AudioClip[] audios; public AudioClip[] localAudios; } //使用 public MajiangSAudio[] _ManSound = new MajiangSAudio[40];