unity3d 赛车游戏——复位点检测优化、反向检测、圈数检测、赛道长度计算

接着上一篇文章说

因为代码简短且思路简单

所以我就把这几个功能汇总为一篇文章

因为我之前就是做游戏外挂的

经过验证核实,**飞车的复位点检测、圈数检测就是以下的方法实现的

至于反向检测和赛道长度计算,没去深入研究,不过应该也八九不离十

在告诉大家个小秘密:

**飞车的复位点检测和圈数检测利用以下文章中的代码思路可以做出外挂

感兴趣的可以试试!我只是技术交流,不是传播外挂,别打我

 

复位点检测优化:

首先感谢 @太粗难进

他的原话:

“不过 你知道 高架桥么?
就是 如果大的轮船经过 会 把 桥 中间 分开。。。
飞车党 很多都喜欢 在这个断桥 中飞跃过去。

那么 如果有人掉下去了。。。
你的两个点会不会 是 桥的 两头,那么 最近的 地方 可不是路哦。
在空中。。。。”
 
这的确是个问题,写插件的时候没有考虑进去
但是还好,我想到一个补救的方法,且很简单
技术分享

 如上图,我添加了两个cube充当高架桥,中间有一个很大的空隙,赛车要从空中飞到第二块地面上

如果赛车在红色箭头所指的地方按下复位的话,按照上一篇的代码来实现赛车就会复位到空中(红色点的位置)

假设我们两个cube下方是长江,那就蛋疼了,一直重复复位,一直复位在空中。。。

还能愉快的玩耍吗,这是要迫使玩家卸载游戏的节奏呀

我的解决方法是在原有的复位方法中再加几句代码

当获取最近复位点后,从复位点位置向下发送一条射线

如果碰撞到的物体为地面,则重新计算地面与复位点的高度

 

这个地方有可能解释的有点迷糊,怕小伙伴们不清楚,我就在解释一下

赛车有小轿车和大货车,很明显,大货车是比小轿车要高的

比如大货车的高度是3米,坐标点是在车的中间

也就是说当一辆大货车接触地面时,地面与货车坐标点的距离是1.5米

如果我们的复位点小于1.5米,那么大货车就会和赛道重叠,然后就掉到地面底下了

当两个碰撞体重合的时候,是没有物理效果的

这样解释按100分计算不知道能不能打90分,哈哈,解释错的地方欢迎批评指正

 

所以为了避免这种情况发生,我们要重新计算地面与复位点的高度

接着上篇文章的代码,添加进新代码得出以下代码(#region 新添加)

代码不难,而且都有注释,我就不过多解释了,不懂的查手册

 1     /// 重置赛车 <summary>
 2     /// 重置赛车
 3     /// </summary>
 4     private void RecoverCar()
 5     {
 6         //获取距离最近的路标点
 7         WaypointsModel ClosestWP = GetClosestWP(WaypointsModelAll, transform.position);
 8 
 9         //下个路标点索引
10         int nextIndex = ClosestWP.Index + 1;
11 
12         //最近路标点
13         Vector3 nearestPoint;
14 
15         //下一个复位点索引 小于 路标点数量 - 1
16         if (nextIndex < WaypointsModelAll.Count - 1)
17         {
18             //获取两个路标点间离赛车最近的点
19             nearestPoint = NearestPoint(
20                 ClosestWP.Position,
21                 WaypointsModelAll[nextIndex].Position, 
22                 transform.position);
23         }
24         else
25         {
26             //最后一个点和起点之间时取最后一个点的位置
27             nearestPoint = WaypointsModelAll[WaypointsModelAll.Count - 1].Position;
28         }
29 
30 
31         #region 新添加
32 
33         RaycastHit[] hit;
34 
35         //是否碰撞到地面
36         bool isColliderGround = false;
37 
38         //向下发送射线
39         hit = Physics.RaycastAll(nearestPoint, ClosestWP.Rotation * -Vector3.up, 100f);
40         
41         for (int i1 = 0; i1 < hit.Length; i1++)
42         {
43             //当碰撞到的物体为地面时
44             if (hit[i1].transform.tag == "Map_ground")
45             {
46                 isColliderGround = true;
47 
48                 //计算当前路标点与地面之间的距离
49                 float temDis = Vector3.Distance(nearestPoint, hit[i1].point);
50 
51                 //调整距离
52                 nearestPoint.y = nearestPoint.y - (temDis - 0.5f);
53 
54                 //Debug.Log(temDis);
55 
56                 //绘制高度线
57                 //Debug.DrawRay(WaypointsModelAll[i].Position, WaypointsModelAll[i].Rotation * -Vector3.up * temDis, Color.red);
58                 break;
59             }
60         }
61 
62         //如果没有碰撞到地面
63         //则说明赛车下面没有地面,处于悬空状态
64         //取最近路标点为复位点
65         if (!isColliderGround)
66             nearestPoint = WaypointsModelAll[ClosestWP.Index].Position;
67 
68         #endregion
69 
70 
71         transform.position = nearestPoint;
72         transform.rotation = Quaternion.LookRotation(ClosestWP.Rotation * Vector3.forward);
73         rigidbody.velocity = Vector3.zero;
74         rigidbody.angularVelocity = Vector3.zero;
75     }

 

反向检测:

介绍CarWaypoints插件的时候我有说到

就好比跑步比赛,人家都往前跑,你往后跑

就算你跑得再快,你也是最后一名,因为你跑反了

跑反不够逗比,逗比的是你跑反了还不知道

所以我们的赛车游戏需要一个反向检测,当逆向行驶时提示玩家

 一开始写这个检测感觉应该挺复杂的,但是有了CarWaypoints插件变得好简单

我们选中一辆赛车,旋转Y角度,我们可以看见赛车对应着值旋转了

正确方向能不能是最近的Waypoint方向?

然后根据这个方向和赛车的方向计算得出是不是反向了?!

答案是完全可以!ok,现在看看代码

 1     /// 检测反向移动 <summary>
 2     /// 检测反向移动
 3     /// 思路如下:
 4     /// 通过Waypoint检测到离赛车最近的点
 5     /// 然后通过计算点的距离而得出是否反向
 6     /// </summary>
 7     private void isReverse()
 8     {
 9 
10         WaypointsModel ClosestWP = GetClosestWP(WaypointsModelAll, transform.position);//获取距离最近的路径点
11 
12         //角度偏移 = 最近路径点的角度 - 赛车的角度
13         float angleOffset = ClosestWP.Rotation.eulerAngles.y - transform.eulerAngles.y;
14 
15         /* 理论上来说
16          * 完全可以只写成 if(Mathf.Abs(angleOffset) >= 90f)
17          * 则判断为反向!但是实际运用时会出现问题
18          * 因为赛道是围成圈形的(首尾相连)
19          * 当赛车移动到下半圈的时候
20          * 明明是正确的方向,但是却提示反向了
21          * 所以为了避免这种情况发生,我们要用360-90=270
22          * 如果还是不理解的话debug路径点角度和赛车的角度就会发现端倪了*/
23 25         //角度偏移<=270f && 角度偏移>=90f && 刚体速度>8f
26         if (Mathf.Abs(angleOffset) <= 270f && Mathf.Abs(angleOffset) >= 90f)
27             Debug.Log("反向移动了:" + Mathf.Abs(angleOffset).ToString());
28     }

代码不多,我的注释挺多的!

先获取最近的路标点,然后根据获取最近路标点的角度 - 赛车角度 = 角度偏移

Mathf.Abs是取绝对值,如果我们对比绝对值的话,可以debug看一下这个角度偏移

运行游戏,直接复位赛车,让赛车保持正确方向

然后手动调整赛车的方向,观察角度偏移的变化

赛车面对着前方时:角度偏移大约为 0 

赛车面对左方时:角度偏移大约为 -270(逐渐变大)

赛车面对右方时:角度偏移大约为-90(逐渐变小)

所以当我们得到角度偏移后,取绝对值对比是否<=270度且>=90度(理解一下正数和负数的变化,小学数学)

 

圈数检测:

做圈数检测我们绝对不能只在终点添加一个cube触发这么简单

因为如果只添加一个cube触发,那玩家倒车,在前进,就算一圈了

除非他是开挂可以做到这么屌爆了的功能

当然肯定会有人说,那我多添加几个cube不就行了吗?

你难道要手动添加一圈cube来触发?累不累啊

在我刚学u3d的时候我记得在游戏蛮牛看见过一个讲解关于赛车检测圈数的视频

他的方法就是在赛道上添加很多cube来触发检测圈数

这的确是一种方法,不过我是不推荐这种方法的,除非是某些特定的功能需要这样的方法

在这里我的方法依旧是使用CarWaypoints插件,轻松简单到爆

取最近路标点这个方法如果你看过前几篇文章不会陌生了吧?

将检测圈数的方法放在update中,赛车移动时他获取最近的路标点

然后将这个路标点储存在已经过路标点集合中

如果这个路标点已存在则不添加

在终点添加一个cube触发,触发时检测是否 已经过路标点集合数量 >= 最少经过路标点

如果成立则完成一圈,不成立就说明玩家在终点前(圈数起点)徘徊或者开挂

说这么多废话不如代码解释来得快,上代码:

 1     /// 检测路标点 <summary>
 2     /// 用来保存已通过的路标点
 3     /// 同样的路标点则不加入
 4     /// 冲过终点线时取数量
 5     /// 大于最少数量则算通过一圈
 6     /// </summary>
 7     private List<WaypointsModel> CheckPoints = new List<WaypointsModel>();
 8 
 9     //完成圈数最少检查点
10     private int minCheckPoints = 35;
11     private int totalCircleNumber = 3;//总圈数
12     private int currentCircleNumber = 1;//当前圈数
13 
14     /// 圈数检测 <summary>
15     /// 圈数检测
16     /// 思路如下:
17     /// 每一帧计算距离最近的检查点
18     /// 检查点存在则不添加,不存在则添加
19     /// 冲过终点线时取数量
20     /// 大于最少数量则算通过一圈
21     /// 然后清零 CheckPoints
22     /// </summary>
23     private void CircleNumberCheck()
24     {
25         WaypointsModel ClosestWP = GetClosestWP(WaypointsModelAll, transform.position);//获取距离最近的路径点
26 
27         //判断当前最近路标点是否已存在
28         for (int i = 0; i < CheckPoints.Count; i++)
29         {
30             //存在则返回
31             if (ClosestWP.Position == CheckPoints[i].Position)
32                 return;
33         }
34 
35         //不存在则添加
36         CheckPoints.Add(ClosestWP);
37         //Debug.Log(ClosestWP.Index);
38     }

我们在代码中看见几个新变量,我在上文中没有提到

minCheckPoints:最小检查点数量

游戏赛道大多数都有近到,如果玩家抄近道的话,就会漏掉一些检查点

如果我一个游戏赛道的waypoints数量为40个

玩家抄近道后只能检测到35个检查点,那么检查点数量就应该设置为35

这个值是按具体情况填写的,可以先自己手动跑一局,debug出数量,然后在设置

还有两个变量不用解释了,计算圈数用的

接着是终点触发代码:

 1 void OnTriggerEnter(Collider Trigger)
 2     {
 3         //检查点数量大于最小检查点数量则算一圈
 4         if (minCheckPoints <= CheckPoints.Count)
 5         {
 6             currentCircleNumber++;
 7 
 8             if (currentCircleNumber > totalCircleNumber)
 9             {
10                 Debug.Log("游戏完成");
11                 return;
12             }
13 
14             Debug.Log("当前圈数:" + currentCircleNumber.ToString());
15 
16             //清空检测点
17             CheckPoints.Clear();
18         }
19     }

记得触发cube勾选isTrigger

如果是计圈赛道,那么完成一圈就清空一次检测点

这样的话就可以计算新的一圈检测点了

 

赛道长度计算:

这个就太简单了,不多余解释,直接上代码:

 1     /// 计算赛道长度 <summary>
 2     /// 计算赛道长度
 3     /// </summary>
 4     /// <returns>返回赛道长度</returns>
 5     private float CalcTotalDis()
 6     {
 7         //把所有点和点的距离相加而得出
 8 
 9         float temTotalDis = 0f;//临时总距离
10         for (int i = 0; i < WaypointsModelAll.Count; i++)
11         {
12             if (i >= WaypointsModelAll.Count - 1)
13             { temTotalDis += Vector3.Distance(WaypointsModelAll[i].Position, WaypointsModelAll[0].Position); }
14             else
15             { temTotalDis += Vector3.Distance(WaypointsModelAll[i].Position, WaypointsModelAll[i + 1].Position); }
16         }
17 
18         return temTotalDis;
19     }

 

完整demo下载地址:http://pan.baidu.com/s/1i3ziILB

CarWaypoints插件使用介绍:http://www.cnblogs.com/shenggege/p/4295616.html

 

文中不足之处欢迎批评指正,如果本文对你有帮助请点一下右下角的推荐

本文链接:http://www.cnblogs.com/shenggege/p/4295986.html

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。