Unity3D封装了一个非常好用的组件来实现第一人称视角与第三人称视角游戏开发,我们称他为角色控制器组件,几乎不用写一行代码就可以完成一切的操作,神奇吧。使用它的方法如下,首先打开Unity游戏引擎编辑器,然后在Project视图中右键选择Import Package -> Charactr Controller(角色控制器)把它导入我们的工程中。如下图所示,第一人称与第三人称的组建已经加入Project视图中。3rd Person Controller 表示第三人称控制器,First Person Controller表示第一人称控制器。
如下图所示,我们将FirstPerson Controller拖拽入Hierarchy(层次视图)中。由于角色控制器是具有一定物理引擎的,所以一定要将它放在地形或面对象之上,否则当它接收物理效果时发现地面没有东西支撑它,它就会掉下去。然后运行游戏你就会发现和CS中的第一人称效果非常相像, W、S、A、D移动人物行走,移动鼠标更改行走的方向,空格键人物会跳跃。
第一人称视角的实现原理是在游戏场景中创建了一个胶囊体的游戏对象,并且给胶囊体对象身上绑定了一个摄像机,摄像机对象如下图所示,它绑定在”Person Controller”中。这时场景中默认的摄像机就会失效,可以直接删掉默认的摄像机。通过按键控制这个胶囊体移动,通过鼠标修改胶囊体的朝向,此时你就会发现第一人称视角已经完全实现,目前为止我们不需要编写一行代码。目前场景中的天空盒子我是使用skyBox组件绑定在摄像机中,因为第一人称视角的摄像机对象在”Person Controller”中,所以需要将SkyBox组件绑定在这个摄像机中,如果绑定在默认摄像机中那么你将不会看到天空的效果。
(点击图片查看大图)
下面我们在看看第三人称视角,如下图所示,在Project视图中将3rd Person Controller拖拽入Hierarchy视图中。第三人称视角需要使用我们原有的摄像机,如果刚刚将摄像机的删掉的话。在Hierarchy视图中点击Creat->Camera 即可。然后选择摄像机,在右侧Inspector视图中设置它的tag为MainCamera,如下图所示。最后在Hierarchy视图中选择3rd Person Controller,在右侧Inspector视图中将Third Person Camera 脚本的 Camera Transform 变量绑定上刚刚创建的主摄像机,此时运行游戏后以第三人称视角移动主角行走与跳跃,摄像机永远都会在跟随在后面除非修改角色控制器组件中默认提供的源码,源码都在右侧监测面板视图中直接点开就可以查看。
(点击图片查看大图)
下面我们学习角色控制器组件在其它模型之间的应用。首先在Hierarchy视图中创建两个Cube(立方体对象) 命名为:Cube0(发出碰撞的对象)Cube1(接收碰撞的对象),然后在Hierarchy视图中选择Cube0对象,接着Unity导航菜单栏中选择Component(组件)-Character->选择任意一个角色控制属性。 补充一句,角色控制器组件一定要在Project视图中导入,否则这里将无法绑定组件。角色控制器组件因为与碰撞组件相互冲突,所以添加角色控制器组建后Collider组件就会消失。下面我们实现一段简单得代码,使用添加过角色控制器组件的Cube0 去碰撞未添加角色控制器组件的Cube1。
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 40 41 42 43 44 |
using UnityEngine; using System.Collections; public class Test : MonoBehaviour { //主动碰撞的对象名称 string castName = null; //接收碰撞的对象名称 string receiveName = null; void OnGUI () { if(castName!= null && receiveName !=null) { //设置显示的颜色为黑色 GUI.color = Color.black; //显示主动碰撞的对象 与接收碰撞的对象名称 GUI.Label(new Rect(100,100,200,30),"主动碰撞的对象名称"+castName); GUI.Label(new Rect(100,200,200,30),"接收碰撞的对象名称"+receiveName); } } //角色控制器组件在与具有Collider组件对象之间的碰撞 void OnControllerColliderHit(ControllerColliderHit hit) { //得到接收碰撞名称 GameObject hitObject = hit.collider.gameObject; //当它不是地面时间 if(!hitObject.name.Equals("Terrain")) { //得到主动碰撞的对象 与接收碰撞的对象名称 castName = gameObject.name; receiveName = hitObject.name; } } } |
将上面这段代码绑定在Cube0中,运行游戏后W、A、S、D按键来控制Cube1立方体移动。当Cube0与Cube1发生碰撞时,程序将进入方法OnControllerColliderHit(),通过参数就可以得到接收碰撞的游戏对象也就是Cube1对象,而gameObject就是当前主动发生碰撞的Cube1。如下图所示,当两个立方体碰撞时使用GUI已经将碰撞的信息打印出来。
接着我在说说刚体组件,默认在Unity中创建的模型是不具备接收物理引擎的,除非给模型添加刚体组件或角色控制器组件。我们先说说刚体,还用奋斗的小鸟来举例子。发射小鸟以后,小鸟以一个抛物线轨迹去撞击物体,发生碰撞后被碰撞的物体会根据小鸟撞击的角度以及力度发生不同的物理效果,并且几乎是完全模拟真实的物理引擎。但是这种物理引擎的效果不能绑定在比如RPG游戏的主角身上。原因很简单,因为刚体所添加的物理引擎太过于真实以至于会影响用户的对主角的操作,举个例子比如用户在控制主角移动时他碰撞到质量较大的物体,根据真实的物理引擎会被这个物体的反弹力把主角反弹回原位。但是这个是不符合逻辑的,因为刚体组件太过于物理话了,所以我们需要给主角添加角色控制器组件,它操作起来比较灵活,更容易让我们操作主角。
下面我们给Cube1对象绑定刚体组件,选择Cube1对象然后在导航菜单栏中选择Component->physics ->Rigidbody(刚体)。我们看看下面这段代码,使用添加了角色控制器组件的Cube0 去碰撞添加刚体组件的Cube1,当他生碰撞时计算一下Cube0碰撞Cube1时的碰撞角度向量,然后通过刚体向他施加一个力把它推开。
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 |
using UnityEngine; using System.Collections; public class Test : MonoBehaviour { //角色控制器组件在与具有Collider组件对象之间的碰撞 void OnControllerColliderHit(ControllerColliderHit hit) { //判断碰撞的对象是否具备刚体组件 GameObject hitObject = hit.collider.gameObject; Rigidbody rigidbody = hitObject.rigidbody; if(rigidbody != null && !rigidbody.isKinematic) { //地面也具备刚体组件,这里判断一下 if(!hitObject.name.Equals("Terrain") ) { rigidbody.AddForce(new Vector3(hit.moveDirection.x, 0, hit.moveDirection.z) * 10); } } } } |
也可以将同样的脚本直接绑定在第三人称的角色控制器组件中。如下图所示,主角移动将周围的箱子都推开了。
总的来说角色控制器组件适用于 既需要感应物理引擎的支持但是又不能完全依赖与物理引擎,需要自己代码去编写一些东西的模型,所以非常适合游戏中主角对象的使用。雨松MOMO祝大家学习愉快,哇咔咔。
- 本文固定链接: https://www.xuanyusong.com/archives/810
- 转载请注明: 雨松MOMO 于 雨松MOMO程序研究院 发表
MOMO老师 我没有找到那个控制器欸
老师,在第一人称模式下还能再建一个Camera并让这两个Camer一个开时一个关么?
老师,添加了CharacterController后 主角怎么和场景地形碰撞呢?会直接穿过去的哦
momo老师,我通过摇杆来控制人物后,人物就可以穿墙了,用键盘就不会,这是什么原因呀?
求解 如何把First Person Controller设置成可以穿墙的效果
MOMO老师,我在第一人称前进的时候,没按键盘了,结果还是会前进一下,冲一下才停,这怎么解决?
MOMO桑 ,求教一个问题 ,我用rigidbody来做碰撞,并且在物理材质里面把弹力设置为0,但是当我的物体撞在其他物体上 还是会弹开,只是没有那么夸张了。我想做的是完全没有弹力的碰撞,请问应该怎么做呢?谢谢~
MoMo老师,我在第一人称下,用鼠标点击一个位置,然后就移动到这个位置,这可以实现吗?愿闻其详,新手请谅解。
Unity3D研究院之鼠标控制角色移动与奔跑示例(二十四)http://www.xuanyusong.com/archives/841看看这里吧。。
谢谢MoMo老师,我一定看懂…
MOMO老师,我有个问题求教:我用系统的Third Person Controller来控制人物。我想增加一个控制,就是点击鼠标左键—人物攻击—播放攻击动画,但是现在人物的攻击动作刚抬手就结束了,整了好几天了都没法搞定,不知道怎样修改代码才能实现啊。
我再补充一下:我的人物只有在运动中跳起攻击时才会播放完整的攻击动画,原地跳起都无法播放完整攻击动画,愁啊!
你仔细阅读一下角色控制器的源代码。。 它里面每一帧默认播放站立动画,所以你的动画就没有播放。 需要修改它的代码。。
谢谢。我已经搞定了。确实是每一帧播放站立动画的原因。没想到做个游戏这么复杂。一起步就掉坑里了。
MOMO,我是一个初学者,很崇拜您,您六月份出版的那本 《Unity3D 游戏开发》上面的例题80%都实现了一遍,在写书上第十章的例子时,遇到一个问题:同时添加 系统的两个机器人(一个第一视角,一个第三视角)时,那个Hero 的摄像机 运行时虽然跟Hero移动但位置一直不对,把Enemy删掉就正常了,找了很久都没有找到原因,希望您能给一些提示,谢谢啦…..
我也来问问题>_<是这样的,我帮一个人物绑定了CharacterController,但是CharacterController的Move好像基本不受物理效果的影响,那么我现在想做一类陷阱例如一个大石头吊在天花板上做钟摆运动,撞到人的时候希望把人推开,这应该怎么写?是要帮这个大石头再加一段脚本判断它碰到人物的时候施加一个推力吗?那假如这类陷阱很多种多样的话会不会写起来很麻烦?
给你的钟 放上刚体组件, 碰到角色控制器的话会有碰撞的
其实我的问题应该说是由物品的发起的CharacterController的移动,CharacterController的所有移动好像都只能通过Move来执行,那么假如上述例子里面我要在人物和物品产生碰撞的时候对人进行一个与碰撞点反方向的移动么?但是只知道碰撞信息的话不清楚要移动多少距离才能令到两者不碰撞?是需要每个可能主动令到人物产生位移的物品都要写一套计算在碰撞的时候需要把人物推动多少的方法么?
請問我做了一個長條狀的方塊想要推它的最邊邊可是依常理來看應該是我推的地方會移動的比較多另外一邊幾乎是不動的請問你有辦法改成更符合正常狀況的碰撞的移動嗎?因為這樣的話它變成整條一起移動好像怪怪的…
你是给箱子添加的是一个力吗? rigidbody.AddForce 吗?
對阿我是用rigidbody.AddForce沒錯但這不是我要的感覺我剛剛測試了一種寫法就得到了我想要的感覺我把test.cs加進了人物並改了一下程式碼變成了void OnControllerColliderHit(ControllerColliderHit hit) { GameObject hitObject = hit.collider.gameObject; Rigidbody rigidbody = hitObject.rigidbody; if(rigidbody != null && !rigidbody.isKinematic) { if(!hitObject.name.Equals(“Terrain”) ) { rigidbody.AddForceAtPosition(new Vector3(hit.moveDirection.x, hit.moveDirection.y, hit.moveDirection.z)*10, transform.position); } } }我把它變成了rigidbody.AddForceAtPosition就剛好是我需要的功能了也可以實現翹翹板的功能了版大你可以試試看可是不知道這樣寫會不會有其他問題哈哈
rigidbody.AddForceAtPosition 这个方法是向一个方向是一个力 总之实现了就好,感谢来访 嚯嚯
呵呵對了版大我忘了自我介紹我是台灣一個對遊戲設計有興趣的人你可以叫我的外號「小i」如果以後有問題可以麻煩你嗎XD
当然没问题, 大家互相学习 欢迎港澳台的开发同胞。
對了版大你的書在廈門的書局買的到嗎?我爸剛好最近去廈門想說要叫他幫我買一下
對阿我是用rigidbody.AddForce沒錯但這不是我要的感覺我剛剛測試了一種寫法就得到了我想要的感覺我把test.cs加進了人物並改了一下程式碼變成了void OnControllerColliderHit(ControllerColliderHit hit){GameObject hitObject = hit.collider.gameObject;Rigidbody rigidbody = hitObject.rigidbody;if(rigidbody != null && !rigidbody.isKinematic){if(!hitObject.name.Equals(“Terrain”) ){rigidbody.AddForceAtPosition(new Vector3(hit.moveDirection.x, hit.moveDirection.y, hit.moveDirection.z)*10, transform.position);}}}我把它變成了rigidbody.AddForceAtPosition就剛好是我需要的功能了也可以實現翹翹板的功能了版大你可以試試看可是不知道這樣寫會不會有其他問題哈哈
如果要退出鼠标控制场景,想让鼠标能够点击场景中的其他对象并有其他操作,怎么改?
使用射线 向鼠标点击的方向发射一条实现 查看这条射线是否碰撞在游戏对象身上 如果碰撞 根据碰撞的游戏对象名称判断你的操作。
很收益,出的书名字是什么啊?也不介绍下
好小子,虽然我不懂游戏,但这个看着很爽….加油,MOMO,支持…..努力
感谢小马, 要常来喔、如果可以的话也希望你也能写一些东西。
继续努力