Unity - skill system

Keywords: Unity

Unity skill system (III)

Unity skill system (I)

Unity skill system (II)

Demo presentation

6, Buff system

Buffs are divided into gain and loss buff s, which should be distinguished;

Originally, it was planned to use and or not to record buffs. A skill may have multiple buffs, but it is the same as using list to store buffs;

A skill can only have two buff icons, one gain buff for itself and one decrease buff for the enemy;

A skill's gain and loss buff may have multiple effects;

For example: skill lightning - causes deceleration + sensing + repulsion + increases rage (abnormal skill);

However, it is more troublesome to write, so it is not so subdivided. One effect and one icon are timed separately;

The requirements are complex. Rewrite them according to the needs;

/// <summary>
///Buff type, stackable
/// </summary>
public enum BuffType
{
    None,
    Burn = 2,           //light
    Slow = 4,           //Slow down
    Light = 8,          //Induced electricity
    Stun = 16,          //vertigo
    Poison = 32,        //poisoning
    BeatBack = 64,      //repel
    BeatUp = 128,       //Strike fly
    Pull = 256,         //pull
    AddDefence = 512,
    RecoverHp = 1024,
}

1.BuffRun

Mount it on the object with buff and calculate the buff effect, such as damage reduction, blood loss, deceleration, etc;

At the same time, it is responsible for buff timing and provides a buff timing refresh interface for repeated buffIcon calls (you can also stack buff layers as required);

Static method initialization and static linked list are used to store the information of buff effect, which is used to dynamically load the preform of buff effect;

public class BuffRun : MonoBehaviour
{
    private float durationTime;
    public BuffType bufftype;
    private float value;            //Damage or bonus

    private float interval;

    private float attackTimer;

    private float curTime;

    private CharacterStatus target;
    
    //Initialize buffrun when adding buff
    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;
    }
	
    //Reset buff time
    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);
    }
	
    //Execute buff effect and support multi segment influence
    private IEnumerator ExcuteDamage()
    {
        attackTimer = 0; //Duration of attack

        do
        {
            //Impact on the enemy
            TargetImpact();
            
            yield return new WaitForSeconds(interval);
            attackTimer += interval;
            //Calculate the damage value
        } while (durationTime > attackTimer);
        
        Destroy(this);
    }

    private void TargetImpact()
    {
        //Buff special effect mount point. Some buff mounts are not in HitFxPos, so they are written on it
        Transform fxPosTf = target.HitFxPos;
		
        //Make corresponding effect response according to different buff s
        if (bufftype == BuffType.Burn || bufftype == BuffType.Poison || bufftype == BuffType.Light)
            target.OnDamage(value, gameObject, true);
        else if (bufftype == BuffType.Slow)//Slow down
            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);
        }

        //Mount buff effect
        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);
        }
    }

    //Store buff effect name and corresponding buff type
    private static Dictionary<BuffType, string> buffFx = new Dictionary<BuffType, string>();
	//Initialize buff effect information
    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");
    }
	
    //Get buff remaining time interface
    public float GetRemainTime()
    {
        return durationTime - curTime;
    }
    
    //buff end recovery target attribute
    private void OnDisable()
    {
        if (bufftype == BuffType.Slow)
            ;
        else if (bufftype == BuffType.AddDefence)
            target.defence -= value;
    }
}

2.BuffIcon

buff icon class, display countdown digital display;

It's not very well written here. You should load buffrun and bufficon at the same time. You don't need separate timing in bufficon;

It cannot be changed temporarily = - =;

Add buffrun field to bufficon, and assign buffrun value while adding bufficon;

Obtain the buff type and remaining countdown through buffrun;

This also stores the information of the traffic in a static way for dynamic loading, which can be stored by importing data externally;

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");
}

What is written here is not very good. Please refer to it;

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("existing buff refresh duration");
        curTime = 0;
    }
}

3. Pit point

1. The UI of the enemy uiportal should try not to use the delay setting Active to control the display and hiding. When Active is false, the bufficon will no longer run. When uiportal is displayed next time, the buff icon will display an error;

2. Adjust the uiPortrait of the current target to the display position by causing damage each time, and the uiPortrait adjustment position of all other enemies exceeds the display area;

Therefore, a single instance is needed to store all uiportals; Just write it in monster Mgr. Anyway, it is also used to manage the enemy. By the way, there is nothing wrong with managing the enemy's Avatar;

Three methods are provided: add uiportlet, delete uiportlet, and hide uiportlet (remove the display area);

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. Sector countdown

The UIbuff icon places two layers of image component objects, and the parent node sets the transparency;

The sub object image component is set according to the following figure, filling mode, filling percentage, and clockwise and counterclockwise;

Code dynamically sets the percentage of fillAmount;

//An example of skill countdown is written in 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 = "";

effect:

Posted by toodi4 on Thu, 11 Nov 2021 18:26:50 -0800