Unity 戰(zhàn)斗飄字飄血 ShootTextPro

前言,寫了一個小工具弥虐,主要就是戰(zhàn)斗中對應的飄字蹋岩,源碼相應的注釋已經(jīng)標注晕换,非常方便自定義改動,其中ShootTextProController可以做成單例磅摹,傳入對應的tranform即可滋迈,為了展示示例,所以并沒有改動户誓。有疑問歡迎在下方給我留言

  • 2018/12/24更新:修復創(chuàng)建周期參數(shù)修改失效

相關系數(shù)可調(diào)節(jié)

ShootTextPro.unitypackage下載

示例展示

源碼如下

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ShootTextInfo
{
    public string content;
    public TextAnimationType animationType;
    public TextMoveType moveType;
    public float delayMoveTime;
    public int size;
    public Transform cacheTranform;
    public float initializedVerticalPositionOffset;
    public float initializedHorizontalPositionOffset;
    public float xIncrement;
    public float yIncrement;
}
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public class ShootTextComponent : MonoBehaviour
{

    public Animator animator = null;
    public CanvasGroup canvasGroup = null;
    public RectTransform rectTransform = null;

    public List<RectTransform> childTransformGroup = new List<RectTransform>();

    public List<Vector2> sizeDeltaGroup = new List<Vector2>();

    #region From ShootTextInfo
    public string content;
    public TextAnimationType animationType;
    public TextMoveType moveType;
    public double delayMoveTime;
    public int size;
    public Transform cacheTranform;
    public double initializedVerticalPositionOffset;
    public double initializedHorizontalPositionOffset;
    #endregion

    public double fadeCurveTime;

    public double xMoveOffeset;
    public double yMoveOffeset;

    public bool isMove = false;
    public void SetInfo(ShootTextInfo shootTextInfo)//執(zhí)行順序優(yōu)于Start
    {
        content = shootTextInfo.content;
        animationType = shootTextInfo.animationType;
        moveType = shootTextInfo.moveType;
        delayMoveTime = Time.time + shootTextInfo.delayMoveTime;
        cacheTranform = shootTextInfo.cacheTranform;
        initializedHorizontalPositionOffset = shootTextInfo.initializedHorizontalPositionOffset;
        initializedVerticalPositionOffset = shootTextInfo.initializedVerticalPositionOffset;
    }
    void Start()
    {
        animator = GetComponent<Animator>();
        canvasGroup = GetComponent<CanvasGroup>();
        rectTransform = GetComponent<RectTransform>();
        childTransformGroup = transform.GetComponentsInChildren<RectTransform>().ToList();
        childTransformGroup.Remove(childTransformGroup[0]);

        for (int i = 0; i < childTransformGroup.Count; i++)
        {
            sizeDeltaGroup.Add(childTransformGroup[i].sizeDelta);
        }
        int state = animationType == TextAnimationType.Normal ? 1 : 2;
        animator.SetInteger("state", state);
    }

    public void ChangeScale(double scale)
    {
        for (int i = 0; i < childTransformGroup.Count; i++)
        {
            Vector2 sizeDelta = sizeDeltaGroup[i];
            sizeDelta.x = sizeDelta.x * (float)scale;
            sizeDelta.y = sizeDelta.y * (float)scale;
            childTransformGroup[i].sizeDelta = sizeDelta;
        }
    }

    public void ChangeAlpha(float alpha)
    {
        canvasGroup.alpha = alpha;
    }
}
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Custom.Log;
using UnityEngine;

[Serializable]
public enum TextAnimationType
{
    None = 0,
    Normal = 1,
    Burst = 2
}
public enum TextMoveType
{
    None,
    Up,
    Down,
    Left,
    Right,
    LeftUp,
    LeftDown,
    RightUp,
    RightDown,
    LeftParabola,
    RightParabola
}
public class ShootTextProController : MonoBehaviour
{
    private readonly string operatorPlusKeyPostfix = "_operator_plus";
    private readonly string operatorMinusKeyPostfix = "_operator_minus";
    private readonly string numberPrefix = "_NumberImage_";
    public Dictionary<string, GameObject> numberDic = new Dictionary<string, GameObject>();

    private List<ShootTextComponent> handleShootTextGroup = new List<ShootTextComponent>();
    private Queue<ShootTextInfo> waitShootTextGroup = new Queue<ShootTextInfo>();
    private List<ShootTextComponent> waitDestoryGroup = new List<ShootTextComponent>();

    private Transform shootTextCanvas = null;
    public Transform ShootTextCanvas
    {
        get
        {
            if (shootTextCanvas == null)
            {
                shootTextCanvas = GameObject.Find("Canvas").transform;
            }
            if (shootTextCanvas == null)
            {
                this.LogError("shootTextCanvas丟失");
            }
            return shootTextCanvas;
        }
        set { shootTextCanvas = value; }
    }
    private Camera shootTextCamera = null;
    public Camera ShootTextCamera
    {
        get
        {
            return shootTextCamera == null ? Camera.main : shootTextCamera;
        }
        set
        {
            shootTextCamera = value;
        }
    }

    [Header("漸隱曲線")]
    [SerializeField]
    private AnimationCurve shootTextCure = null;
    [Header("拋物線曲線")]
    [SerializeField]
    private AnimationCurve shootParabolaCure = null;

    [Header("最大等待彈射數(shù)量")]
    [SerializeField]
    private int MaxWaitCount = 20;
    [Header("超過此數(shù)量彈射加速")]
    [SerializeField]
    private int accelerateThresholdValue = 10;
    [Header("加速彈射速率因子")]
    [SerializeField]
    private float accelerateFactor = 2;
    private bool isAccelerate = false;
    [Header("默認創(chuàng)建周期:秒/一次")]
    [SerializeField]
    private float updateCreatDefualtTime = 0.2f;
    //[SerializeField]
    private float updateCreatTime = 0.2f;
    //[SerializeField]
    private float updateCreatTempTime;
    [Header("從移動到消失的生命周期 單位:秒")]
    [SerializeField]
    private float moveLifeTime = 1.0f;

    [Header("遠近縮放因子")]
    [SerializeField]
    private float shootTextScaleFactor = 0.6f;

    [Header("等待指定時間后開始移動")]
    [SerializeField]
    private float delayMoveTime = 0.3f;

    public float DelayMoveTime { get { return delayMoveTime; } set { delayMoveTime = value; } }
    [Range(-4, 4)]
    [Header("初始化位置垂直偏移量")]
    [SerializeField]
    private float initializedVerticalPositionOffset = 0.8f;
    [Range(-4, 4)]
    [Header("初始化位置水平偏移量")]
    [SerializeField]
    private float initializedHorizontalPositionOffset = 0.0f;
    [Header("垂直移動速率")]
    [Range(0, 20)]
    [SerializeField]
    private float verticalMoveSpeed = 10;
    [Header("水平移動速率")]
    [Range(0, 20)]
    [SerializeField]
    private float horizontalMoveSpeed = 10;
    [Header("字體動畫類型")]
    public TextAnimationType textAnimationType;
    [Header("字體移動類型")]
    public TextMoveType textMoveType;

    /// <summary>
    /// 一次飄字UI父節(jié)點
    /// </summary>
    private GameObject shootTextPrefab = null;
    void Start()
    {
        Initialized();
    }
    private void Initialized()
    {
        shootTextCure = new AnimationCurve(new Keyframe[] { new Keyframe(0, 1f), new Keyframe(moveLifeTime, 0f) });
        shootTextPrefab = Resources.Load<GameObject>("Prefabs/ShootText_Pure");
        updateCreatTempTime = updateCreatTime;
        //加法圖片
        numberDic.Add(TextAnimationType.Normal.ToString() + operatorPlusKeyPostfix, Resources.Load<GameObject>("Prefabs/" + TextAnimationType.Normal.ToString() + operatorPlusKeyPostfix));
        numberDic.Add(TextAnimationType.Burst.ToString() + operatorPlusKeyPostfix, Resources.Load<GameObject>("Prefabs/" + TextAnimationType.Burst.ToString() + operatorPlusKeyPostfix));
        //減法圖片
        numberDic.Add(TextAnimationType.Normal.ToString() + operatorMinusKeyPostfix, Resources.Load<GameObject>("Prefabs/" + TextAnimationType.Normal.ToString() + operatorMinusKeyPostfix));
        numberDic.Add(TextAnimationType.Burst.ToString() + operatorMinusKeyPostfix, Resources.Load<GameObject>("Prefabs/" + TextAnimationType.Burst.ToString() + operatorMinusKeyPostfix));

        for (int i = 0; i < 10; i++)
        {
            numberDic.Add(TextAnimationType.Normal.ToString() + numberPrefix + i, Resources.Load<GameObject>("Prefabs/" + TextAnimationType.Normal.ToString() + numberPrefix + i));
        }
        for (int i = 0; i < 10; i++)
        {
            numberDic.Add(TextAnimationType.Burst.ToString() + numberPrefix + i, Resources.Load<GameObject>("Prefabs/" + TextAnimationType.Burst.ToString() + numberPrefix + i));
        }
    }
    void Update()
    {

        float deltaTime = Time.deltaTime;

        //操作handleShootTextGroup中移動
        for (int i = 0; i < handleShootTextGroup.Count; i++)
        {
            ShootTextComponent shootTextComponent = handleShootTextGroup[i];

            Vector3 shootTextCreatPosition = Vector3.zero;
            shootTextCreatPosition = shootTextComponent.cacheTranform.GetComponent<Collider>().bounds.center + (((Vector3.up * shootTextComponent.cacheTranform.GetComponent<Collider>().bounds.size.y) * 0.5f));
            shootTextCreatPosition.x += (float)shootTextComponent.initializedHorizontalPositionOffset;
            shootTextCreatPosition.y += (float)shootTextComponent.initializedVerticalPositionOffset;

            Vector2 anchors = ShootTextCamera.WorldToViewportPoint(shootTextCreatPosition);//飄字初始錨點位置
            Vector2 changeAnchoredPosition = new Vector2((float)(anchors.x + shootTextComponent.xMoveOffeset), (float)(anchors.y + shootTextComponent.yMoveOffeset));//飄字這一幀所在位置

            //設定錨點
            shootTextComponent.rectTransform.anchorMax = anchors;
            shootTextComponent.rectTransform.anchorMin = anchors;
            //設置相對坐標
            shootTextComponent.rectTransform.anchoredPosition = changeAnchoredPosition;

            if (shootTextComponent.delayMoveTime <= Time.time)//允許移動操作
            {
                shootTextComponent.isMove = true;
            }

            //處理近大遠小
            double objectHigh = ModelInScreenHigh(shootTextComponent.cacheTranform);
            double scale = (objectHigh / 100) * shootTextScaleFactor;

            shootTextComponent.ChangeScale(scale);
            double xMoveOffeset = horizontalMoveSpeed * deltaTime * objectHigh;
            double yMoveOffeset = verticalMoveSpeed * deltaTime * objectHigh;

            if (shootTextComponent.isMove == true)//處理位置信息
            {
                switch (shootTextComponent.moveType)
                {
                    case TextMoveType.None:
                        break;
                    case TextMoveType.Up:
                        {
                            shootTextComponent.yMoveOffeset += yMoveOffeset;
                        }
                        break;
                    case TextMoveType.Down:
                        {
                            shootTextComponent.yMoveOffeset -= yMoveOffeset;
                        }
                        break;
                    case TextMoveType.Left:
                        {
                            shootTextComponent.xMoveOffeset -= xMoveOffeset;
                        }
                        break;
                    case TextMoveType.Right:
                        {
                            shootTextComponent.xMoveOffeset += xMoveOffeset;
                        }
                        break;
                    case TextMoveType.LeftUp:
                        {
                            shootTextComponent.xMoveOffeset -= xMoveOffeset;
                            shootTextComponent.yMoveOffeset += yMoveOffeset;
                        }
                        break;
                    case TextMoveType.LeftDown:
                        {
                            shootTextComponent.xMoveOffeset -= xMoveOffeset;
                            shootTextComponent.yMoveOffeset -= yMoveOffeset;
                        }
                        break;
                    case TextMoveType.RightUp:
                        {
                            shootTextComponent.xMoveOffeset += xMoveOffeset;
                            shootTextComponent.yMoveOffeset += yMoveOffeset;
                        }
                        break;
                    case TextMoveType.RightDown:
                        {
                            shootTextComponent.xMoveOffeset += xMoveOffeset;
                            shootTextComponent.yMoveOffeset -= yMoveOffeset;

                        }
                        break;
                    case TextMoveType.LeftParabola:
                        {
                            float parabola = shootParabolaCure.Evaluate((float)(shootTextComponent.fadeCurveTime / moveLifeTime));
                            shootTextComponent.xMoveOffeset -= xMoveOffeset;
                            shootTextComponent.yMoveOffeset += yMoveOffeset + parabola;
                        }
                        break;
                    case TextMoveType.RightParabola:
                        {
                            float parabola = shootParabolaCure.Evaluate((float)(shootTextComponent.fadeCurveTime / moveLifeTime));
                            shootTextComponent.xMoveOffeset += xMoveOffeset;
                            shootTextComponent.yMoveOffeset += yMoveOffeset + parabola;
                        }
                        break;
                    default:
                        break;
                }
            }

            //處理漸隱
            if (shootTextComponent.isMove == true)
            {
                shootTextComponent.fadeCurveTime += deltaTime;
                float alpha = shootTextCure.Evaluate((float)(shootTextComponent.fadeCurveTime));
                shootTextComponent.ChangeAlpha(alpha);
            }
            else
            {
                shootTextComponent.ChangeAlpha(1);
            }

            //處理刪除對應的飄字
            if (shootTextComponent.isMove == true && shootTextComponent.canvasGroup.alpha <= 0)
            {
                waitDestoryGroup.Add(shootTextComponent);
            }
        }

        //是否加速
        isAccelerate = waitShootTextGroup.Count >= accelerateThresholdValue ? true : false;
        if (isAccelerate)
        {
            updateCreatTime = updateCreatTime / accelerateFactor;
        }
        else
        {
            updateCreatTime = updateCreatDefualtTime;
        }

        //創(chuàng)建
        if ((updateCreatTempTime -= deltaTime) <= 0)
        {
            updateCreatTempTime = updateCreatTime;
            if (waitShootTextGroup.Count > 0)
            {
                GameObject tempObj = InstanceShootText(waitShootTextGroup.Dequeue());
                tempObj.transform.SetParent(ShootTextCanvas, false);
            }
        }

        //刪除已經(jīng)完全消失飄字
        for (int i = 0; i < waitDestoryGroup.Count; i++)
        {
            handleShootTextGroup.Remove(waitDestoryGroup[i]);
            Destroy(waitDestoryGroup[i].gameObject);
        }
        waitDestoryGroup.Clear();
    }
    public void CreatShootText(string content, Transform targetTranform)
    {
        CreatShootText(content, this.textAnimationType, this.textMoveType, this.delayMoveTime, this.initializedVerticalPositionOffset, this.initializedHorizontalPositionOffset, targetTranform);
    }
    public void CreatShootText(string content, TextAnimationType textAnimationType, TextMoveType textMoveType, float delayMoveTime, float initializedVerticalPositionOffset, float initializedHorizontalPositionOffset, Transform tempTransform)
    {
        ShootTextInfo shootTextInfo = new ShootTextInfo();
        shootTextInfo.content = content;
        shootTextInfo.animationType = textAnimationType;
        shootTextInfo.moveType = textMoveType;
        shootTextInfo.delayMoveTime = delayMoveTime;
        shootTextInfo.initializedVerticalPositionOffset = initializedVerticalPositionOffset;
        shootTextInfo.initializedHorizontalPositionOffset = initializedHorizontalPositionOffset;
        shootTextInfo.cacheTranform = tempTransform;

        CreatShootText(shootTextInfo);
    }
    public void CreatShootText(ShootTextInfo shootTextInfo)
    {
        if (CheckNumber(shootTextInfo.content))
        {
            if (IsAllowAddShootText())
            {
                waitShootTextGroup.Enqueue(shootTextInfo);
            }
            else
            {
                this.LogWarning("數(shù)量過多不能添加!!!");
            }
        }
        else
        {
            this.LogError("飄字數(shù)據(jù)不合法");
        }
    }

    private GameObject InstanceShootText(ShootTextInfo shootTextInfo)
    {
        GameObject shootText = Instantiate(shootTextPrefab);
        //先拼裝字體饼灿,順序顛倒會造成組件無法找到對應物體
        BuildNumber(shootTextInfo, shootText.transform);

        ShootTextComponent tempShootTextComponent = shootText.GetComponent<ShootTextComponent>();
        tempShootTextComponent.SetInfo(shootTextInfo);
        handleShootTextGroup.Add(tempShootTextComponent);
        return shootText;
    }

    private void BuildNumber(ShootTextInfo shootTextInfo, Transform parent)
    {
        string tempNumber = shootTextInfo.content;
        char numberOperator = tempNumber[0];
        string animationType = "";
        string plusOrMinus = "";

        //出現(xiàn)字體時對應的動畫類型
        animationType = shootTextInfo.animationType == TextAnimationType.None ? TextAnimationType.Normal.ToString() : shootTextInfo.animationType.ToString();

        #region 運算符
        GameObject operatorObj = null;
        plusOrMinus = numberOperator == '+' ? operatorPlusKeyPostfix : operatorMinusKeyPostfix;

        operatorObj = Instantiate(numberDic[animationType + plusOrMinus]);
        operatorObj.transform.SetParent(parent, false);
        #endregion

        #region 數(shù)字
        GameObject numberObj = null;
        for (int i = 1; i < tempNumber.Length; i++)
        {
            numberObj = Instantiate(numberDic[animationType + numberPrefix + tempNumber[i]]);
            numberObj.transform.SetParent(parent, false);
        }
        #endregion
    }

    private double ModelInScreenHigh(Transform payerTranform)
    {
        Vector3 playerCenter = payerTranform.GetComponent<Collider>().bounds.center;
        Vector3 halfYVector3 = (Vector3.up * payerTranform.GetComponent<Collider>().bounds.size.y) * 0.5f;
        float topValue = ShootTextCamera.WorldToScreenPoint(playerCenter + halfYVector3).y;
        float bottomValue = ShootTextCamera.WorldToScreenPoint(playerCenter - halfYVector3).y;
        return topValue - bottomValue;
    }

    private bool IsAllowAddShootText()
    {
        return waitShootTextGroup.Count < MaxWaitCount;
    }
    /// <summary>
    /// 正則檢查字符是否合法
    /// </summary>
    /// <param name="content">飄字內(nèi)容</param>
    /// <returns></returns>
    private bool CheckNumber(string content)
    {
        string pattern = @"^(\+|\-)\d*$";
        bool IsLegal = Regex.IsMatch(content, pattern);
        return IsLegal;
    }
}
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市帝美,隨后出現(xiàn)的幾起案子碍彭,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件硕旗,死亡現(xiàn)場離奇詭異窗骑,居然都是意外死亡,警方通過查閱死者的電腦和手機漆枚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進店門创译,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人墙基,你說我怎么就攤上這事软族。” “怎么了残制?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵立砸,是天一觀的道長。 經(jīng)常有香客問我初茶,道長颗祝,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任恼布,我火速辦了婚禮螺戳,結果婚禮上,老公的妹妹穿的比我還像新娘折汞。我一直安慰自己倔幼,他們只是感情好,可當我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布爽待。 她就那樣靜靜地躺著损同,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鸟款。 梳的紋絲不亂的頭發(fā)上膏燃,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天,我揣著相機與錄音何什,去河邊找鬼蹄梢。 笑死,一個胖子當著我的面吹牛富俄,可吹牛的內(nèi)容都是我干的禁炒。 我是一名探鬼主播,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼霍比,長吁一口氣:“原來是場噩夢啊……” “哼幕袱!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起悠瞬,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤们豌,失蹤者是張志新(化名)和其女友劉穎涯捻,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體望迎,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡障癌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了辩尊。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涛浙。...
    茶點故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖摄欲,靈堂內(nèi)的尸體忽然破棺而出轿亮,到底是詐尸還是另有隱情,我是刑警寧澤胸墙,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布我注,位于F島的核電站,受9級特大地震影響迟隅,放射性物質(zhì)發(fā)生泄漏但骨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一智袭、第九天 我趴在偏房一處隱蔽的房頂上張望奔缠。 院中可真熱鬧,春花似錦补履、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至雨女,卻和暖如春谚攒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背氛堕。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工馏臭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人讼稚。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓括儒,卻偏偏與公主長得像,于是被迫代替她去往敵國和親锐想。 傳聞我的和親對象是個殘疾皇子帮寻,可洞房花燭夜當晚...
    茶點故事閱讀 44,884評論 2 354