Unity——技能系統(三)

小紫蘇發表於2021-11-11

Unity技能系統(三)

Unity技能系統(一)

Unity技能系統(二)

Demo展示

六.Buff系統

buff分為增益和減益buff,應該區分開來;

本來計劃是也用與或非來記錄buff的,一個技能可能有多個buff,但是好像用list來儲存也是一樣的;

一個技能只能有兩個buff圖示,一個增益buff給自身,一個減益buff給敵人;

一個技能的增益和減益buff可能有多重效果;

比如:技能閃電——導致減速+感電+擊退+自身增加狂暴(變態技能);

但是說這麼說,寫起來比較麻煩,就不那麼細分了,一種效果一個圖示單獨計時;

這裡面需求比較複雜,根據需求自行改寫吧;

/// <summary>
/// Buff型別,可疊加
/// </summary>
public enum BuffType
{
    None,
    Burn = 2,           //點燃
    Slow = 4,           //減速
    Light = 8,          //感電
    Stun = 16,          //眩暈
    Poison = 32,        //中毒
    BeatBack = 64,      //擊退
    BeatUp = 128,       //擊飛
    Pull = 256,         //拉拽
    AddDefence = 512,
    RecoverHp = 1024,
}

1.BuffRun

掛載在擁有buff的物體上,計算buff的效果,如減傷,掉血,減速等;

同時負責buff計時,提供buff計時重新整理介面供重複buffIcon呼叫(也可疊加buff層數按需求);

使用了靜態方法初始化和靜態連結串列儲存了buff特效的資訊,用來動態載入buff特效預製體;

public class BuffRun : MonoBehaviour
{
    private float durationTime;
    public BuffType bufftype;
    private float value;            //傷害或者加成

    private float interval;

    private float attackTimer;

    private float curTime;

    private CharacterStatus target;
    
    //新增buff時候初始化buffrun
    public void InitBuff(BuffType buffType,float duration,float value,float interval)
    {
        bufftype = buffType;
        
        if (buffType == BuffType.BeatBack || buffType == BuffType.BeatUp || buffType == BuffType.Pull)
            duration = 2f;
        
        durationTime = duration;
        this.value = value;
        this.interval = interval;
        curTime = 0;
    }
	
    //重置buff時間
    public void Reset()
    {
        attackTimer = 0;
        curTime = 0;
    }

    void Start()
    {
        curTime = 0;
        target = GetComponent<CharacterStatus>();
        StartCoroutine(ExcuteDamage());
    }

    private void Update()
    {
        curTime += Time.deltaTime;
        
        if(curTime > durationTime)
            Destroy(this);
    }
	
    //執行buff效果,支援多段影響
    private IEnumerator ExcuteDamage()
    {
        attackTimer = 0; //已持續攻擊的時間

        do
        {
            //對敵人的影響
            TargetImpact();
            
            yield return new WaitForSeconds(interval);
            attackTimer += interval;
            //做傷害數值的計算
        } while (durationTime > attackTimer);
        
        Destroy(this);
    }

    private void TargetImpact()
    {
        //buff特效掛載點,有些buff掛載不在HitFxPos,所以寫在上面
        Transform fxPosTf = target.HitFxPos;
		
        //根據不同buff做相應的效果響應
        if (bufftype == BuffType.Burn || bufftype == BuffType.Poison || bufftype == BuffType.Light)
            target.OnDamage(value, gameObject, true);
        else if (bufftype == BuffType.Slow)//減速
            fxPosTf = target.transform;
        else if (bufftype == BuffType.BeatBack)
        {
            Vector3 dir = -target.transform.position + GameObject.FindGameObjectWithTag("Player").transform.position;
            dir.y = 0;
            target.transform.DOMove(target.transform.position - dir.normalized * value,0.5f);
            durationTime = 2f;
        }
        else if (bufftype == BuffType.BeatUp)
        {
            target.transform.DOMove(target.transform.position - Vector3.up * value,0.5f);
            durationTime = 2f;
        }
        else if (bufftype == BuffType.AddDefence)
        {
            fxPosTf = target.transform;
            target.defence += value;
        }
        else if (bufftype == BuffType.RecoverHp)
        {
            target.OnDamage(-value, gameObject, true);
        }

        //掛載buff特效
        if (buffFx.ContainsKey(bufftype))
        {
            GameObject go = Resources.Load<GameObject>($"Skill/{buffFx[bufftype]}");
            GameObject buffGo = GameObjectPool.I.CreateObject(buffFx[bufftype], go, fxPosTf.position, fxPosTf.rotation);
            buffGo.transform.SetParent(fxPosTf);
            GameObjectPool.I.Destory(buffGo, interval);
        }
    }

    //儲存buff特效名稱和對應buff型別
    private static Dictionary<BuffType, string> buffFx = new Dictionary<BuffType, string>();
	//初始化buff特效資訊
    public static void InitAllBuff()
    {
        buffFx.Add(BuffType.Burn,"Skill_32_R_Fly_100");
        buffFx.Add(BuffType.Light,"Skill_75_Cast");
        buffFx.Add(BuffType.Slow,"Skill_21_R_Fly_100");
        buffFx.Add(BuffType.Poison,"Skill_12_R_Fly_100");
        buffFx.Add(BuffType.AddDefence,"FX_CHAR_Aura");
        buffFx.Add(BuffType.RecoverHp,"FX_Heal_Light_Cast");
    }
	
    //獲取buff剩餘時間介面
    public float GetRemainTime()
    {
        return durationTime - curTime;
    }
    
    //buff結束恢復目標屬性
    private void OnDisable()
    {
        if (bufftype == BuffType.Slow)
            ;
        else if (bufftype == BuffType.AddDefence)
            target.defence -= value;
    }
}

2.BuffIcon

buff圖示類,顯示倒數計時數字顯示;

這裡寫的不是很好,應該載入buffrun的同時載入bufficon,bufficon中不需要單獨計時;

暫時改不動了=-=;

bufficon中新增buffRun欄位,新增bufficon的同時,賦值buffrun;

通過buffrun獲取buff型別和剩餘倒數計時;

這也用靜態方法儲存了bufficon的資訊,用來動態載入,可以通過外部匯入資料來儲存;

public static Dictionary<BuffType, string> buffIconName = new Dictionary<BuffType, string>();

public static void InitBuffIconName()
{
    buffIconName.Add(BuffType.Burn,"Buff_13");
    buffIconName.Add(BuffType.Slow,"Buff_15");
    buffIconName.Add(BuffType.Stun,"Buff_12");
    buffIconName.Add(BuffType.Poison,"Buff_14");
    buffIconName.Add(BuffType.BeatBack,"Buff_5");
    buffIconName.Add(BuffType.BeatUp,"Buff_4");
    buffIconName.Add(BuffType.Pull,"Buff_6");
    buffIconName.Add(BuffType.AddDefence,"Buff_3");
    buffIconName.Add(BuffType.RecoverHp,"Buff_7");
    buffIconName.Add(BuffType.Light,"Buff_8");
}

這裡寫的不太行,參考一下吧;

public class BuffIcon : MonoBehaviour
{
    public Text textCD;
    public Image imgIcon;
    
    private float durationTime;
    private float curTime;

    public BuffType buffType;

    public void LoadIcon(BuffType buffType, float duration)
    {
        durationTime = duration;
        this.buffType = buffType;
        Sprite[] temp = Resources.LoadAll<Sprite>("BuffIcon/Buff");
        if (temp != null)
        {
            foreach (var sp in temp)
            {
                if (sp.name == SkillDeployer.buffIconName[buffType])
                {
                    imgIcon.sprite = Instantiate(sp);
                }
            }
        }
    }

    private void OnEnable()
    {
        curTime = 0;
    }

    void Update()
    {
        curTime += Time.deltaTime;
        
        textCD.text = (durationTime - curTime).ToString("F0");

        if (curTime > durationTime)
        {
            gameObject.SetActive(false);
            curTime = 0;
        }
    }

    public void Refresh()
    {
        //Debug.Log("已有buff重新整理持續時間");
        curTime = 0;
    }
}

3.坑點

1.敵人uiPortrait的UI儘量不用使用延遲設定Active來控制顯隱藏,Active為false時,bufficon將不再執行,等下次再顯示uiPortrait時buff圖示會顯示錯誤;

2.採用每次造成傷害將當前目標的uiPortrait調整到顯示位置,其他所有敵人的uiPortrait調整位置超出顯示區域;

因此需要一個單例來儲存所有的uiPortrait;就將它寫在MonsterMgr中吧,反正也是用來管理敵人的,順便管理一下敵人頭像也沒什麼毛病;

提供三個方法,新增uiPortrait,刪除uiPortrait,隱藏uiPortrait(移除顯示區域);

private List<UIPortrait> allEnemyPortraits = new List<UIPortrait>();
public void AddEnemyPortraits(UIPortrait uiPortrait)
{
    allEnemyPortraits.Add(uiPortrait);
}
public void RemoveEnemyPortraits(UIPortrait uiPortrait)
{
    allEnemyPortraits.Remove(uiPortrait);
}
public void HideAllEnemyPortraits()
{
    foreach (var it in allEnemyPortraits)
    {
        it.GetComponent<RectTransform>().anchoredPosition = hidePos;
    }
}

4.扇形倒數計時

UIbuff圖示放置兩層image元件物體,父節點設定透明度;

1636621558(1)

子物體image元件按下圖設定,填充模式,填充百分比,以及順逆時針;

1636621295

程式碼動態設定fillAmount的百分比;

//技能倒數計時舉例,寫在Update中
float cd = csm.skills[i].coolRemain;
skillImgs[i].fillAmount = 1 - cd / csm.skills[i].skill.coolTime;
skillTexts[i].text = cd.ToString("F0");
if (skillTexts[i].text == "0")
    skillTexts[i].text = "";

效果:

121212312

相關文章