在手机上,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);
}