-------小基原创,转载请给我一个面子
上回书说道,英雄和魔王都做完了,子弹也能发射了,就是不知道啥时候能干死魔王。那小基得做个血条来展示一下,他离死不远了(•౪• ) 其实血条也可以看作是进度条的一种用法,大家经常看到的loading图各式各样,无非都是在表示进度的百分比。所以下面小基来演示怎么做一个比较low的通用进度条。
上面是几个例子,最后一个什么鬼小基也不知道,有会做的老司机请手把手教我,谢谢( ᐛ )
第一步先把组件拼装好
创建空物体起名叫做BossHpBar,本例非常直观的叫法,当然起通用的名字更好一下比如ProgressBar(whatever)
里面放三个图片,分别叫做bg(底),up(上),damage(损血可回复)
注意红色箭头,锚点要修改好。意思是up停靠在bg最左边距离0个像素位置,damage在up的最右边距离0个像素位置。
血量减少,或者进度增加的过程,就是改变up图片的scale大小,左边位置固定了,如果scale的x变大,就相当于变长(进度增加),x变小就对应变短(血量减少)。
damage这个有的游戏“损血可恢复”这一说,比如“一定时间内可缓慢回复”,“交换队员后台回血”(铁拳TT2),“攻击对方可回复损血部分”(血源诅咒)
当然如果你不需要这个功能,不加这个东西就是了~
最后你应该能够做出下面这个效果。白色是血条整体,红色为血量,黄色为损血部分(就这么叫吧)
此时你可以自己改变up和damage的scale的x值,看看变化效果。
第二步准备上代码,让它“自己动”
给父节点BossHpBar上添加一个脚本,名字叫做BossHpBar吧
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 using UnityEngine.UI; 5 6 [ExecuteInEditMode] 7 public class ProgressBar : MonoBehaviour { 8 9 [SerializeField] 10 private Image Bg; 11 [SerializeField] 12 private Image Up; 13 [SerializeField] 14 private Image ChangeImg; 15 [SerializeField] 16 private float _maxValue; 17 [SerializeField] 18 private float _value; 19 [SerializeField] 20 private float _changeValue; 21 22 private float size = 1; 23 private float changeSize = 0; 24 25 //最大值 26 public float MaxValue 27 { 28 get 29 { 30 return _maxValue; 31 } 32 set 33 { 34 _maxValue = value; 35 } 36 } 37 38 //当前值 39 public float Value 40 { 41 get 42 { 43 return _value; 44 } 45 set 46 { 47 _value = value; 48 UpdateBar(); 49 } 50 } 51 52 //变化值 53 public float ChangeValue 54 { 55 get 56 { 57 return _changeValue; 58 } 59 set 60 { 61 _changeValue = value; 62 UpdateBar(); 63 } 64 } 65 66 public void SetValue(float value, float maxValue) 67 { 68 SetValue(value, 0, maxValue); 69 } 70 71 public void SetValue(float value, float changeValue, float maxValue) 72 { 73 _value = value; 74 _changeValue = changeValue; 75 _maxValue = maxValue; 76 UpdateBar(); 77 } 78 79 void UpdateBar() 80 { 81 if (Bg == null || Up == null) 82 return; 83 84 size = Mathf.Clamp01(_value / _maxValue); 85 //Debug.Log("size:" + size); 86 Up.rectTransform.localScale = new Vector3(size, 1, 1); 87
if(size != 0)
{
88 changeSize = -Mathf.Clamp01(_changeValue / _maxValue)/size;
} 89 ChangeImg.rectTransform.localScale = new Vector3(changeSize, 1, 1); 90 } 91 92 93 //面板调试 94 [SerializeField] 95 bool refresh = false; 96 #if UNITY_EDITOR 97 void Update() 98 { 99 if(refresh) 100 { 101 refresh = false; 102 UpdateBar(); 103 } 104 } 105 #endif 106 }
上面总揽全局,下面小基具体分析
[SerializeField]这个作用是让下面的对象可以在Inspector面板里显示,方便拖动物体与其绑定。不用这个的话就把private改成public是一个道理,不过public尽量少用(优化,规范,性能这些不打算在这个系列里面提及太多,增加对新人的学习成本。这个系列目的就是让零基础的童鞋也能做出效果,体验成就感爆棚的感觉!)
MaxValue,Value,ChangeValue这三个属性,里面可以get获取到对应的值,也可以set给对应的值赋值(小基让你满血就满血,让你残血就残血,氪金的话,小基让你锁血无敌吼不吼啊~)
这里在set设置血量或者损血量的时候,会调用刷新血条UpdateBar()这个方法,这样UI那边才能反映出变化对吧。注意,MaxValue一般来说,固定了就不需要再变化了,所以小基就没有加UpdateBar() 如果你是杠精可能会说“使用道具提升血量上限”这个事,那就在MaxValue里面也加个UpdateBar()就好了。万能的ctrl+c,ctrl+v
接下来提供设置血量的方法吧(A脚本直接调用B脚本的 .XXX这种赋值方式,一旦多人开发,会让你崩溃的。 最好不要这么写 bScript.MaxValue = 100,上面几个属性不要set会更好吧。大家自行选择吧,群众的眼睛是雪亮的)
这两个方法名字一样,区别是参数不同(数量多一个),装X术语讲应该叫做overload(重载),不是那个动漫啦。还有个叫overwrite(重写)这俩完全两回事,别弄混,面试会考的。
当你调用SetValue()方法的时候,会根据你传的参数,自行找到合适代码(传2个参数就用上面的,传3个参数就调用下面的)
意思就是:如果你调用方法时候,传2个参数,我就默认你changeValue为0,毕竟可能你整个游戏都没有这个需要,每次非传3个参数还必须中间填个0,多无聊。如果你调用方法时候传递3个参数,那么这3个参数的值你自己掌控就好。代码里面就是给三个变量赋值,然后更新血条
下面是本代码重头戏,血条更新
如果Bg,Up这俩都没图片的话,等着报错吧,所以为了安全,所以加个判断是否为null吧,啥都没准备好,那就return返回就是了。
接下来算scale到底要缩小到多少。当前/总血量 是计算百分比,然后用Mathf.Clamp01这个限制这个值最终为0~1之间(你血量超出上限的话,那你也是棒棒哒)
接下来对Up的localScale赋值做缩放,改变长就ok了,y,z两个保持1就行了(其实这里不好,万一控件y,z不是1,你这么写就要粗事。最好是提前保存下来y,z 的值,然后这里附近去。还是不想增加阅读成本,怎么简单怎么来吧,这里的坑小基就指一下)
再下面是对damage损血那个图片的长度进行计算,不过因为damage是up的子物体,再localScale是在up缩放后的基础上再次缩放,所以后面多 /size 一下,是去掉up的缩放影响。这样如果up跟damage值是一样的话,看起来长度也应该是一样的。
最后说一下方便调节的东东
类前面增加这个标签,可以在unity没有运行的条件下,操作面板来调用代码(这样就不用老去运行的时候看效果了)
那么需要非运行条件下看效果的部分就是这个,如果我点击refresh这个勾选,就立刻看一下血条的刷新情况。这样调节MaxValue,Vaule,ChangeValue的效果就非常方便了
挂上脚本后的样子,绑定好对应的物体
这样就可以通过设置值,来控制血条的变化了。
别急,事还没完呢!
我们要打怪啊,他要掉血啊!
上一篇文章中大家还记得Enemy那个脚本吧,就是被打后,移动那个ByHit方法那个事,现在我们来把它改成掉血
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 5 public class Enemy : MonoBehaviour { 6 7 private GameObject bossHpBar; 8 private ProgressBar bossHpBarCpnt; 9 10 public float MaxHp = 100; 11 12 // Use this for initialization 13 void Start () { 14 bossHpBar = Instantiate((GameObject)Resources.Load("Prefab/BossHpBar")); 15 bossHpBar.transform.SetParent(GameObject.Find("Canvas").transform, false); 16 bossHpBarCpnt = bossHpBar.GetComponent<ProgressBar>(); 17 bossHpBarCpnt.MaxValue = MaxHp; 18 bossHpBarCpnt.Value = MaxHp; 19 bossHpBarCpnt.ChangeValue = 0; 20 //Debug.Log("bossHpBar:" + bossHpBar); 21 } 22 23 private float curHp = 0; 24 void ByHit(int damageValue) 25 { 26 curHp = bossHpBarCpnt.Value - damageValue; 27 bossHpBarCpnt.SetValue(curHp, MaxHp); 28 29 if(curHp <= 0) 30 { 31 Destroy(this.gameObject); 32 Destroy(bossHpBar); 33 Debug.Log("You Win"); 34 } 35 } 36 }
这次我们像子弹一样,动态创建血条,不然三个boss圈踢你一个,你还想在场景里放三个血条,然后拖动绑定么?那玩割草无双那么多血条,怕不是累成dog
这里就是老规矩,把BossHpBar制作成Prefab方法特定目录下,然后start()里面创建出来并附上初始值,这些方法之前博客里面都有讲过,相信你肯定都记得。
GameObject.Find("Canvas")就是动态查找到Canvas,不像之前似得还得拖动绑定了,有点low
GetComponent<ProgressBar>就是获取预制体BossHpBar上面的ProgressBar脚本,然后错误示范 bossHpBarCpnt.MaxValue = MaxHp。最好用bossHpBarCpnt.SetValue(MaxHp,0,MaxHp)这么调用赋值
这回我们挨揍后不移动了,改为取当前血量,然后减去要扣的血量,给血条赋值。(damageValue是子弹那边传过来的)
if判断里面是,如果敌人挂了,就删除掉自身,和自己的血条。顺便恭喜你一下