按照正常情况游戏中角色在上坡爬坡的时候速度应该减慢,可是角色控制器组件没有帮我们做这个判断,刚好最近工作中需要做这个功能,我就用勾股定理的法则来解决这个问题。如下图所示,当角色在爬坡的时候,角色控制器默认行走的距离就是 “C” 。但是如果行走的距离是“C” 你会发现上坡的速度太快了。这里我们需要计算”b”,让角色在上坡时候一次移动的距离是”b”这样移动就很正常了。
按照勾股定理的法则, c二次方 = a二次方 + b二次方。已知 c 和 a 我们求的b的距离即可。
第一步:主角目前所在地形的3D坐标,以及主角面朝方向行走“一段距离”后的3D坐标。 “一段距离” 我这里使用他的行走速度也就是1秒行走的长度。
第二步:把部分代码添加入角色控制器组件。
默认角色控制器行走是走路,简单改一下让他跑步。找到UpdateSmoothedMovementDirection ()方法,加入“|isMoving” 这个判断条件。
1 2 3 |
if (Input.GetKey (KeyCode.LeftShift) | Input.GetKey (KeyCode.RightShift) | isMoving) |
然后找到Update()方法,(C#和js都可以编译通过下面这段代码)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
// Apply jumping logic ApplyJumping (); //-------------开始插入代码 ------------- //当主角处于移动状态时开始计算主角目前坐标以及1秒后坐标 if(IsMoving()) { //得到主角行走1秒后所在位置地形的坐标 var newPos = transform.position + (transform.rotation * Vector3.forward * moveSpeed); newPos.y = Terrain.activeTerrain.SampleHeight(newPos); //得到主角当前位置所在地形的坐标 var heropos = transform.position; heropos.y = Terrain.activeTerrain.SampleHeight(transform.position); //绘制一条Debug的线段,在编辑器中看的更清楚。 Debug.DrawLine(heropos,newPos,Color.red); //斜边的长度 var c = moveSpeed; //短直角边的长度 var b = newPos.y - heropos.y; //b >0 标示主角在爬坡 b < 0 表示主角在下坡 if(b > 0) { // 根据公式计算 a = 根号下 c二次方 - b二次方 var a = Mathf.Sqrt(Mathf.Pow(c,2) - Mathf.Pow(b,2)); moveSpeed = a; } } //-------------结束插入代码 ------------- // Calculate actual motion Vector3 movement = moveDirection * moveSpeed + new Vector3 (0, verticalSpeed, 0) + inAirVelocity; movement *= Time.deltaTime; |
直接运行游戏,你会发现当你在爬坡的时候主角的移动速度会减少,下坡与平地的时候移动速度正常。
另外还有一个地方需要注意下。 使用角色控制器时如果你的坡度角度过于大,你会发现你的主角无法继续爬坡。如下图所示,默认坡度角度为45。如果你需要爬坡更高的角度,直接修改Slope Limit数值即可。
当然代码中我们也可以控制角色是否可以继续爬坡,根据上述代码两点Y坐标的插值也可以判断。
1 2 3 |
float xx = newPos.y - heropos.y; |
接着,如果你的项目中没有使用地形元素,而是用美术建模形成的地形话,那么就需要通过射线来取得“地形”上的两个点。如下图所示。
白色射线:得到主角面朝方向一步以后的地形坐标。
蓝色线段:主角移动的起点和终点的线段。
红色射线:处理摄像机(与本章无关)
在地形之上,我们使用下面这个方法得到地形的高度。
1 2 3 |
Terrain.activeTerrain.SampleHeight() |
如果项目中的地形是美术做的话,需要用射线的方式来计算。代码比较简单我就不用注释了, 就是上图中的那个白色射线,从天上射向地面。参数是模型面前下一步的将要行走的坐标,然后通过射线换算成实际Y轴高度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public static float SampleHeight(Vector3 point) { var sample = point; sample.y += 20; Ray ray = new Ray(sample, Vector3.down); RaycastHit hit; if (Physics.Raycast(ray, out hit)) { if(hit.collider.gameObject.tag != "Terrain") { Debug.DrawLine(sample,hit.point); return hit.point.y; } } return -1; } |
剩下的地方就和上面的代码差不多了。如果你的项目中没有使用角色控制器,用别的方式来实现移动效果,也可以使用这样的方法,总是就是勾股定理了。
最后是本文的内容的下载地址,这样看起来原理就清晰很多。欢迎留言给我,大家互相学习与进步,哇咔咔。
http://vdisk.weibo.com/s/r1xrb
- 本文固定链接: https://www.xuanyusong.com/archives/2092
- 转载请注明: 雨松MOMO 于 雨松MOMO程序研究院 发表
加了角色控制器之后。两个角色不能互相穿插了,怎么办?
为什么下坡速度会慢?上坡正常的,用的Character Controller,Update里有添加垂直向下的“重力”。好奇怪,斜坡,楼梯都是这样子,下坡就慢。
momo,你好,我觉得这个上坡减速有点问题,运行的时候也会报错,因为第三人称模型,Z轴永远指向的前方,不会因为坡度而发生变化,那么求出来的下一秒运动的位置实际上是基于底边运行了一秒,也就是求得的c的值实际是b的长度,并不是在斜边上运行一秒的距离,如果坡度固定的话,就应该不会报错,如果坡度不固定,随着坡度越来越高,求根的时候c*2-b*2会小于0所以报了错,如果模型的Z轴会发生改变 我想直接用勾股定理应该是没问题的,所以我觉得如果用第三人称应该通过随角度的变化即时改变速度的大小,求出实际斜边的速度
雨松你好!前段时间买了你的书,感觉对初学者很有帮助。现在研究到细致一些,在角色控制器上遇到问题,在此求教:如何使角色控制器能够精确地检测碰撞边缘?比如角色站在悬崖边,向前轻微移动时预期角色掉下悬崖,但控制器的collisionFlags底边检测一直存在,导致角色一半地面,一半悬空,效果很不好。同样问题表现在陡坡时,如果陡坡斜度接近垂直,按道理角色应该直接滑落,但角色仍然一半地面,一半悬空(非关重力问题),不知你有没有碰到类似问题?求教思路,不胜感激!
MOMO 你好,我遇到了点问题,我在弄个坦克大战,模型是在网上找的,每个坦克都加了rigidbody后坦克都可以落到地面,但是每个坦克之间却可以互相穿过,这是为什么?
MOMO 你好,我遇到了点问题,我在弄个坦克大战,模型是在网上找的,每个坦克都加了rigidbody后坦克都可以落到地面,但是每个坦克之间却可以互相穿过,这是为什么?
MO哥泥嚎!学生我有个问题。我在内置地形上自定义了一个角色,用CollisionFlags.CollidedBelow判断是否着地,若是,则可操控,反之不行。问题来了,在下坡时isGrounded状态不连续,以致操作断断续续。我参考了官方的bootcamp,看不懂(= =)。ps:重力用pos.y-=G*Time.deltaTime实现,与isGrounded无关
已解决。加了个变量保存上一帧的y,差小于n仍是Grounded-3-
还没解决= =
你参考角色控制器吧。。
没错。。。科学设置offset和skin width就好
momo你好,我之前问题可能说的不清楚。 所以在贴吧发了个帖子 有图有真相,有空希望你帮我看看。谢谢。http://tieba.baidu.com/p/2207264639
我知道为什么你没能掉下来了。。 因为角色控制器是没有重力系统的,你需要用代码来实现重力。 源码里面有这一段。。verticalSpeed -= gravity * Time.deltaTime * 10; 计算了重力,然后得到移动的向量。 Vector3 movement = moveDirection * moveSpeed + new Vector3 (0, verticalSpeed, 0) + inAirVelocity; movement *= Time.deltaTime;最后 CharacterController controller = GetComponent(); collisionFlags = controller.Move(movement);就会感应重力。。
灰常感谢 已经解决。
解决了就好蛤蛤。
我知道为什么你没能掉下来了。。 因为角色控制器是没有重力系统的,你需要用代码来实现重力。 源码里面有这一段。。verticalSpeed -= gravity * Time.deltaTime * 10;计算了重力,然后得到移动的向量。Vector3 movement = moveDirection * moveSpeed new Vector3 (0, verticalSpeed, 0) inAirVelocity;movement *= Time.deltaTime;最后CharacterController controller = GetComponent();collisionFlags = controller.Move(movement);就会感应重力。。
载入了一个 带动画的3d模型 想再地图上行走,但是遇到山体就穿进去了如果用了钢体 的重力的话 就会从地图上穿下去。请教一下 怎么让一个模型沿着制作的地图随意行走。动画调用会了 移动也会了 现在就是不知道 怎么沿着山体行走。
你应该就山林那里增加一个collider。 然后选定一个碰撞的区域, 不用增加刚体给主角加刚体 或角色控制器(建议角色控制器) 就可以感应碰撞
感谢momo回答 但还是不太懂啊 地图本身有一个地图collider 怎么再加呢我是想 实现 官方例子里的 爬坡 用了角色控制器 发现模型还是穿过了地图的山体部分。
这次摊上大事了 momo 同学。我用角色控制器的move了想要的效果 可以爬坡了。但爬坡完毕后 角色的高度提高了下不来…. 又遇到下坡问题 怎么让它有重力效果呢
这个就更简单了。。 代码中25行 if(b < 0) 就表示角色在下坡、通过下坡的时间还有重力就可以计算出来。 我觉得下坡没必要增加速度。。。 原始速度就可以,上坡要加,我们的项目下坡就没有加, 就上坡加了。。
感谢回答 但我的问题是: 我的模型不下坡 直接成飞的了 我没用系统自带 是自己做了个模型 加入了角色控制组件。 现在只能上坡不能下坡。
奇怪,我这里没出过你的问题, 非常稳定 呵呵。。
雨松MOMO,你出版的书第85页的backAnim数组变量是什么含义呢,没有注释所以没看懂哇,呵呵?
MOMO ,急盼回复!
用来保存动画的。。数组
谢谢MOMO这么快就给予解答哈 ,这下我好像明白一些了!我呀,本来学策划的,如果脚本要是无注释,看起来就比较费力。希望尽快把你写的书学通!有问题,我还会回来滴
嗯嗯 期待你下次在来访。。
我也是有疑惑,你使用数组时刻保证保存着备份的用意,因为整个例子它没有起到作用,希望解惑
还有就是我下载你书的源代码,在最新版的unity3D 4.2.1f4打开总途中是崩溃,下官方的AngryBots也是这样。。。崩溃,
好吧我登陆了╮(╯▽╰)╭再赞一个!
感谢你的支持 嘿嘿。
momo 你真是个 大好人啊!!!momo 你真是个 大好人啊!!!
好吧, 我被你折服了 HOHO。
喂 写一些 服务端的东西嘛 好不我想实现一个 多人在线打怪的简单DEMO 呵呵嘿嘿 老板要看 momo洗个文章 指点撒
关注关注
感谢关注 蛤蛤。。
MOMO哥请教一下,怎么在寻路的时候实现多端同步呢?比如点击了某个目标位置,然后多台终端显示的角色的位置信息、朝向等都是同步于服务器的,momo给个思路吧,谢谢。
这就是 mmorpg 同步坐标嘛。 网上例子非常多, 你谷歌一下吧。。。
momo好懒啊你!! 上次发文到这次间隔了一个月…….
其实是不知道写什么。 你可以把你想知道的告诉我 我抽时间写点 蛤蛤
a? b?=c?好吧 这你居然不知道怎么打!!!!!!!!!!!!!!!!!!
a²+b²=c²好吧 这你居然不知道怎么打!!!!!!!!!!!!!!!!!!
不知道啊 怎么打的 哈哈哈
自己沙发! 加油蛤蛤。。