人物的名称与血条的绘制方法很简单,但是我们需要解决的问题是如何在3D世界中寻找合适的坐标。因为3D世界中的人物是会移动的,它是在3D世界中移动,并不是在2D平面中移动,但是我们需要将3D的人物坐标换算成2D平面中的坐标,继而找到人物头顶在屏幕中的2D坐标最后使用GUI将名称与血条绘制出来。
首先学习本文的重点内容,如何将游戏世界中任意3D坐标转换成屏幕中的2D坐标。根据这个方法计算出的2D坐标屏幕左下角的点为0.0 ,屏幕右上角的坐标为1.1 所以真实的2D坐标还得通过Screen.height 与Screen.width计算一下才行。
Vector2 position = camera.WorldToScreenPoint (worldPosition);
在Unity工程导入角色控制器组件,不知道角色控制器的朋友请阅读我之前的文章哈。创建一个Plane做为游戏的地面,然后利用角色控制器组件创建两个模型,一个做为主角,一个作为NPC,主角可以通过控制来移动从四周来观察NPC对象。由于地面的面积比较小移动主角时为了避免主角越界掉下去,我们做一个边界的物理层。物理层其实很简单,就是给平面四周放置四个平面在四周将平面包围着,给四周的四个平面绑定上Box Collider组件,这样主角就不会越界掉下去啦。因为没有给贴图所以效果上看不到这四个对象。哇咔咔~ 如下图所示,在场景是途中主角被四个平面包围这,即时它拼命的想往外条但是还是跳不出去,哈哈。
创建脚本NPC.cs 然后把脚本挂在NPC对象身上,在脚本中我们绘制主角的血条以及名称。
NPC.cs
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来做血条,大家看看我的这篇文章吧。https://www.xuanyusong.com/archives/2644
- 本文固定链接: https://www.xuanyusong.com/archives/1032
- 转载请注明: 雨松MOMO于 雨松MOMO程序研究院 发表



Vector2 bloodSize = GUI.skin.label.CalcSize (new GUIContent(blood_red));請問這行是什麼GUI 的?? 如果要改為Js要如何改@@?
請高手解答 謝謝@@..
看看 unity API吧。。 里面写的很清楚的。
var bloodSize : Vector2 = GUI.skin.label.CalcSize(GUIContent(blood_red)); 以解決 謝謝^^
如果有建筑将人员挡住时,玩家的名称依然会显示,请问 @雨松MOMO 这个如何克服掉?CS里的名称显示是什么原理实现的?
用射线判断。。。
我是在摄像机与人物之间画一条直线来判断的,这样会不会占用太多系统资源啊?
– -我是新手 昨天刚开始接触Unity 然后模仿您的代码敲了一下 发现当角色移动时 NPC会晃的很厉害 然后将Update方法做了一下小改动。NPC比之前改善很多 float x=hero.transform.position.x;float y=hero.collider.bounds.size.y*hero.transform.localScale.y;float z=hero.transform.position.z;Vector3 vec_look=new Vector3(x,y,z);thisTransform.LookAt(vec_look);不知道是我的原因还是您的代码有点小漏洞。。总之。万分感谢您的教程。真心的受益匪浅。。
– -我是新手 昨天刚开始接触Unity 然后模仿您的代码敲了一下 发现当角色移动时 NPC会晃的很厉害 然后将Update方法做了一下小改动。NPC比之前改善很多 float x=hero.transform.position.x; float y=hero.collider.bounds.size.y*hero.transform.localScale.y; float z=hero.transform.position.z; Vector3 vec_look=new Vector3(x,y,z); thisTransform.LookAt(vec_look);不知道是我的原因还是您的代码有点小漏洞。。总之。万分感谢您的教程。真心的受益匪浅。。
过奖了。。。
还有其他的方法么? 这个确实如果调整视角背对着人物,血条也会显示在上面。
办法有很多种, 这里提到的只是其中的一种。 可以通过条件判断的
下载了代码….. 顺便点了两次广告….
感谢支持 嘿嘿
3D坐标转换成屏幕中的2D坐标后,屏幕右上角的坐标不是(1.1) 吧?就是屏幕的像素啊
Debug.log(Input.mousePosition); 就一清二楚了。。
如何制作边界物理层呢,新手求指教
放一个透明的cube加上碰撞就可以的。。 蛤蛤
搞定了 谢了 哈
再请教一下,那该如何让正方体刚好罩住整张地图呢?
再请教一下,那该如何让正方体刚好罩住整张地图呢?
做一个cube然后把摄像机放在里面即可。。
正规3D游戏项目不会用这种方法的,都是max里边直接做个绑定点在角色头顶,血条、图标直接用绑定点的坐标定位,再lookat摄像机平面。
嗯的。。
我想问一下,如果视角带缩放,就是拉近拉远视角的话,头顶上的血条会不会偏移。。我自己做的只要一缩放视角,就会偏移。
不会的
//得到真实NPC头顶的2D坐标 position = new Vector2 (position.x, Screen.height – position.y);这句,若是npc的缩放比例很大的话,position.y就会大于Screen.height;Screen.height-position.y的结果是负值。
我就是想知道 GUI.Label(new Rect(position.x – (nameSize.x/2),position.y – nameSize.y – bloodSize.y ,nameSize.x,nameSize.y), name); 里面的nameSize是哪里来的?
这个程序 有点 问题,在//根据NPC头顶的3D坐标换算成它在2D屏幕中的坐标 Vector2 position = camera.WorldToScreenPoint (worldPosition);这个方法 返回的是一个三维坐标,屏幕坐标也是三维, 而且这个程序有个bug 如果你脚本付给敌人,使显示敌人的血量,但是 当你背对敌人,敌人血量也会显示;我的解决方法是 将敌人的坐标转化为屏幕3D 坐标,并判断z轴 是否大于0,如果小于零代表敌人不在你的 视野内 不显示敌人血量
离镜头远的时候, 血条和文字是不是应该变小?而近的时候应该变大。
GUI.skin.label.CalcSize ()这个函数怎么找不到啊。。在unity3d圣典上啊
不会的, 肯定是有的,好好找找哈。
今天I找了半天。。唉还是没找到。。麻烦你给说下把。。或者给发个地址也可以啊 啊
我想实现 模型有个类似气泡一样的文字标注,,就像外国电影一样 蓝色的框框里面有几行字
当然可以实现啊。。。
能不能讲一下思路哇
MOMO是女程序员?
学习一下
我不做游戏,但真的很酷!加油,MOMO…![[good] [good]](http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/d8/good_org.gif)
学习,支持雨松