首页 > Unity3D频道 > 【Unity3D研究院之游戏开发】 > Unity3D研究院之处理摄像机跟随避免相机穿墙拉近或透明的方法(四十四)
2013
01-05

Unity3D研究院之处理摄像机跟随避免相机穿墙拉近或透明的方法(四十四)

          当摄像机处于跟随主角状态时,那么主角移动后很有可能摄像机被别的模型挡住。这样用户体验就非常不好,一般3D游戏在处理视角的时候有两种方法,第一种是让被挡住的模型变成透明,第二种是拉近摄像机。前者时候固定视角游戏使用,后者适合变化视角游戏使用。两个我们都学一学蛤蛤。

          如下图所示,MOMO写了一个简单的例子,通过鼠标或触摸来牵引主角向不同的角度移动,旁边有一面墙当主角被这面墙挡住,此时摄像机将拉近。  

    Unity3D研究院之处理摄像机跟随避免相机穿墙拉近或透明的方法(四十四) - 雨松MOMO程序研究院 - 1

 

首先,为了方面鼠标与移动平台上的触摸同时相应我写了一个通用的方法。

包括 开始触摸  触摸中 结束触摸 ,电脑手机都可以用这个方法相应到。

JFConst.cs

 

以前写过一篇文章关于移动主角的,如果对移动主角还有不懂的请看这篇 Unity3D研究院之鼠标控制角色移动与奔跑示例(二十四)  。 这里我就不解释移动主角这部分的代码了。 下面是角色控制器ThirdPersonController.cs 绑定在主角身上。
下面代码中需要详细看的就是161行到174行 ,这部分就是根据触摸屏幕的2D坐标计算主角在3D世界中移动的坐标。

 

如此我们就可以通过鼠标与触摸牵引控制主角移动了,别急这紧紧是开始,下面是本章的要点。

这里我们分析一下摄像机到底什么时候该拉近,什么时候该不拉近。 我的作法是这样的,当角色移动的时候我会从主角身上向摄像机方向发射一条射线,如果射线碰撞到的第一个对象是“摄像机” 那么就标示主角和摄像机之间没有别的模型挡住,此时摄像机就不应该拉近。如果射线碰撞到的第一个对象不是摄像机,那么就标示主角和摄像机之间有别的模型所挡住,此时取得射线与别的模型碰撞的3D坐标接着将“摄像机”的坐标移动到这个坐标上即可。

如下图所示,(红色表示由主角发射到摄像机的射线)主角身后的射线没有被别的模型挡住,射线机不会拉近。

 

Unity3D研究院之处理摄像机跟随避免相机穿墙拉近或透明的方法(四十四) - 雨松MOMO程序研究院 - 2

 

如下图所示,MOMO改变了一下主角的位置,此时主角身后的射向已经被墙挡住,这时把摄像机的坐标移动到被墙挡住点的坐标,这样摄像机就拉近了。

Unity3D研究院之处理摄像机跟随避免相机穿墙拉近或透明的方法(四十四) - 雨松MOMO程序研究院 - 3

 

原理大概就是这样,接着我们学习一下这段代码该如何来写。

MyCamera.cs 挂在射线机上

 

牛头人被墙挡住了,摄像机直接拉近了,小牛头人是不是很帅气?哈哈哈!!

 

Unity3D研究院之处理摄像机跟随避免相机穿墙拉近或透明的方法(四十四) - 雨松MOMO程序研究院 - 4

 

最后雨松MOMO把源码放出来,希望大家学习愉快,哇咔咔。。。

下载地址:http://vdisk.weibo.com/s/mAhiI

 

补充 :摄像机遮挡物体透明。

遮挡透明最简单的办法就是修改材质的透明度,前提需要把材质设置成支持透明通道的 shader Transparent 。 如下图所示,摄像机挡住的那面墙已经成透明状态了。

 

Unity3D研究院之处理摄像机跟随避免相机穿墙拉近或透明的方法(四十四) - 雨松MOMO程序研究院 - 5

 

看看代码是如何写的,把上述代码MyCamera.cs 简单的改一下就可以。 主要是92行到101行。

代码核心就是通过射线得到碰撞模型的材质,然后修改材质的透明度。color.a就是透明度 1是完全不透明,0是完全透明。注意一定是支持透明通道的模型才可以。lastobj是记录上次透明的对象,用于模型透明还原使用。

 

最后,假设你的模型做的非常大,贴图材质也非常的多,你像透明其中一小部分那么上述方法就不合适了,除非把他们都拆出来。我觉得也可以自己写shader实现。我们的项目采用了摄像机拉近的方式。欢迎讨论!!!

 

回答楼下问题:上帝视角代码,看到楼下有朋友问我,那么我就贴出来,其实很简单。下面代码挂在摄像机上, target就是角色对象,distance 是摄像机与角色的距离 height是摄像机与角色的高度。在编辑器中手动调节即可。

 

 

回答问题 :摄像机抖动如何修改

今天微薄上有个朋友问我这样的问题,我在这里解答一下。

来福12345:我又厚颜无耻的来了,您的《Unity3D的研究院之处理摄像机跟随避免相机穿墙拉近或透明的方法(四十四)》对于不规则的物体该怎么弄,我是直接在unity上弄了几个山脉,由于地面凹凸不平,沿着山走的时候发现摄像机会一直透视,而且抖动的很厉害,请问该怎么解决啊

 

1.从设计上避免射线遇到一些复杂的面,如下图所示,红颜色是主角身后的射线,当主角身后的射线碰撞到这样的面,如果此时移动速快快一些摄像机肯定会抖动。 这里最好直接删除Mesh Collider 换成BoxCollider 效率还可以得到提升。产经设计这块我觉得可以参考一下魔兽世界,我在项目中遇到这样问题的地方就是 比较小的门(棱角那种) 或者这样的树。

 

Unity3D研究院之处理摄像机跟随避免相机穿墙拉近或透明的方法(四十四) - 雨松MOMO程序研究院 - 6

 

 

2.但是还有一些比较特殊的地方,如果你的项目必需使用MeshColider 的话 我建议你写触发脚本来修改摄像机,假设 当摄像机被挡住后,变成跟随视角,只到摄像机没被挡住的时候在回到原来视角。

3.我检查了一下上面的代码中我们在加一个判断,应该就可以解决大部分问题、在LateUpdate () 中最后加入代码

 

bool closer 是表示是否拉近。 当摄像机被挡住的时候这个数值等于true;

 

当closer等于假的时候,此时直接修更新摄像机的位置,因为没被挡住。 当closer等于真的时候,摄像机已经被挡住了,我们不修改摄像机的位置,计算摄像机保证不被挡住时,摄像机此时和主角的距离是多少,然后修改distance全局变量即可。当摄像机不被遮挡的时候在恢复之前的距离。 如有问题请留言,我会即时解答。。 谢谢

 

 

//补充/////////

之前的代码还是有点问题,后来我又仔细的重构了一下摄像机的代码。下面的代码在我的项目中已经完美无暇的模拟摄像机拉近了。。 代码对外有两个公有类型, 一个是模型的 一个是模型位置 与头顶的偏移, 因为射线应该是重头顶向后发射的。

 

 

 

雨松MOMO提醒您:亲,如果您觉得本文不错,快快将这篇文章分享出去吧 。另外请点击网站顶部彩色广告或者捐赠支持本站发展,谢谢!

--

最后编辑:
作者:雨松MOMO
专注移动互联网,Unity3D游戏开发
捐 赠如果您愿意花10块钱请我喝一杯咖啡的话,请用手机扫描二维码即可通过支付宝直接向我捐款哦。

  1. 大神还在嘛,好像这个脚本还有点小问题,不知道怎么上传图片,之好符号代替了_____←墙2 当射线第一次碰到墙1的时候 |←墙1 可以拉近相机 |O←相机 | | ←射线 | | O角色_____←墙2 相机离开墙体的时候会穿过去, O |←墙1 碰到墙2,这时墙1又遮挡了 相 | 机 | | ←射线 | | O角色是不是因为默认射线的起点不对啊

  2. momo老师你好,请教一下,UpdateSmoothedMovementDirection函数是否只是算出移动的方向?我想知道屏幕的2D坐标在3D世界中的具体坐标,该怎么算呢?

  3. 再问一个问题,如果写游戏时对一个对象编写了2种第三人称的控制代码,一个是自动跟踪,另一个是鼠标自由控制(可以360°观察),我想在游戏中通过按钮切换不同的控制效果,是不是用组件的enable来控制就可以了??还有什么别的便捷高效的做法吗?!

  4. 雨松老师,我想请问下,最后你重构的代码中,哪个参数可以修改,摄像机跟随物体时,旋转的速度?我改了好多个,都没改出来!! Unity3D研究院之处理摄像机跟随避免相机穿墙拉近或透明的方法(四十四) - 雨松MOMO程序研究院 - 1

  5. 你好。。。我请问一下,有时候会出现这种问题,怎么解决,并且,你这个场景比较简单,假如东西多的话,这种问题会出现很多。

  6. 你好,使用了你最后发布的代码,目标跟随一辆赛车,赛车前进的时候,发现画面有一顿、-顿的抖动现象,这是为什么?要调整那些参数?谢谢指点

  7. 写得挺不错的。不过你上面的关于透明度的那段还是有点问题。这种方法只适合一个物体挡住的情况,多个物体挡住的情况只能一个物体变透明,建议用RaycastHit[] hit来判断。

  8. 雨松老师,请教一下,像计算机单机<<游击队保罗>>(保罗队长)游戏中,若两人或多人分屏(split screen)模式下摄像机及人物移动到边界时如何控制?描述:当两人或多人向同一方向移动时摄像机跟随移动,当不同方向移动时,若人物移动到边界时人物将不能移动

  9. Vector3 aim = target.transform.position; //得到方向(主角到相机): Vector3 ve = (target.transform.position – transform.position).normalized; float an = transform.eulerAngles.y; aim -= an * ve ; //在场景视图中可以看到这条射线 Debug.DrawLine(target.transform.position,aim,Color.red); //主角朝着这个方向发射射线 RaycastHit hit; if(Physics.Linecast(target.transform.position ,aim,out hit)) { string name = hit.transform.gameObject.tag; if(name != “MainCamera” && name !=”terrain” && name!=”Plane” ) { //当碰撞的不是摄像机也不是地形 那么直接移动摄像机的坐标 transform.position = hit.point; } }这段代码我试了一下,有时能捕获碰撞,有时却不行。不行的时候大多是开始时角色移动到障碍后面,障碍挡住了相机的视线,但依然无法获得碰撞。能捕获碰撞的时候是角色不动,相机以角色为中心旋转的时候,当障碍挡住相机视线时

  10. NullReferenceExceptionUnityEngine.Component.get_transform () (at C:/BuildAgent/work/cac08d8a5e25d4cb/Runtime/ExportGenerated/Editor/UnityEngineComponent.cs:21)ThirdPersonController.UpdateSmoothedMovementDirection () (at Assets/ThirdPersonController.cs:138)ThirdPersonController.Update () (at Assets/ThirdPersonController.cs:329)指向的代码为 Transform cameraTransform = Camera.main.transform;

  11. 求老师解析下:Vector3 ve = (target.position – transform.position ).normalized; float an = transform.eulerAngles.y; aim -= an * ve;这里不是十分理解。。。

  12. 您好,我想请问一下,unity3d添加碰撞可以通过component——>physics——>mesh collider来实现,请问如何用程序实现上一过程呢?即在程序运行过程中通过鼠标键盘来控制碰撞的添加。

  13. Pingback: 在公司摔键盘之后,整理来年要解决的技术要点(人工置顶不断更新中)。

  14. 刚刚我跟 那边提了个建议 被鄙视了 各位看看我的想法到底有多幼稚哈我们做一个 MMO类型的,我的建议是,整个分4个国家,A B C D广东这一代的用户登录就是 A 国上海 浙江 江苏 登录就是 B 国北京天津东北 就是 C 国....  就是D 国然后 各地 搞PK    3天一次国战各位 看着想法咋样呢

  15. 貌似找到两个错误:1,83行,Vector3 aim = target.position; 应该改为Vector3 aim = transform.position;否则射线的起始点都是一样的;2, 用于还原透明模型的else{}语句,应该放在if(Physics.Linecast(target.position,aim,out hit)) 外,否则不会执行到这里。另外补充一个:设置shader模式:bj.renderer.material.shader = Shader.Find(“Transparent/Diffuse”);设置为这样就可以实现半透明了。momo,我这样的学生勤奋吧,嘿嘿

  16. 雨松老师你好!最近买了你的<unity3d游戏开发>在研究!我现在想实现一个效果!就是我吧自己的模型做成预览效果!靠鼠标点击屏幕后拖动来改变摄像机的旋转角度,以达到可以360度行赏模型的效果!我现在看到粒子系统这一章节了,还是没什么头绪,希望雨松老师能给个思路,或说说改用什么方法来实现!求教了!

  17. 雨松,请问一下,我现在的主摄像机是一个俯视的视角,我想实现一个上帝视角,应该怎么做?直接Translate相机的话貌似因为相机本身是俯视所以造成相机不是水平移动