学更好的别人,

做更好的自己。

——《微卡智享》

本文长度为4303,预计阅读11分钟

前言

上一篇《学习|Unity3d的导航实现循环线路移动》讲了一下定制循环行驶路线的方法,在视频中还有一个就是非会员的车辆需要人工收费,所以就要有一个减速停车等待的动画效果,本篇就来讲讲怎么用Unity3d的协程来实现的这一效果。

实现效果

上面的动图中我们可以看到无卡车辆在到达起杆前有一个慢慢减速后停止的效果(如果动图不明显可以看看历史文章里的这个视频),实现这个效果我用的是协程的方式,其实在FixUpdate的函数中进行处理应该效果会更好一些,但是也是为了掌握协程这个技巧,所以才用的协程的方式来实现的。

关于协程

微卡智享

协程本身有点像线程,但又不同于线程,协程本身还是在主程序中运行的,完全不用考虑使用线程时如线程锁或是线程同步的问题。

Update()函数中我们可以知道是每一帧都调用的,在每一帧处理时我们可能会有不少事件需要进行判断处理,如果需要判断的事件还需要有计时的处理时,在Update中看代码的可读性非常差,所有这里我们就可以用到协程了。

官方文档Monobehaviour的函数执行顺序图,就对协程再次执行的时机做了很好的描述:

相关函数

函数 参数
开启协程 StartCoroutine(string methodName)输入参数名
StartCoroutine(IEnumerator method),输入方法名,此方法可以有多个参数
终止协程 StopCoroutine(string methodName);//终止指定的协程
StopAllCoroutine();//终止所有协程
挂起协程 yield return 0;//程序在下一帧中从当前位置继续执行
yield return null;//程序在下一帧中从当前位置继续执行
yield return new WaitForSeconds(N);//程序等待N秒后从当前位置继续执行
yield new WaitForEndOfFrame();//在所有的渲染以及GUI程序执行完成后从当前位置继续执行
yield new WaitForFixedUpdate();//所有脚本中的FixedUpdate()函数都被执行后从当前位置继续执行
yield return WWW;//等待一个网络请求完成后从当前位置继续执行
yield return StartCoroutine(xxx);//等待一个xxx的协程执行完成后从当前位置继续执行
yield break;//如果使用yield break语句,将会导致协程的执行条件不被满足,不会从当前的位置继续执行程序,而是直接从当前位置跳出函数体,回到函数的根部

程序实现

微卡智享

01

碰撞器与钢体的设置

上图中红色框是我们设置的一个boxCollider(盒形碰撞器),而蓝框的车里面我们也加入了一个碰撞器,还有一个钢体。

在红框里的碰撞器我们把isTrigger打上勾,代表是触发器,这样两个物体碰撞是不会产生物理效果了,只会生成触发的事件。这样基本就设置好了,下面的就是我们在代码里实现了。

实现思路

1.  当两个物体碰撞触发事件后,判断是否是会员车辆,如果不是进入停车减速的协程,设置一个停车减速的时间为参数。

2.  根据输入的减速时间参数先计算出大约多少帧,然后用当前的车速除帧数得到每一帧应该减的速度为多少,设置循环,每一帧降低刚才计算要减的车速,直到停车

3.  设置一个停止时长

4. 再按刚才的帧数每帧再增加车速,直到恢复原来的速度

核心代码

    IEnumerator StopAndStartCar(float seconds){Debug.Log("time:" + Time.deltaTime);//计算输入时长大约多少帧float fps = seconds / Time.deltaTime;//计算每帧要调整的车速float speed = oldspeed / fps;Debug.Log("fps:" + fps);Debug.Log("speed:" + speed);//减速停车for (int i=0; i < fps; ++i){if (nav.speed > 0){nav.speed -= speed;if (nav.speed < 0) nav.speed = 0;}yield return null;}if (nav.speed != 0) nav.speed = 0;//等待0.5秒yield return new WaitForSeconds(1f);//启动加速for(int i=0; i < fps; ++i){nav.speed += speed;yield return null;}nav.speed = oldspeed;}

上面这个就是实现减速后停止0.5秒,然后再加速的协程方法。

当我们进入触发函数时判断不是会员车辆加入了一个启动协程的方法,参数输入的是3f(即3秒)。这样我们的停车减速的效果就实现了。

完整代码

using System.Collections;
using System.Collections.Generic;
using System.Data;
using UnityEngine;
using UnityEngine.AI;public class NavCar : MonoBehaviour
{//定义接收导航网络组件private NavMeshAgent nav;//坐标点列表private List<Vector3> destpoints;//导航下一个坐标点private int nextindex;//离导航坐标点的距离 private float calcdist = 5f;private float dist = 0f;private TextMesh textMesh;private float oldspeed;// Start is called before the first frame updatevoid Start(){//将定义的路线加入到List列表中destpoints = new List<Vector3>();destpoints.Add(GameObject.Find("RoadPoint0").transform.position);destpoints.Add(GameObject.Find("RoadPoint1").transform.position);destpoints.Add(GameObject.Find("RoadPoint2").transform.position);destpoints.Add(GameObject.Find("RoadPoint3").transform.position);textMesh = this.transform.GetComponentInChildren<TextMesh>();//获取当前车辆的NavMeshAgentnav = this.transform.GetComponent<NavMeshAgent>();oldspeed = nav.speed;//计算最近的点,获取下一点的序号Vector3 navpoint = this.transform.position;Debug.Log("now:" + navpoint);for (int i = 0; i < destpoints.Count; ++i){//首先判断点在当前位置的前方还是后方,如果是后方不做计算Vector3 dir = destpoints[i] - navpoint;float dot = Vector3.Dot(transform.forward, dir);Debug.Log("dot:" + dot);//判断点在前方时才计算最近的点的距离if (dot > 0){float tmpdist = Vector3.Distance(destpoints[i], navpoint);if (dist == 0){dist = tmpdist;nextindex = i;}else if (dist > tmpdist){dist = tmpdist;nextindex = i;}Debug.Log("position:" + destpoints[i] + "  dist:" + tmpdist);}}Debug.Log("final:" + nextindex + " dist:" + dist);}private void OnTriggerEnter(Collider other){if (other.gameObject.name == "GateDown"){Debug.Log(other.gameObject.name);if (textMesh.text != "会员车辆"){StartCoroutine(StopAndStartCar(3f));}}}IEnumerator StopAndStartCar(float seconds){Debug.Log("time:" + Time.deltaTime);//计算输入时长大约多少帧float fps = seconds / Time.deltaTime;//计算每帧要调整的车速float speed = oldspeed / fps;Debug.Log("fps:" + fps);Debug.Log("speed:" + speed);//减速停车for (int i=0; i < fps; ++i){if (nav.speed > 0){nav.speed -= speed;if (nav.speed < 0) nav.speed = 0;}yield return null;}if (nav.speed != 0) nav.speed = 0;//等待0.5秒yield return new WaitForSeconds(1f);//启动加速for(int i=0; i < fps; ++i){nav.speed += speed;yield return null;}nav.speed = oldspeed;}// Update is called once per framevoid Update(){//判断距离是否在到达范围内,如果在走到一下个点if (Vector3.Distance(this.transform.position, destpoints[nextindex])< calcdist){if (nextindex == destpoints.Count - 1){nextindex = 0;}else{nextindex++;}}nav.SetDestination(destpoints[nextindex]);}
}

在动画中的起杆的动画也是按这个方法实现的,这里就不再进行描述了。

扫描二维码

获取更多精彩

微卡智享

「 往期文章 」

学习|Unity3d的导航实现循环线路移动

学习|C#线程中AutoResetEvent的使用

学习|C#的EventHandler的委托使用

学习|Unity3D使用协程实现减速停车效果相关推荐

  1. Unity3D 学习笔记6 ——协程

    一.协程的概述: 1.定义: 官方的定义是: A coroutine is a function that is executed partially and, presuming suitable ...

  2. Kotlin学习——简单运用协程网络下载图片并更新到UI

    kotlin学习 协程Coroutines学习 简单小Demo:通过协程下载一张网络图片并显示出来 文章目录 kotlin学习 前言 一.如何开启一个协程? 二.如何在项目中使用协程 增加对 Kotl ...

  3. lua学习笔记之协程

    1.基础 所有协程相关的在表coroutine中,创建通过create来创建,参数为函数,返回值类型为thread. 协程状态有:suspended, running, normal, dead.通过 ...

  4. Go语言学习笔记 - PART13 - 协程与通道

    协程 应用程序处理并发的部分 协程跟操作系统的线程之间不是一对一的关系 协程是独立执行的,它们之间必须同行才会变得有用 通道 负责协程之间的通信,从而避免所有由共享内存导致的陷阱 通道只能传输一种类型 ...

  5. Kotlin学习笔记26 协程part6 协程与线程的关系 Dispatchers.Unconfined 协程调试 协程上下文切换 Job详解 父子协程的关系

    参考链接 示例来自bilibili Kotlin语言深入解析 张龙老师的视频 1 协程与线程的关系 import kotlinx.coroutines.* import java.util.concu ...

  6. Kotlin学习笔记25 协程part5 协程的同步与异步

    参考链接 示例来自bilibili Kotlin语言深入解析 张龙老师的视频 1 程序运行时间统计measureTimeMillis /*** 程序运行时间统计measureTimeMillis** ...

  7. Kotlin学习笔记24 协程part4 协程的取消与超时

    参考链接 示例来自bilibili Kotlin语言深入解析 张龙老师的视频 1 如何取消协程 import kotlinx.coroutines.*/*** 协程的取消*/fun main() = ...

  8. Kotlin学习笔记23 协程part3 lambda表达式深入 挂起函数 全局协程

    参考链接 示例来自bilibili Kotlin语言深入解析 张龙老师的视频 1 lambda表达式深入 /*** lambda 表达式深入* 当函数参数是函数时 并且该函数只有一个参数 可以不传入任 ...

  9. Kotlin学习笔记22 协程part2 join CoroutineScope 协程vs线程

    参考链接 示例来自bilibili Kotlin语言深入解析 张龙老师的视频 1 Job的join方法 import kotlinx.coroutines.* /*** Job的join方法* 它会挂 ...

最新文章

  1. linux系统进入管理员命令行,Linux的15个命令行别名,帮系统管理员提升工作效率!...
  2. px4flow智能光学流动传感器
  3. 机器学习(二)监督学习
  4. C语言orders order订单的算法(附完整源码)
  5. 如何用ARP欺骗来嗅探主机流量
  6. [转载] 什么是istio 官网内容
  7. 航测大数据量处理_揭秘航测局“航空摄影质量自动检查系统”的“黑科技”
  8. Linux Ubuntu 安装 Matlab
  9. 洛谷——P2381 圆圆舞蹈
  10. 代理对象之spring注解事务控制或shiro注解权限控制的理解
  11. MySQL中的“<=>”操作符
  12. hdu 5101 n集合选2个不同集合数使和大于k
  13. fanuc系统屏蔽服务器,FANUC伺服轴的屏蔽方法
  14. 【ps小技巧】内容识别, 1分钟去掉图片上的文字
  15. Java面向对象编程 实验报告
  16. Oracle 客户端工具介绍
  17. wps怎么只删除本页的页眉_WPS如何删除文件中某一页的页眉
  18. EXCEL 制作多簇并列的堆积柱形图
  19. 浏览器如何禁用或启用Cookie
  20. 阿里达摩院获KDD 2022最佳论文,国内企业首次获奖

热门文章

  1. C语言关键字之循环语句for语句
  2. 外星人电脑 在线安装_2019台北国际电脑展前瞻 智能PC、OLED笔记本和5G全都有
  3. 怎么更新服务器上的项目,更新服务器的项目
  4. 史上最全GAN综述2020版:算法、理论及应用(A Review on Generative Adversarial Networks: Algorithms, Theory, and Applic)
  5. sim插拔识别时间_求助:如何实现不关机SIM卡插拔(不用PUSH式卡座)
  6. 支付宝、腾讯都发了NFT?监管是怎么看的?
  7. 十岁孩子幽门螺旋杆菌会不会影响发育?最好的治疗方法是这样的
  8. linux 内核态 cos函数,浅析μCOS/II v2.85内核OSMboxPend()和OSMboxPost()函数工作原理
  9. MVC设计模式、单体架构、前后端分离、微服务
  10. Opencv+Qt之基于阈值、形态学处理的种子分割自动分类系统