原理其实很简单,先通过UI创建一张image,和一个mask,通过改变mask的width值来显示血量的剩余量,但是我想要稍稍改进一下,让血量decrease/increase不那么突兀。
这里使用到了Coroutine(协程):
关于协程
1,什么是协程
协程,从字面意义上理解就是协助程序的意思,我们在主任务进行的同时,需要一些分支任务配合工作来达到最终的效果
稍微形象的解释一下,想象一下,在进行主任务的过程中我们需要一个对资源消耗极大的操作时候,如果在一帧中实现这样的操作,游戏就会变得十分卡顿,这个时候,我们就可以通过协程,在一定帧内完成该工作的处理,同时不影响主任务的进行
2,协程的原理
首先需要了解协程不是线程,协程依旧是在主线程中进行
然后要知道协程是通过迭代器来实现功能的,通过关键字IEnumerator来定义一个迭代方法。
IEnumerator都需要一个yield return 返回
3、协程的使用
首先通过一个迭代器定义一个返回值为IEnumerator的方法,然后再程序中通过StartCoroutine来开启一个协程即可:
在正式开始代码之前,需要了解StartCoroutine的两种重载方式:
StartCoroutine(string methodName):这种是没有参数的情况,直接通过方法名(字符串形式)来开启协程
StartCoroutine(IEnumerator routine):通过方法形式调用
StartCoroutine(string methodName,object values):带参数的通过方法名进行调用
在一个协程开始后,同样会对应一个结束协程的方法StopCoroutine与StopAllCoroutines两种方式,但是需要注意的是,两者的使用需要遵循一定的规则,在介绍规则之前,同样介绍一下关于StopCoroutine重载:
StopCoroutine(string methodName):通过方法名(字符串)来进行
StopCoroutine(IEnumerator routine):通过方法形式来调用
StopCoroutine(Coroutine routine):通过指定的协程来关闭
刚刚我们说到他们的使用是有一定的规则的,那么规则是什么呢,答案是前两种结束协程方法的使用上,如果我们是使用StartCoroutine(string methodName)来开启一个协程的,那么结束协程就只能使用StopCoroutine(string methodName)和StopCoroutine(Coroutine routine)来结束协程。
接下来写一个EnemyBar脚本,挂在Canvas_EnemyBar上,调好参数。
public class EnemyBar : MonoBehaviour
{
[field: SerializeField]
public int MaxValue { get; private set; }
[field: SerializeField]
public int Value { get; private set; }
[SerializeField]
private RectTransform white;
[SerializeField]
private RectTransform red;
[SerializeField]
private float _animationSpeed = 10f;
private float _fullWidth;
private float TargetWidth => Value * _fullWidth / MaxValue;
private Coroutine _adjustBarWidthCoroutine;
private void Start()
{
_fullWidth = red.rect.width;
}
private void Update()
{
if (Input.GetKeyDown("k"))
{
Change(20);
}
if (Input.GetKeyDown("l"))
{
Change(-20);
}
}
public void Change(int amount)
{
Value = Mathf.Clamp(Value + amount, 0, MaxValue);
if (_adjustBarWidthCoroutine != null)
{
StopCoroutine(_adjustBarWidthCoroutine); //关闭上一次的协程
}
_adjustBarWidthCoroutine = StartCoroutine(AdjustBarWidth(amount));
}
private IEnumerator AdjustBarWidth(int amount)
{
var suddenChangeBar = amount >= 0 ? white : red;
var slowChangeBar = amount >= 0 ? red : white;
suddenChangeBar.setWidth(TargetWidth);
while(Mathf.Abs(suddenChangeBar.rect.width-slowChangeBar.rect.width)>0.1f)
{
slowChangeBar.setWidth(Mathf.Lerp(slowChangeBar.rect.width, TargetWidth,
Time.deltaTime * _animationSpeed));
yield return null; //下一帧再执行下一次循环,直到循环结束
}
slowChangeBar.setWidth(TargetWidth);
//yield return 0;
}
}
public static class RectTransformExtension
{
public static void setWidth(this RectTransform t,float width)
{
t.sizeDelta = new Vector2(width, t.rect.height);
}
}
这里仅为测试代码,后续我会让它跟随机器人移动,并且相应变化。