unity3d 赛车游戏——复位点检测优化、反向检测、圈数检测、赛道长度计算
接着上一篇文章说
因为代码简短且思路简单
所以我就把这几个功能汇总为一篇文章
因为我之前就是做游戏外挂的
经过验证核实,**飞车的复位点检测、圈数检测就是以下的方法实现的
至于反向检测和赛道长度计算,没去深入研究,不过应该也八九不离十
在告诉大家个小秘密:
**飞车的复位点检测和圈数检测利用以下文章中的代码思路可以做出外挂
感兴趣的可以试试!我只是技术交流,不是传播外挂,别打我
复位点检测优化:
首先感谢 @太粗难进
他的原话:
就是 如果大的轮船经过 会 把 桥 中间 分开。。。
飞车党 很多都喜欢 在这个断桥 中飞跃过去。
那么 如果有人掉下去了。。。
你的两个点会不会 是 桥的 两头,那么 最近的 地方 可不是路哦。
在空中。。。。”
如上图,我添加了两个cube充当高架桥,中间有一个很大的空隙,赛车要从空中飞到第二块地面上
如果赛车在红色箭头所指的地方按下复位的话,按照上一篇的代码来实现赛车就会复位到空中(红色点的位置)
假设我们两个cube下方是长江,那就蛋疼了,一直重复复位,一直复位在空中。。。
还能愉快的玩耍吗,这是要迫使玩家卸载游戏的节奏呀
我的解决方法是在原有的复位方法中再加几句代码
当获取最近复位点后,从复位点位置向下发送一条射线
如果碰撞到的物体为地面,则重新计算地面与复位点的高度
这个地方有可能解释的有点迷糊,怕小伙伴们不清楚,我就在解释一下
赛车有小轿车和大货车,很明显,大货车是比小轿车要高的
比如大货车的高度是3米,坐标点是在车的中间
也就是说当一辆大货车接触地面时,地面与货车坐标点的距离是1.5米
如果我们的复位点小于1.5米,那么大货车就会和赛道重叠,然后就掉到地面底下了
当两个碰撞体重合的时候,是没有物理效果的
这样解释按100分计算不知道能不能打90分,哈哈,解释错的地方欢迎批评指正
所以为了避免这种情况发生,我们要重新计算地面与复位点的高度
接着上篇文章的代码,添加进新代码得出以下代码(#region 新添加)
代码不难,而且都有注释,我就不过多解释了,不懂的查手册
/// 重置赛车 <summary> /// 重置赛车 /// </summary> private void RecoverCar() { //获取距离最近的路标点 WaypointsModel ClosestWP = GetClosestWP(WaypointsModelAll, transform.position); //下个路标点索引 ; //最近路标点 Vector3 nearestPoint; //下一个复位点索引 小于 路标点数量 - 1 ) { //获取两个路标点间离赛车最近的点 nearestPoint = NearestPoint( ClosestWP.Position, WaypointsModelAll[nextIndex].Position, transform.position); } else { //最后一个点和起点之间时取最后一个点的位置 nearestPoint = WaypointsModelAll[WaypointsModelAll.Count - ].Position; } #region 新添加 RaycastHit[] hit; //是否碰撞到地面 bool isColliderGround = false; //向下发送射线 hit = Physics.RaycastAll(nearestPoint, ClosestWP.Rotation * -Vector3.up, 100f); ; i1 < hit.Length; i1++) { //当碰撞到的物体为地面时 if (hit[i1].transform.tag == "Map_ground") { isColliderGround = true; //计算当前路标点与地面之间的距离 float temDis = Vector3.Distance(nearestPoint, hit[i1].point); //调整距离 nearestPoint.y = nearestPoint.y - (temDis - 0.5f); //Debug.Log(temDis); //绘制高度线 //Debug.DrawRay(WaypointsModelAll[i].Position, WaypointsModelAll[i].Rotation * -Vector3.up * temDis, Color.red); break; } } //如果没有碰撞到地面 //则说明赛车下面没有地面,处于悬空状态 //取最近路标点为复位点 if (!isColliderGround) nearestPoint = WaypointsModelAll[ClosestWP.Index].Position; #endregion transform.position = nearestPoint; transform.rotation = Quaternion.LookRotation(ClosestWP.Rotation * Vector3.forward); rigidbody.velocity = Vector3.zero; rigidbody.angularVelocity = Vector3.zero; }
反向检测:
介绍CarWaypoints插件的时候我有说到
就好比跑步比赛,人家都往前跑,你往后跑
就算你跑得再快,你也是最后一名,因为你跑反了
跑反不够逗比,逗比的是你跑反了还不知道
所以我们的赛车游戏需要一个反向检测,当逆向行驶时提示玩家
一开始写这个检测感觉应该挺复杂的,但是有了CarWaypoints插件变得好简单
我们选中一辆赛车,旋转Y角度,我们可以看见赛车对应着值旋转了
正确方向能不能是最近的Waypoint方向?
然后根据这个方向和赛车的方向计算得出是不是反向了?!
答案是完全可以!ok,现在看看代码
/// 检测反向移动 <summary> /// 检测反向移动 /// 思路如下: /// 通过Waypoint检测到离赛车最近的点 /// 然后通过计算点的距离而得出是否反向 /// </summary> private void isReverse() { WaypointsModel ClosestWP = GetClosestWP(WaypointsModelAll, transform.position);//获取距离最近的路径点 //角度偏移 = 最近路径点的角度 - 赛车的角度 float angleOffset = ClosestWP.Rotation.eulerAngles.y - transform.eulerAngles.y; /* 理论上来说 * 完全可以只写成 if(Mathf.Abs(angleOffset) >= 90f) * 则判断为反向!但是实际运用时会出现问题 * 因为赛道是围成圈形的(首尾相连) * 当赛车移动到下半圈的时候 * 明明是正确的方向,但是却提示反向了 * 所以为了避免这种情况发生,我们要用360-90=270 * 如果还是不理解的话debug路径点角度和赛车的角度就会发现端倪了*/ //角度偏移<=270f && 角度偏移>=90f && 刚体速度>8f if (Mathf.Abs(angleOffset) <= 270f && Mathf.Abs(angleOffset) >= 90f) Debug.Log("反向移动了:" + Mathf.Abs(angleOffset).ToString()); }
代码不多,我的注释挺多的!
先获取最近的路标点,然后根据获取最近路标点的角度 - 赛车角度 = 角度偏移
Mathf.Abs是取绝对值,如果我们对比绝对值的话,可以debug看一下这个角度偏移
运行游戏,直接复位赛车,让赛车保持正确方向
然后手动调整赛车的方向,观察角度偏移的变化
赛车面对着前方时:角度偏移大约为 0
赛车面对左方时:角度偏移大约为 -270(逐渐变大)
赛车面对右方时:角度偏移大约为-90(逐渐变小)
所以当我们得到角度偏移后,取绝对值对比是否<=270度且>=90度(理解一下正数和负数的变化,小学数学)
圈数检测:
做圈数检测我们绝对不能只在终点添加一个cube触发这么简单
因为如果只添加一个cube触发,那玩家倒车,在前进,就算一圈了
除非他是开挂可以做到这么屌爆了的功能
当然肯定会有人说,那我多添加几个cube不就行了吗?
你难道要手动添加一圈cube来触发?累不累啊
在我刚学u3d的时候我记得在游戏蛮牛看见过一个讲解关于赛车检测圈数的视频
他的方法就是在赛道上添加很多cube来触发检测圈数
这的确是一种方法,不过我是不推荐这种方法的,除非是某些特定的功能需要这样的方法
在这里我的方法依旧是使用CarWaypoints插件,轻松简单到爆
取最近路标点这个方法如果你看过前几篇文章不会陌生了吧?
将检测圈数的方法放在update中,赛车移动时他获取最近的路标点
然后将这个路标点储存在已经过路标点集合中
如果这个路标点已存在则不添加
在终点添加一个cube触发,触发时检测是否 已经过路标点集合数量 >= 最少经过路标点
如果成立则完成一圈,不成立就说明玩家在终点前(圈数起点)徘徊或者开挂
说这么多废话不如代码解释来得快,上代码:
/// 检测路标点 <summary> /// 用来保存已通过的路标点 /// 同样的路标点则不加入 /// 冲过终点线时取数量 /// 大于最少数量则算通过一圈 /// </summary> private List<WaypointsModel> CheckPoints = new List<WaypointsModel>(); //完成圈数最少检查点 ; ;//总圈数 ;//当前圈数 /// 圈数检测 <summary> /// 圈数检测 /// 思路如下: /// 每一帧计算距离最近的检查点 /// 检查点存在则不添加,不存在则添加 /// 冲过终点线时取数量 /// 大于最少数量则算通过一圈 /// 然后清零 CheckPoints /// </summary> private void CircleNumberCheck() { WaypointsModel ClosestWP = GetClosestWP(WaypointsModelAll, transform.position);//获取距离最近的路径点 //判断当前最近路标点是否已存在 ; i < CheckPoints.Count; i++) { //存在则返回 if (ClosestWP.Position == CheckPoints[i].Position) return; } //不存在则添加 CheckPoints.Add(ClosestWP); //Debug.Log(ClosestWP.Index); }
我们在代码中看见几个新变量,我在上文中没有提到
minCheckPoints:最小检查点数量
游戏赛道大多数都有近到,如果玩家抄近道的话,就会漏掉一些检查点
如果我一个游戏赛道的waypoints数量为40个
玩家抄近道后只能检测到35个检查点,那么检查点数量就应该设置为35
这个值是按具体情况填写的,可以先自己手动跑一局,debug出数量,然后在设置
还有两个变量不用解释了,计算圈数用的
接着是终点触发代码:
void OnTriggerEnter(Collider Trigger) { //检查点数量大于最小检查点数量则算一圈 if (minCheckPoints <= CheckPoints.Count) { currentCircleNumber++; if (currentCircleNumber > totalCircleNumber) { Debug.Log("游戏完成"); return; } Debug.Log("当前圈数:" + currentCircleNumber.ToString()); //清空检测点 CheckPoints.Clear(); } }
记得触发cube勾选isTrigger
如果是计圈赛道,那么完成一圈就清空一次检测点
这样的话就可以计算新的一圈检测点了
赛道长度计算:
这个就太简单了,不多余解释,直接上代码:
/// 计算赛道长度 <summary> /// 计算赛道长度 /// </summary> /// <returns>返回赛道长度</returns> private float CalcTotalDis() { //把所有点和点的距离相加而得出 float temTotalDis = 0f;//临时总距离 ; i < WaypointsModelAll.Count; i++) { ) { temTotalDis += Vector3.Distance(WaypointsModelAll[i].Position, WaypointsModelAll[].Position); } else { temTotalDis += Vector3.Distance(WaypointsModelAll[i].Position, WaypointsModelAll[i + ].Position); } } return temTotalDis; }
完整demo下载地址:http://pan.baidu.com/s/1i3ziILB
CarWaypoints插件使用介绍:http://www.cnblogs.com/shenggege/p/4295616.html
文中不足之处欢迎批评指正,如果本文对你有帮助请点一下右下角的推荐
本文链接:http://www.cnblogs.com/shenggege/p/4295986.html
unity3d 赛车游戏——复位点检测优化、反向检测、圈数检测、赛道长度计算的更多相关文章
- unity3d 赛车游戏——复位点检测
一直没有时间写博客 昨天我的CarWaypoints插件也告一段落了 今年没回家,过年就我一个人 挺无聊的,那就休息一天写几篇博客吧 我的代码可能很少,但是思路很重要 希望不懂的朋友别只copy代码 ...
- 使用Unity3D的设计思想实现一个简单的C#赛车游戏场景
最近看了看一个C#游戏开发的公开课,在该公开课中使用面向对象思想与Unity3D游戏开发思想结合的方式,对一个简单的赛车游戏场景进行了实现.原本在C#中很方便地就可以完成的一个小场景,使用Unity3 ...
- 使用Unity3D引擎开发赛车游戏
Car Tutorial 在Unity3D的Asset Store有一个赛车的Demo —— Car Tutorial,看起来特别酷的赛车游戏Demo,不过我还没有下载下来,因为在公司下载Assets ...
- Unity创作赛车游戏的四款插件
本文,我们将介绍其中4款:Racing Game Starter Kit.GeNa 2 .NWH Vehicle Physics.Curvy Splines. Racing Game Starter ...
- Unity赛车游戏之移动
这个赛车游戏真是让我费劲脑汁啊.尤其是写这种系统化的东西. 目前漂移还没找到更好的算法,不过基本的移动还是可以做到的. 别看就光是个移动,其实也是很费事的. Unity给了个对于赛车系统很好的碰撞组件 ...
- 《Unity3D/2D游戏开发从0到1(第二版本)》 书稿完结总结
前几天,个人著作<Unity3D/2D游戏开发从0到1(第二版)>经过七八个月的技术准备以及近3个月的日夜编写,在十一长假后终于完稿.今天抽出一点时间来,给广大热心小伙伴们汇报一下书籍概况 ...
- 《Unity3D/2D游戏开发从0到1》正式出版发行
<Unity3D/2D游戏开发从0到1>正式出版发行 去年个人编写的Unity书籍正式在2015年7月正式发行,现在补充介绍一下个人著作.书籍信息: 书籍的名称: <Uni ...
- Unity3D手机游戏开发
<Unity3D手机游戏开发> 基本信息 作者: 金玺曾 出版社:清华大学出版社 ISBN:9787302325550 上架时间:2013-8-7 出版日期:2013 年8月 开本:16开 ...
- Unity的赛车游戏实现思路
unity目前版本实现赛车的技术方案主要有3种: 1.wheelCollider,设置motorTorque.brakeTorque.steerAngle来实现车子的推动和转弯,优点是上手简单,而且很 ...
随机推荐
- 如何在linux系统中设置静态ip地址
在终端中输入:vi /etc/sysconfig/network-scripts/ifcfg-eth0 开始编辑,填写ip地址.子网掩码.网关.DNS等.其中"红框内的信息"是必须 ...
- for循环练习题
■■■■■■■■■■■■■■■■■■■■■■■■■ 代码: <script> for(i=0;i<5;i++) { for(j=0;j<5;j++) { document.wr ...
- 搭建Struts2不同版本jar包不同
struts2的版本比较多,所以在开发的时候特别要注意版本不同所需引入的包是不一样的.否则,会出现各种问题.而且很难找到问题所在. 以下是我遇到的问题总结: 一.当我运用struts2.3.4.1时, ...
- c#,关于Big Endian 和 Little Endian,以及转换类
Big Endian:最高字节在地址最低位,最低字节在地址最高位,依次排列. Little Endian:最低字节在最低位,最高字节在最高位,反序排列. 当在本地主机上,无需注意机器用的是Big En ...
- hdu 4635 Strongly connected 强连通缩点
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4635 题意:给你一个n个点m条边的图,问在图不是强连通图的情况下,最多可以向图中添多少条边,若图为原来 ...
- visual studio 2013 快捷键大全
1.回到上一个光标位置/前进到下一个光标位置 1)回到上一个光标位置:使用组合键“Ctrl + -”: 2)前进到下一个光标位置:“Ctrl + Shift + - ”. 2.复制/剪切/删除整行代码 ...
- android去掉顶部标题栏
在清单文件(manifest.xml)里面实现 <application> <activity android:name="cn.ui.activity.UserRegAc ...
- Java基础の乱弹琴二:break关键字
Java中的break一般用于 跳出一个switch或者循环. 跳出switch基本不用赘述. break跳出循环一般是跳出当前一层循环. 如若需要跳出多层循环可以在break后加标签,然后把标签标注 ...
- Hive drop table batched
if the hive version not support drop table tablename purge. your drop table command will move data t ...
- java 15 - 6 List的方法
List集合的特有功能: A:添加功能 void add(int index,Object element):在指定索引处添加元素 B:获取功能 Object get(int index):获取指定索 ...