一般来讲,在回合制和RPG游戏中,不可避免的需要在人物头上显示被攻击的伤害值,或则是被治疗时候的治疗值。一般就是一些跟随人物Label就可以了,加上一些出现的小动画就可以了。
一、效果元素组
//动画的一个状态元素
using UnityEngine;
public class PopElement {
public float posx = 0.0f;//起始位置
public float posy = 0.0f;
public float detailTime = 0.0f;//这个状态的总共时间
public float transferX = 0.0f;//移动的偏移位置
public float transferY = 0.0f;
public float scale = 1.0f;//缩放
public float startAlpha = 1.0f;//开始的透明度
public float alpha = 1.0f;//结束的透明度
float totalTime = 0f;
public PopElement(Vector2 pos, float time, Vector2 transfer, float scale, float startalpha, float alpha)
{
this.posx = pos.x;
this.posy = pos.y;
this.detailTime = time;
this.transferX = transfer.x;
this.transferY = transfer.y;
this.scale = scale;
this.startAlpha = startalpha;
this.alpha = alpha;
Init();
}
public void Init()
{
totalTime = detailTime;
}
public void Update(float time)
{
totalTime -= time;
}
public bool isEnd()
{
return totalTime <= 0;
}
}//一个效果将由很多个动画状态组成
using System.Collections.Generic;
public class PopEffect {
public List<PopElement> elementList = new List<PopElement>();
}//具体的一个效果
using UnityEngine;
public class PopEffectHurt : PopEffect {
public PopEffectHurt()
{
elementList.Add(new PopElement(new Vector2(0f, 0f), 0.15f, new Vector2(0f, 1.99f), 1.05f, 0.3f, 1f));//0.15秒 从透明到显示 放大一点 向上移动1.99
elementList.Add(new PopElement(new Vector2(0f, 0f), 0.15f, new Vector2(0f, 1.99f), 0.95f, 1f, 1f));//0.15秒 往上走 缩小一点
elementList.Add(new PopElement(new Vector2(0f, 0f), 0.8f, new Vector2(0f, 0f), 1.0f, 1f, 1f));//停留不动0.8秒
elementList.Add(new PopElement(new Vector2(0f, 0f), 0.1f, new Vector2(0, -0.5f), 0.95f, 1f, 0.3f));//0.1秒 往下走并隐藏
}
}二、那个Damage物体,一般是一个Label或Text
//一个PDamage代表一个伤害Label的实例
using UnityEngine;
using UnityEngine.UI;
public class PDamage {
public GameObject go = null;//实例化出来的物体
public Text lable = null;//显示数值的Label,在go下的
public PopEffect effect = null;//使用的动画效果
public void Destory()
{
GameObject.Destroy(go);
go = null;
}
public void Clear()
{
effect = null;
go.SetActive(false);
}
}//将会绑定到上面的go上的,这个是基类,一些移动缩放偏移等的基本实现
using UnityEngine;
using UnityEngine.UI;
public class UIHeadElement : MonoBehaviour
{
protected GameObject attachedObject;//联系上的物体,3D人物
protected Vector3 worldPos = Vector3.zero;
protected Vector3 attachedOffset = Vector3.zero;//偏移
private bool isTransfer = false;//是否计算移动
protected float transX = 0.0f;//总的偏移量
protected float transY = 0.0f;
private float shiftPosX = 0.0f;//当前的偏移移动量
private float shiftPosY = 0.0f;
private float startPosX = 0.0f;//起始位置
private float startPosY = 0.0f;
protected float transScale = 1.0f;//缩放因子
private float ClipFar = 100; //远剪裁面的距离,用来计算大小,实现越远数值大小也越小
Vector3 curPos = Vector3.zero;
Transform trans;
public Text chatLable;
protected void Awake()
{
trans = gameObject.transform;
GameObject uiRt = GameObject.Find("Canvas");
trans.parent = uiRt.transform;
InitHead();
}
void InitHead()
{
worldPos = Vector3.zero;
shiftPosX = 0f;
shiftPosY = 0f;
trans.localPosition = Vector3.zero;
trans.localRotation = Quaternion.identity;
if (attachedObject != null)
{
trans.localScale = Vector3.one * (1 - CameraManager.Instance.CalObjDepthFromCamera( attachedObject.transform.position.z ) / ClipFar);
}
trans.position = new Vector3(10000.0f,10000.0f,0.0f); //先移到看不到的地方
}
protected void Update ()
{
if(attachedObject == null)
return;
CalcBoxPos();
ShowBoxPos();
}
protected void Tick (float time)
{
if(attachedObject == null)
return;
CalcBoxPos(time);
ShowBoxPos();
}
void CalcBoxPos(float time)
{
worldPos = attachedObject.transform.position + attachedOffset;
if (isTransfer)
{
//计算动画的移动 和 缩放
shiftPosX = shiftPosX + transX * time;
shiftPosY = shiftPosY + transY * time;
worldPos.x += shiftPosX;
worldPos.y += shiftPosY;
worldPos.x += startPosX;
worldPos.y += startPosY;
trans.localScale *= transScale;//直接乘于缩放因子
}
worldPos = CameraManager.Instance.PosFromMainCamToUICam(worldPos);
worldPos.z = trans.position.z;
}
void CalcBoxPos()
{
worldPos = attachedObject.transform.position + attachedOffset;
if(isTransfer)
{
shiftPosX = shiftPosX + transX * Time.deltaTime;
shiftPosY = shiftPosY + transY * Time.deltaTime;
worldPos.x += shiftPosX;
worldPos.y += shiftPosY;
worldPos.x += startPosX;
worldPos.y += startPosY;
trans.localScale *= transScale;
}
worldPos = CameraManager.Instance.PosFromMainCamToUICam(worldPos);
worldPos.z = trans.position.z;
}
void ShowBoxPos()
{
if(curPos == worldPos)
return;
trans.position = worldPos;
curPos = worldPos;
}
public void AttachObject(GameObject obj, Vector3 offset)
{
attachedObject = obj;
attachedOffset = offset;
InitHead();
}
public void SetTransfer(bool isTrans)
{
isTransfer = isTrans;
}
public void setTransPos(float x,float y)
{
transX = x;
transY = y;
}
public void setStartPos(float x,float y)
{
startPosX = x;
startPosY = y;
}
public void setScale(float scale)
{
transScale = scale;
}
public void SetClipFar(float far)
{
ClipFar = far;
}
}//继承UIHeadElement,使用PopEffect来设置动画状态,并控制每个Element的使用
using UnityEngine;
public delegate void DestoryCB(PDamage pDamaga);
public class UIDamagaElement :UIHeadElement {
enum PopState
{
PopState_None,
PopState_Ready,
PopState_Loop,
PopState_Finish,
}
DestoryCB destoryCB = null;
PopState state = PopState.PopState_None;
int curIndex= 0;
float alpha = 0f;
PDamage pDamaga;
void Awake()
{
base.Awake();
}
public void Init(PDamage damaga,DestoryCB destoryCB)
{
pDamaga = damaga;
if(!this.gameObject.activeSelf)
{
this.gameObject.SetActive(true);
}
this.destoryCB = destoryCB;
}
void Update()
{
Tick(Time.deltaTime);
}
public void Tick(float time)
{
switch(state)
{
case PopState.PopState_None:
curIndex = 0;
state = PopState.PopState_Ready;
this.SetTransfer(true);
break;
case PopState.PopState_Ready:
{
//初始化时间计数,设置好所有属性
pDamaga.effect.elementList[curIndex].Init();
if(!pDamaga.go.activeSelf)
{
pDamaga.go.SetActive(true);
}
//获取初始alpha值
alpha = pDamaga.effect.elementList[curIndex].startAlpha;
//设置位移量和缩放比例
this.setStartPos(pDamaga.effect.elementList[curIndex].posx,pDamaga.effect.elementList[curIndex].posy);
this.setTransPos(pDamaga.effect.elementList[curIndex].transferX,pDamaga.effect.elementList[curIndex].transferY);
this.setScale(pDamaga.effect.elementList[curIndex].scale);
//设置字当前的alpha
pDamaga.lable.color = new Color(pDamaga.lable.color.r, pDamaga.lable.color.g, pDamaga.lable.color.b, alpha);
state = PopState.PopState_Loop;
break;
}
case PopState.PopState_Loop:
{
//记录重置的
pDamaga.effect.elementList[curIndex].Update(time);
base.Tick(time);
//设置字的alpha值
alpha += (pDamaga.effect.elementList[curIndex].alpha / (pDamaga.effect.elementList[curIndex].detailTime / time));
pDamaga.lable.color = new Color(pDamaga.lable.color.r, pDamaga.lable.color.g, pDamaga.lable.color.b, alpha);
//判断是否结束
if (pDamaga.effect.elementList[curIndex].isEnd())
{
curIndex ++;
if(curIndex < pDamaga.effect.elementList.Count)
state = PopState.PopState_Ready; //循环播放
else
state = PopState.PopState_Finish; //停止播放
}
break;
}
case PopState.PopState_Finish:
{
this.AttachObject(null, Vector3.zero);
this.SetTransfer(false);
Clear();
state = PopState.PopState_None;
break;
}
default:
break;
}
}
void Clear()
{
if(destoryCB != null)
{
destoryCB(pDamaga);
}
curIndex= 0;
alpha = 0f;
}
}//提供两个相机之间 物体位置的转换,和距离的计算等
using UnityEngine;
public class CameraManager : MonoBehaviour {
private Camera MainCamera;
private Camera UICamera;
public static CameraManager Instance;
void Awake () {
MainCamera = GameObject.FindWithTag("MainCamera").GetComponent<Camera>();
UICamera = GameObject.FindWithTag("UICamera").GetComponent<Camera>();
Instance = this;
}
public Vector3 PosFromMainCamToUICam(Vector3 position)
{
if (MainCamera == null || UICamera == null) return Vector3.zero;
Vector3 pos = MainCamera.WorldToViewportPoint(position);
return UICamera.ViewportToWorldPoint(pos);
}
public float CalObjDepthFromCamera(float z)
{
if (MainCamera == null) return 0;
return z - MainCamera.transform.position.z;
}
}//伤害弹弹弹的管理器,负责初始化PDamage实例,缓存并使用PDamage
//为某一个3D人物,添加伤害显示
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PopDamageManager : MonoBehaviour {
public static PopDamageManager Instance;
private Queue<PDamage> damagePool = new Queue<PDamage>();
private List<PDamage> activeDamage = new List<PDamage>();
public GameObject DamagePrefab;
public Vector3 DefaultOffset = new Vector3(0, 0.6f, 0);
void Awake () {
Instance = this;
}
//初始化数量到对象池中,一般知道人物的多少,这些数量是可以预估出来的
public void Init(int damageCount)
{
if (DamagePrefab == null) return;
for (int i = 0; i < damageCount; i++)
{
PDamage pDamage = new PDamage();
pDamage.go = Instantiate(DamagePrefab) as GameObject;
pDamage.lable = pDamage.go.GetComponentInChildren<Text>();
pDamage.go.AddComponent<UIDamagaElement>();
damagePool.Enqueue(pDamage);
pDamage.Clear();
}
}
//为go人物,添加一个伤害显示,指定好动画效果
public void AttachDamageToGameObject(GameObject go, PopEffect effect, string text)
{
if (damagePool.Count == 0) return;
PDamage pDamage = damagePool.Dequeue();
pDamage.effect = effect;
pDamage.lable.text = text;
UIDamagaElement element = pDamage.go.GetComponent<UIDamagaElement>();
if (element != null)
{
element.AttachObject(go, DefaultOffset);
element.Init(pDamage, RecoverDamage);
}
activeDamage.Add(pDamage);
}
//动画播放完成后就 隐藏回收到对象池
void RecoverDamage(PDamage node)
{
activeDamage.Remove(node);
node.Clear();
damagePool.Enqueue(node);
}
public void Destory()
{
for (int i = 0; i < activeDamage.Count; i++)
{
damagePool.Enqueue(activeDamage[i]);
}
activeDamage.Clear();
PDamage node = null;
while (damagePool.Count != 0 && (node = damagePool.Dequeue()) != null)
{
node.Destory();
}
}
}三、测试
using UnityEngine;
public class TestPop : MonoBehaviour {
public GameObject Target;
void Start()
{
PopDamageManager.Instance.Init(10);
}
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
int val = Random.Range(100, 1000);
PopDamageManager.Instance.AttachDamageToGameObject(Target, new PopEffectHurt(), "-" + val);
}
if (Input.GetMouseButtonDown(1))
{
PopDamageManager.Instance.Destory();
}
}
}
四、扩展
可以加上字体或则是字体颜色等,继承PopEffect创建更多的动画效果,达到暴击加血等等的不同效果。