主要用到协程(Coroutines)和游戏对象的生命周期(GameObject Lifecycle)基础知识,巧妙解决了游戏重启的问题。

关于协程,这里有篇文章我觉得写的非常好,理解起来也很容易。推荐先看这篇文章:对Unity中Coroutines的理解>>

协程简单来看分三部分:

1)启动,常用方法:StartCoroutine(IEnumerator routine) | StartCoroutine(string methodName)

2)执行,执行的函数其返回值必须为IEnumerator(迭代器)

3)停止,常用方法:StopCoroutine(string methodName) | StopCoroutine(IEnumerator routine) | StopCoroutine(Coroutine routine) | StopAllCoroutines

启动、停止基本上都是对应的方法,有Start就有Stop(StopAllCoroutines比较特殊一点,没有对应的Start)。

那么重点就在执行的函数上,只要用到协程,就不得不提关键词 yield,先看它的语法:

yield return <expression>;
yield break;

(来源:https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/yield)

怎么理解它呢?以yield return null;为例,当执行了协程函数时,遇到yield return <expression>后,本帧就暂停执行后面的语句,然后下一帧再check表达式的返回值,如果为return则继续执行,如果不为return则到下一帧继续检测 —— 这里的前提是下一帧会执行,如果对象被销毁了,下一帧就不执行了,上面说法也就不成立了。

来验证一下上面的说法:

1、如果真的是一帧执行一次,那我就设定Unity的帧率为1(即1秒只执行一次Update);

2、在Start()函数之后,启动一个协程,里面跑一个死循环 while (true),然后输出时间,从控制台上观察其输出;

设定帧率:

1、关闭系统的帧率设定,菜单 Edit –> Project Settings –> Quality

在场景的主相机上挂一个脚本,在 Start()函数中修改帧率

// 修改当前的FPS
Application.targetFrameRate = ;

上面的代码也可以放在场景加载之前的函数里,比如:OnBeforeSceneLoadRuntimeMethod

[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
static void OnBeforeSceneLoadRuntimeMethod()
{
// todo…
    }

2、随便找一个GameObject,在其挂载脚本的Start()函数中启动协程

运行之后,在控制台上可以看到如下的输出,是符合预期的。

注:前三行的输出先忽略,下面会进行说明,关键看后面的输出。

Unity内置了一些yield指令(YieldInstruction):

-)WaitForSeconds

等待指定的游戏时间(游戏流逝时间可用Time.scale调整)

-)WaitForSecondsRealtime

等待指定的真实时间(现实时间不受Time.scale影响)

-)WaitForFixedUpdate

等待下一次的FixedUpdate后再执行

-)WaitForEndOfFrame

等待这一帧(Frame)绘制完但还没有显示的时间点再执行,可以用来取得绘制结果做一些事情

-)WaitUntil

待到传入的 delegate 满足条件返回 true 后再执行

-)WaitWhile

跟WaitUntil差不多,只是传入的 delegate 满足条件返回 false 后再执行,与WaitUntil返回值正好相反

开发游戏,通常我们都需要支持热更新,而腾讯开源的XLua无疑是目前最好的一个选择。既然是热更新,游戏重启我们肯定不希望像安卓那样暴力:

1、先杀掉进程;

2、再重启启动进程;

不是不可以,只是在iOS环境下这个方案就行不通了。那怎么办呢?最好当然是游戏自己实现,像启动App进程那样,先走销毁(Destroy)流程再走初始化流程(Init)。

因为C#的脚本通常都会挂载到一个场景(Scene)或者一个游戏对象(GameObject)上,而重启就势必会销毁场景,这样脚本里的函数在Destroy就不会再被执行了。这就需要一个独立的一个C#脚本,它独立于游戏的加载、销毁之外,我第一次看到这个方案时有点惊讶,绝对是奇技淫巧。

我在示例中使用的是点击按钮时,调用函数的重启方法(Restart)

Restart方法只干了一件事,new一个GameObject对象出来,当这个GameObject被实例化时,该类的Start函数会被调用,然后在Start函数中执行:销毁 –> 间隔一段时间(用上面的yield return null或者Unity提供的延时处理指令) –> 初始化 –> 销毁 new出来的GameObject对象。

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine; public class Startup : MonoBehaviour { static GameObject m_GameObject; [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
static void OnBeforeSceneLoadRuntimeMethod()
{
// 在场景加载之前,可以做一些与游戏无关的一些初始化工作:Lua虚拟机的启动、SDK初始化、Bugly的初始化、资源管理的初始化... Debug.Log("Startup....... Before first scene loaded");
} // Use this for initialization
private IEnumerator Start()
{
Debug.Log("Start ----------------"); // 加载一个空的场景,设置其主相机的Clear Flag为Don't Clear,然后停一帧
yield return null; // 这里可以调用销毁的相关函数,如:关闭Lua虚拟机、相关资源的释放等,然后再停一帧 yield return null; // 重新调用初始化方法
OnBeforeSceneLoadRuntimeMethod(); // 加载游戏的主场景 Destroy(m_GameObject);
m_GameObject = null;
} public static void Restart()
{
if (m_GameObject == null)
{
m_GameObject = new GameObject("Startup", typeof(Startup));
DontDestroyOnLoad(m_GameObject);
}
} }

参考资料:

[1] Unity Coroutine 使用筆記

[2] 对Unity中Coroutines的理解

[3] Unity 协程运行时的监控和优化

Unity中巧用协程和游戏对象的生命周期处理游戏重启的问题的更多相关文章

  1. Unity脚本编程之——协程(Coroutine)

    本文翻译自Unity官方文档:https://docs.unity3d.com/Manual/Coroutines.html 专有名词: Coroutine 协程 Alpha 不透明度 当你调用一个函 ...

  2. Unity之"诡异"的协程

    为什么说是诡异的协程呢?首先从一个案例说起吧,示例如下: 游戏目标:让小车进入到对应颜色屋子里,即可获得一分.(转弯的道路可控)   为了让小车能够平滑转弯,小车的前进方向需要和车子的位置与圆心组成的 ...

  3. Unity 3D Framework Designing(3)——构建View和ViewModel的生命周期

    > 对于一个View而言,本质上是一个MonoBehaviour.它本身就具备生命周期这个概念,比如,Awake,Start,Update,OnDestory等.这些是非常好的方法,可以让开发者 ...

  4. Hibernate中Java对象的生命周期

    一个对象的出生源于我们的一个new操作,当我们使用new语句创建一个对象,这个对象的生命周期就开始了,当我们不在有任何引用变量引用它,这个对象就的生命就此结束,它占用的内存就可以被JVM的垃圾回收器回 ...

  5. java之hibernate之session中对象的生命周期

    1. session是用来执行对象的crud操作,并且session是对象事务工厂.session是线程级别的,所以生命周期比较短. 2.session中对象的生命周期图: 3.session中对象的 ...

  6. yield学习续:yield return迭代块在Unity3D中的应用——协程

    必读好文推荐: Unity协程(Coroutine)原理深入剖析 Unity协程(Coroutine)原理深入剖析再续 上面的文章说得太透彻,所以这里就记一下自己的学习笔记了. 首先要说明的是,协程并 ...

  7. python中线程 进程 协程

    多线程:#线程的并发是利用cpu上下文的切换(是并发,不是并行)#多线程执行的顺序是无序的#多线程共享全局变量#线程是继承在进程里的,没有进程就没有线程#GIL全局解释器锁#只要在进行耗时的IO操作的 ...

  8. 038_go语言中的状态协程

    代码演示: package main import ( "fmt" "math/rand" "sync/atomic" "time ...

  9. Android中的Coroutine协程原理详解

    前言 协程是一个并发方案.也是一种思想. 传统意义上的协程是单线程的,面对io密集型任务他的内存消耗更少,进而效率高.但是面对计算密集型的任务不如多线程并行运算效率高. 不同的语言对于协程都有不同的实 ...

随机推荐

  1. PYTHON-UDP

    1.TCP 和 UDP 发送数据时的流程 ***** 解释 为何TCP是可靠的 是因为发送数据后必须收到确认包 2. UDP的模板代码 ***** 1.UDP协议: (数据报协议) 特点: 无连接 优 ...

  2. python接口自动化测试二十四:上传多个附件,参数化

    # 添加多个附件参数化files = [("1.png", "1.png") ("2.png", "2.png") ]d ...

  3. python 全栈开发,Day136(爬虫系列之第3章-Selenium模块)

    一.Selenium 简介 selenium最初是一个自动化测试工具,而爬虫中使用它主要是为了解决requests无法直接执行JavaScript代码的问题 selenium本质是通过驱动浏览器,完全 ...

  4. ruby学习--条件控制

    条件控制 本人喜欢用程序demo记录的方式来记录某方法的使用,如times方法,仅作个人学习记录 #--------------if语句(相反是unless)而while相同于until------- ...

  5. (第1篇)什么是hadoop大数据?我又为什么要写这篇文章?

    摘要: hadoop是什么?hadoop是如何发展起来的?怎样才能正确安装hadoop环境? 这些天,有很多人咨询我大数据相关的一些信息,觉得大数据再未来会是一个朝阳行业,希望能尽早学会.入行,借这个 ...

  6. openstack学习-nove控制节点部署(四)

    nove在openstack非常重要,主要负责创建虚拟机 nova计算服务 API :负责接收和响应外部请求.支持openstack API,EC2 API Cert:负责身份认证EC 2 Sched ...

  7. 【CSS3】响应式布局

    准备工作1:设置Meta标签 首先我们在使用Media的时候需要先设置下面这段代码,来兼容移动设备的展示效果: 1 <meta name="viewport" content ...

  8. BZOJ1801 [Ahoi2009]chess 中国象棋 动态规划

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1801 题意概括 在N行M列的棋盘上,放若干个炮可以是0个,使得没有任何一个炮可以攻击另一个炮. 请 ...

  9. Camera摄像头

    <LinearLayout android:id="@+id/btn_layout" android:layout_width="match_parent" ...

  10. 【java并发核心一】Semaphore 的使用思路

    最近在看一本书<Java并发编程 核心方法与框架>,打算一边学习一边把学习的经验记下来,所粘贴的代码都是我运行过的,大家一起学习,欢迎吐槽. 估计也没多少人看我的博客,哈哈,那么我还是会记 ...