人物的名称与血条的绘制方法很简单,但是我们需要解决的问题是如何在3D世界中寻找合适的坐标。因为3D世界中的人物是会移动的,它是在3D世界中移动,并不是在2D平面中移动,但是我们需要将3D的人物坐标换算成2D平面中的坐标,继而找到人物头顶在屏幕中的2D坐标最后使用GUI将名称与血条绘制出来。
首先学习本文的重点内容,如何将游戏世界中任意3D坐标转换成屏幕中的2D坐标。根据这个方法计算出的2D坐标屏幕左下角的点为0.0 ,屏幕右上角的坐标为1.1 所以真实的2D坐标还得通过Screen.height 与Screen.width计算一下才行。
1 2 3 |
Vector2 position = camera.WorldToScreenPoint (worldPosition); |
在Unity工程导入角色控制器组件,不知道角色控制器的朋友请阅读我之前的文章哈。创建一个Plane做为游戏的地面,然后利用角色控制器组件创建两个模型,一个做为主角,一个作为NPC,主角可以通过控制来移动从四周来观察NPC对象。由于地面的面积比较小移动主角时为了避免主角越界掉下去,我们做一个边界的物理层。物理层其实很简单,就是给平面四周放置四个平面在四周将平面包围着,给四周的四个平面绑定上Box Collider组件,这样主角就不会越界掉下去啦。因为没有给贴图所以效果上看不到这四个对象。哇咔咔~ 如下图所示,在场景是途中主角被四个平面包围这,即时它拼命的想往外条但是还是跳不出去,哈哈。
创建脚本NPC.cs 然后把脚本挂在NPC对象身上,在脚本中我们绘制主角的血条以及名称。
NPC.cs
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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
using UnityEngine; using System.Collections; public class NPC : MonoBehaviour { //主摄像机对象 private Camera camera; //NPC名称 private string name = "我是雨松MOMO"; //主角对象 GameObject hero; //NPC模型高度 float npcHeight; //红色血条贴图 public Texture2D blood_red; //黑色血条贴图 public Texture2D blood_black; //默认NPC血值 private int HP = 100; void Start () { //根据Tag得到主角对象 hero = GameObject.FindGameObjectWithTag("Player"); //得到摄像机对象 camera = Camera.main; //注解1 //得到模型原始高度 float size_y = collider.bounds.size.y; //得到模型缩放比例 float scal_y = transform.localScale.y; //它们的乘积就是高度 npcHeight = (size_y *scal_y) ; } void Update () { //保持NPC一直面朝主角 transform.LookAt(hero.transform); } void OnGUI() { //得到NPC头顶在3D世界中的坐标 //默认NPC坐标点在脚底下,所以这里加上npcHeight它模型的高度即可 Vector3 worldPosition = new Vector3 (transform.position.x , transform.position.y + npcHeight,transform.position.z); //根据NPC头顶的3D坐标换算成它在2D屏幕中的坐标 Vector2 position = camera.WorldToScreenPoint (worldPosition); //得到真实NPC头顶的2D坐标 position = new Vector2 (position.x, Screen.height - position.y); //注解2 //计算出血条的宽高 Vector2 bloodSize = GUI.skin.label.CalcSize (new GUIContent(blood_red)); //通过血值计算红色血条显示区域 int blood_width = blood_red.width * HP/100; //先绘制黑色血条 GUI.DrawTexture(new Rect(position.x - (bloodSize.x/2),position.y - bloodSize.y ,bloodSize.x,bloodSize.y),blood_black); //在绘制红色血条 GUI.DrawTexture(new Rect(position.x - (bloodSize.x/2),position.y - bloodSize.y ,blood_width,bloodSize.y),blood_red); //注解3 //计算NPC名称的宽高 Vector2 nameSize = GUI.skin.label.CalcSize (new GUIContent(name)); //设置显示颜色为黄色 GUI.color = Color.yellow; //绘制NPC名称 GUI.Label(new Rect(position.x - (nameSize.x/2),position.y - nameSize.y - bloodSize.y ,nameSize.x,nameSize.y), name); } //下面是经典鼠标点击对象的事件,大家看一下就应该知道是什么意思啦。 void OnMouseDrag () { Debug.Log("鼠标拖动该模型区域时"); } void OnMouseDown() { Debug.Log("鼠标按下时"); if(HP >0) { HP -=5 ; } } void OnMouseUp() { Debug.Log("鼠标抬起时"); } void OnMouseEnter() { Debug.Log("鼠标进入该对象区域时"); } void OnMouseExit() { Debug.Log("鼠标离开该模型区域时"); } void OnMouseOver() { Debug.Log("鼠标停留在该对象区域时"); } } |
注解1:通过collider.bounds.size 可以拿到模型对应三个轴向的高度,但是模型是可以缩放的,所以真实的模型高度应当是原始高度乘以缩放系数才行。 transform.localScale可以拿到模型对应三个轴向的缩放系数,因为这里我们需要模型的高度,所以忽略X轴与Z轴。
注解2:在这里我们计算血条的宽度,GUI.skin.label.Calcsize()这个方法是以默认的皮肤对象Label对象去参数对象的宽高。参数是new GUIContent(blood_Red)意思是拿红色血条的贴图的宽高,它将保存在返回的size中。最后以宽高将血条绘制在屏幕中,我们的血条采取两层。背景是黑色的,前面是红色的,当人物费血时红色血条减少。
注解3: 这里通过字符串来获取它整体的宽度与高度,因为NPC的名称是可变的,所以我们需要动态的获取整体的显示区域。同样是以GUI.skin.label对象去调用CalcSize。
如下图所示,当使用鼠标点击NPC对象时,NPC头顶的血条将开始发生减血。这个例子我使用OnGUI绘制当然大家也可以在Hierarchy 视图中的创建GUI Texture 或者GUI Text对象 来实现,不过原理都是这样的 大家可以试试 哇咔咔。
最后雨松MOMO希望和大家一起进步,一起学习,哇咔咔~
本章内容下载地址:http://vdisk.weibo.com/s/abYBx
补充,现在都用NGUI来做血条,大家看看我的这篇文章吧。http://www.xuanyusong.com/archives/2644
- 本文固定链接: https://www.xuanyusong.com/archives/1032
- 转载请注明: 雨松MOMO 于 雨松MOMO程序研究院 发表
为什么不用UGUI呢, 是因为更消耗性能吗
大神,刚好需要用,但是我是做一个类似球球大作战的游戏,球球会有缩放,并且缩放比例很大,我发现这个脚本并不行,,,我觉得找了一些方法实现了正真意义上的无论缩放多少都可以刚好在头顶血条测试有效并使用,帮到大家最好
//修改了一下,得到模型原始高度
size_y = GetComponent().mesh.bounds.size.y/2;
我们在做一个2d卡牌游戏,需要在卡牌(Sprite)上绘制卡牌的文本,参照这个文章做了之后,发现有一些问题,不知道能否指导一二
1、这个方法的文字大小是固定的,不会随着分辨率变化而变化
2、不知道如何处理层次问题,这个方法绘制的文本似乎会挡住其他层
3、在MainCamera使用模糊滤镜之后,发现绘制的文本不会模糊
不知道是否有更官方的方案,还望指导
问下,如果是写在onGUI里面的话也就意味着是全程显示血条了…有什么办法能隐藏血条,在特定情况下才会显示血条以及名字么?
为啥要写在OnGUI里呢?
我是说你的代码阿。。。血条绘制的过程不是写在OnGUI里面么,然后我试了一下好像没办法隐藏。。。
如果不写在OnGui里面的话应该怎么写呢?我自己试了一下,好像都失败了,一定要引用GUI的东西,然后我又试了一下在OnGUI里面添加一个bool,如果bool是true的话才开始绘制血条…然而还是没用..
非常感谢雨松,我照着做了实现了这个效果。不过绘制出来的血条把游戏的UI给遮挡了请问该怎么解决呢? 我UI是用UGUI做的。场景摄像机深度0,UI摄像机深度1.
要改深度,, UGUI有自己的深度。
我看了你的这篇文章了http://www.xuanyusong.com/archives/3293。不过GUI.DrawTexture()方法绘制出来的是个什么东东呢? 怎样获取它然后与画布交换深度呢?
我看了你的这篇文章了http://www.xuanyusong.com/archives/3293。不过GUI.DrawTexture()方法绘制出来的是个什么东东呢? 怎样获取它然后与画布交换深度呢?
同学,按你的作法尝试做了一下,unity编辑状态中的 public Texture2D blood_red; public Texture2D blood_black;是不是要加入图片,不加入的话,发现出错,但是加入了之后发现血条是拖入的图片
这个血条是2d的,不会根据离敌人的远近而变大变小。我觉得还需要优化。
雨凇大大,好久没来了,有个问题啊,你这样计算模型的高度是不是欠妥啊,你这只是计算了碰撞体的高度,没有计算模型的实际高度啊。。。应该通过mesh来算吧。。。期待大大的回复。
对头,应该是这么做
楼上的方法不行,不是z轴正负的原因,是相机坐标小于零的原因。还没发现解决方法。我是在另一个项目里解决这个问题,不是您发的场景里面,但是问题原因应该是一样的。
momo啊,你的package我打开就崩溃。但是我把你的代码加入我的场景中,帧数由70掉到20,把你的脚本disable后由恢复到70帧。开始怀疑是因为js和cs 脚本的互调导致。但是我把你的脚本改成js之后还是帧数下降严重。是不是计算坐标导致的啊?如果这样的话,这个取坐标功能不是很废?我是安卓平台。
程序有两点需要注意的地方:1、当Y轴缩放比例过大时,npcHeight会特别大,后面会导致负值,从而血条在NPC的太上方。1以内没什么明显影响。2、当背对NPC时,也显示血条。相关代码修改如下:Vector3 position = camera.WorldToScreenPoint (worldPosition);if position.z>0这样就可以消除了。这两点回复都说到了,我只是总结一下,希望大家能互相学习。第一点是那个函数WorldToScreenPoint转换问题,我没有什么修改办法,大神们有什么建议没?
我創建兩個物體,一個主角,一個怪物,按照雨松MOMO老師腳本~有主要攝影機關係,所以我把攝影機掛在人物上,把腳本丟在怪物上,怪物頭上會顯示血條,可是人物拉進那遠時候,發現人物走近怪物血條會變短,人物移遠怪物血條變長,請問腳本該怎麼改善。還是我從雨松老師腳本會錯意,攝影機關係弄錯 請老師指點謝謝~。我有試著,在創鍵一個攝影機,可是發現同時只能一個主要攝影機不然他會說攝影機關係模糊不清,發生錯誤。 private Camera camera;camera = Camera.main;
游戏中其实 可以让美术去做一个片 这个片 始终面朝摄像机、、、
请问雨MOMO松大大原本血条可以显示结果从新执行后他就一直显示这3行错误1.The name blood_red’ does not exist in the current context2.The best overloaded method match for
UnityEngine.GUI.DrawTexture(UnityEngine.Rect, UnityEngine.Texture)' has some invalid arguments3.Argument
#2′ cannot convertobject' expression to type
UnityEngine.Texture’我用GOOGLE翻译意思好像无效参数與图片不能转换的意思请问怎么解决?请大大帮忙