Ok, So I'm sharing the scripts I use to display various status effects from ORK. for example for the custom Character status screen. Or a health bar.

The way it works.
Step 1: in CharacterSummaryCore use SetCombatant() to set combatant that must be tracked.
Step 2: Use any of status tracking components, and assign CharacterSummaryCore to them.

From there on the specific status type tracking components will track and display changes.



I have not actually tested this outside my project, so if there are adjustment I missed let me know.
I also use Textmeshpro so you may need to adapt to UnityUI text.


CharacterSummaryCore

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using ORKFramework;


namespace Animmal.YezugaRPG
{
public class UnityCombatantEvent : UnityEvent<Combatant> { }
[System.Serializable]
public class UnityBoolEvent : UnityEvent<bool> { }

public class CharacterSummaryCore : MonoBehaviour
{
public UnityCombatantEvent OnCombatantChanged = new UnityCombatantEvent();
public UnityBoolEvent OnPauseStateChanged = new UnityBoolEvent();

private bool _IsPaused;
public bool IsPaused
{
get { return _IsPaused; }
set { _IsPaused = value; }
}


private Combatant _Combatant;
public Combatant Combatant
{
get { return _Combatant; }
}

public void SetCombatant(Combatant _Combatant, bool _StartPaused = false)
{
this._Combatant = _Combatant;
OnCombatantChanged.Invoke(_Combatant);
SetPauseState(_StartPaused);
}

public void SetPauseState(bool _Pause)
{
IsPaused = _Pause;
OnPauseStateChanged.Invoke(IsPaused);
}
}
}



CharacterSummaryItemBase is just a parent blank component to handle shared functionality. You just need to have it in project.

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

namespace Animmal.YezugaRPG
{
public class CharacterSummaryItemBase : MonoBehaviour
{
public CharacterSummaryCore CharacterStatsPreviewCore;


protected Combatant SavedCombatant;
protected bool IsPaused;

protected virtual void Start()
{
CharacterStatsPreviewCore.OnCombatantChanged.AddListener(CombatantChanged);
CharacterStatsPreviewCore.OnPauseStateChanged.AddListener(PauseStateChanged);
}

protected virtual void ActivateListeners()
{

}

protected virtual void DeActivateListeners()
{

}

protected virtual void PauseStateChanged(bool _Paused)
{
IsPaused = _Paused;
}

protected virtual void CombatantChanged(Combatant _Combatant)
{
DeActivateListeners();
SavedCombatant = _Combatant;
ActivateListeners();
}
}
}
Post edited by hellwalker on
  • edited May 26
    Experience Tracker

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    using TMPro;
    using ORKFramework;

    namespace Animmal.YezugaRPG
    {
    public class CharacterSummaryExperience: CharacterSummaryItemBase
    {
    public TextMeshProUGUI ExperienceText;
    public Image ExperienceBar;
    public int ExperienceStatusValueID;

    protected override void CombatantChanged(Combatant _Combatant)
    {
    if (ExperienceText != null)
    ExperienceText.text = _Combatant.Status[ExperienceStatusValueID].GetValue().ToString() + "/" + _Combatant.Status[ExperienceStatusValueID].GetMaxValue().ToString();
    if (ExperienceBar != null)
    {
    float _CurrentValue = _Combatant.Status[ExperienceStatusValueID].GetValue() - _Combatant.Status[ExperienceStatusValueID].GetMinValue();
    float _MaxValue = _Combatant.Status[ExperienceStatusValueID].GetMaxValue() - _Combatant.Status[ExperienceStatusValueID].GetMinValue();
    ExperienceBar.fillAmount = (_CurrentValue / _MaxValue);
    }
    }
    }
    }


    Level Tracker
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    using TMPro;
    using ORKFramework;

    namespace Animmal.YezugaRPG
    {
    public class CharacterSummaryLevel : CharacterSummaryItemBase
    {
    public TextMeshProUGUI LevelText;


    protected override void CombatantChanged(Combatant _Combatant)
    {
    if (LevelText != null)
    LevelText.text = _Combatant.Status.Level.ToString();
    }
    }
    }


    Name Tracker
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    using TMPro;
    using ORKFramework;

    namespace Animmal.YezugaRPG
    {
    public class CharacterSummaryName : CharacterSummaryItemBase
    {
    public TextMeshProUGUI NameText;
    public TMP_InputField NameInputField;


    protected override void CombatantChanged(Combatant _Combatant)
    {
    if (NameText != null)
    NameText.text = _Combatant.GetName();
    if (NameInputField != null)
    NameInputField.text = _Combatant.GetName();
    }
    }
    }
    Post edited by hellwalker on
  • edited May 28
    For Ability Tracker you need to setup a ability item prefab and ability list. I call them skills hence the naming.

    CharacterSummarySkillList component which will spawn abilities of defined ability types.
    using System.Collections;
    using System.Collections.Generic;
    using ORKFramework;
    using UnityEngine;

    namespace Animmal.YezugaRPG
    {
    public class CharacterSummarySkillList : CharacterSummaryItemBase
    {
    public CharacterSummarySkillItem SkillItemPrefab;
    public Transform SkillItemParent;
    public List<int> SkillTypes = new List<int>();

    List<CharacterSummarySkillItem> SkillItems = new List<CharacterSummarySkillItem>();


    protected override void ActivateListeners()
    {
    if (SavedCombatant != null)
    SavedCombatant.Changed += Abilities_AbilitiesChanged;
    }


    protected override void DeActivateListeners()
    {
    if (SavedCombatant != null)
    SavedCombatant.Changed -= Abilities_AbilitiesChanged;
    }

    private void Abilities_AbilitiesChanged(Combatant combatant)
    {
    UpdateSkills();
    }

    protected void UpdateSkills()
    {
    if (SavedCombatant == null)
    return;

    List<AbilityShortcut> _Skills = new List<AbilityShortcut>();

    for (int i = 0; i < SkillTypes.Count; i++)
    {
    _Skills.AddRange(SavedCombatant.Abilities.GetByType(false, SkillTypes[i], false, true, true, true, true, IncludeCheckType.No));
    }

    for (int i = 0; i < _Skills.Count; i++)
    {
    if (i >= SkillItems.Count)
    {
    SkillItems.Add(Instantiate(SkillItemPrefab, SkillItemParent) as CharacterSummarySkillItem);
    }
    SkillItems[i].gameObject.SetActive(true);
    SkillItems[i].Setup(_Skills[i].ID, CharacterStatsPreviewCore);
    }

    for (int i = _Skills.Count; i < SkillItems.Count; i++)
    {
    SkillItems[i].gameObject.SetActive(false);
    }
    }

    protected override void CombatantChanged(Combatant _Combatant)
    {
    DeActivateListeners();
    SavedCombatant = _Combatant;
    ActivateListeners();
    UpdateSkills();
    }
    }
    }


    Skill Item Component set this up as prefab to be used in Skill list code above.

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    using TMPro;
    using ORKFramework;

    namespace Animmal.YezugaRPG
    {
    public class CharacterSummarySkillItem : CharacterSummaryItemBase
    {
    public RawImage Icon;
    public TextMeshProUGUI NameText;
    public TextMeshProUGUI DescriptionText;
    public TextMeshProUGUI LevelText;
    public bool NameAllCaps = true;
    public bool DescriptionAllCaps = true;
    int SkillID;

    public void Setup(int _SkillID, CharacterSummaryCore _SummaryCore)
    {
    CharacterStatsPreviewCore = _SummaryCore;
    SavedCombatant = _SummaryCore.Combatant;

    SkillID = _SkillID;

    string _Name = ORK.Abilities.Get(_SkillID).GetName();
    string _Description = ORK.Abilities.Get(_SkillID).GetDescription();

    if (Icon != null)
    Icon.texture = ORK.Abilities.Get(_SkillID).GetIcon();
    if (NameText != null)
    {
    NameText.text = NameAllCaps ? _Name.ToUpper() : _Name;
    }
    if (LevelText != null)
    {
    LevelText.text = SavedCombatant.Abilities.Get(SkillID).Level.ToString();
    }
    if (DescriptionText != null)
    {
    DescriptionText.text = DescriptionAllCaps ? _Description.ToUpper() : _Description;
    }
    }
    }
    }
    Post edited by hellwalker on
  • edited May 30
    Status value. Ok this is a bit complex, so you might want to cut code you don't need.

    I basically have an Advanced Slider Bar component. It supports bars that have preview functionality and lock functionality.

    Preview is when you want a copy of the slider to instantly get to desired value, and part to animate and slowly move towards value. This is game design trick to make bar change more noticeable. You could have white bar that instantly moves to value, and red bar that catches up to it.

    Lock is another game design trick. If you have mechanic where maximum health could be reduced for example, you might want to have a "Locked" visual for a bar.

    Anyway if you don't want this just delete
    " public UI_Slider Slider;
    public bool UseStatPenaltyPreview;
    public int PenaltyFormula;"

    These values and all the code that errors out because of this.

    Status Value Tracker

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    using TMPro;
    using ORKFramework;
    using Animmal.UI;

    namespace Animmal.YezugaRPG
    {
    public class CharacterSummaryStatusValue : CharacterSummaryItemBase
    {
    public Image Icon;
    public TextMeshProUGUI NameText;
    public TextMeshProUGUI DescriptionText;
    public TextMeshProUGUI ValueText;
    public int StatusID;
    public UI_Slider Slider;
    public bool UseStatPenaltyPreview;
    public int PenaltyFormula;

    public bool NameAllCaps = true;
    public bool ShowMaxValue;
    public string ValueSeparator = "/";

    protected override void ActivateListeners()
    {
    if (SavedCombatant != null)
    SavedCombatant.Status[StatusID].ValueChanged += CharacterStatsPreviewStatusValue_Changed;
    }

    protected override void DeActivateListeners()
    {
    if (SavedCombatant != null)
    SavedCombatant.Status[StatusID].ValueChanged -= CharacterStatsPreviewStatusValue_Changed;
    }

    protected override void CombatantChanged(Combatant _Combatant)
    {
    base.CombatantChanged(_Combatant);
    SavedCombatant = _Combatant;
    SetupInitialValues();
    }

    void SetupInitialValues()
    {
    if (SavedCombatant == null)
    return;

    if (Icon != null)
    Icon.sprite = SavedCombatant.Status[StatusID].Setting.GetSprite();
    if (NameText != null)
    {
    string _Name = ORK.StatusValues.Get(StatusID).GetName();
    _Name = NameAllCaps ? _Name.ToUpper() : _Name;
    NameText.text = _Name;
    }
    if (DescriptionText != null)
    DescriptionText.text = ORK.StatusValues.Get(StatusID).GetDescription();
    if (ValueText != null)
    ValueText.text = ShowMaxValue ? SavedCombatant.Status[StatusID].GetValue().ToString() + ValueSeparator + SavedCombatant.Status[StatusID].GetMaxValue() : SavedCombatant.Status[StatusID].GetValue().ToString();
    if (Slider != null)
    {
    Slider.SetSlider(SavedCombatant.Status[StatusID].GetValue(), SavedCombatant.Status[StatusID].GetMaxValue(), false, GetPenaltyValue());
    }
    }

    private void CharacterStatsPreviewStatusValue_Changed(Combatant combatant, int id, int change)
    {
    if (ValueText != null)
    ValueText.text = ShowMaxValue ? SavedCombatant.Status[StatusID].GetValue().ToString() + ValueSeparator + SavedCombatant.Status[StatusID].GetMaxValue() : SavedCombatant.Status[StatusID].GetValue().ToString();
    if (Slider != null)
    {
    Slider.SetSlider(SavedCombatant.Status[StatusID].GetValue(), SavedCombatant.Status[StatusID].GetMaxValue(), true, GetPenaltyValue());
    }
    }

    float GetPenaltyValue()
    {
    if (UseStatPenaltyPreview == false)
    return 0;

    FormulaCall _FormulaCall = new FormulaCall(0, SavedCombatant, SavedCombatant);
    return Mathf.Abs(ORK.Formulas.Get(PenaltyFormula).Calculate(_FormulaCall));
    }
    }
    }



    UI_SliderImage You have to put this on each Bar Image, for Fill based slider be sure to have ui images type set to filled.
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    using TMPro;

    namespace Animmal.UI
    {
    public class UI_SliderImage : MonoBehaviour
    {
    public enum SliderTypeEnum { Fill, Width, Height }
    public SliderTypeEnum SliderType;
    public Gradient SliderGradient;
    public RectTransform SizeRect;
    public Image SliderImage;

    float _CurrentValue;
    public float CurrentValue { get { return _CurrentValue; } }

    public void SetValue(float _NormalizedValue)
    {
    _CurrentValue = _NormalizedValue;
    switch (SliderType)
    {
    case SliderTypeEnum.Fill:
    SliderImage.fillAmount = _NormalizedValue;
    break;
    case SliderTypeEnum.Width:
    SliderImage.rectTransform.sizeDelta = new Vector2(SizeRect.sizeDelta.x * _NormalizedValue, SizeRect.sizeDelta.y);
    break;
    case SliderTypeEnum.Height:
    SliderImage.rectTransform.sizeDelta = new Vector2(SizeRect.sizeDelta.x, SizeRect.sizeDelta.y * _NormalizedValue);
    break;
    }
    SliderImage.color = SliderGradient.Evaluate(_NormalizedValue);
    }
    }
    }



    Actual Slider, you can just leave Lock or preview images unassigned if you just want a simple bar.
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    using TMPro;
    using Sirenix.OdinInspector;

    namespace Animmal.UI
    {
    [System.Serializable]
    public class SliderItem
    {
    public UI_SliderImage SliderMain;
    [Space()]
    public UI_SliderImage SliderPreview;
    public float SliderAnimSpeed;

    WaitForEndOfFrame _WaitForEndOfFrame;
    WaitForEndOfFrame WaitForEndOfFrame { get { if (_WaitForEndOfFrame == null) _WaitForEndOfFrame = new WaitForEndOfFrame(); return _WaitForEndOfFrame;} }
    IEnumerator AnimationCoroutine;

    public void SetSlider(float _Value, float _MaxValue, float _LockValue, bool _AnimateSlider, MonoBehaviour _MonoBehavior)
    {
    if (AnimationCoroutine != null)
    _MonoBehavior.StopCoroutine(AnimationCoroutine);
    float _NormalizedValue = _Value / (_MaxValue +_LockValue);
    if (_AnimateSlider == false || SliderMain.CurrentValue == _NormalizedValue)
    {
    SliderMain.SetValue(_NormalizedValue);
    }
    else
    {
    AnimationCoroutine = SliderAnimation(_NormalizedValue);
    _MonoBehavior.StartCoroutine(AnimationCoroutine);
    }
    }

    IEnumerator SliderAnimation(float _NormalizedValue)
    {
    float _OriginalValue = SliderMain.CurrentValue;
    float _MaxLerpTime = 1*(Mathf.Abs(_OriginalValue - _NormalizedValue)/SliderAnimSpeed);
    float _LerpTime = 0;
    float _Time = 0;

    UI_SliderImage _StaticSlider = _NormalizedValue > _OriginalValue ? SliderPreview : SliderMain;
    UI_SliderImage _AnimatedSlider = _NormalizedValue > _OriginalValue ? SliderMain : SliderPreview;

    if (_StaticSlider != null)
    _StaticSlider.SetValue(_NormalizedValue);

    while (Mathf.Abs(_AnimatedSlider.CurrentValue - _NormalizedValue) > 0.01f)
    {
    _LerpTime += Time.deltaTime;

    if (_LerpTime > _MaxLerpTime)
    _LerpTime = _MaxLerpTime;

    _Time = _LerpTime / _MaxLerpTime;
    float _Value = Mathf.Lerp(_OriginalValue, _NormalizedValue, _Time);
    _AnimatedSlider.SetValue(_Value);
    yield return WaitForEndOfFrame;
    }
    _AnimatedSlider.SetValue(_NormalizedValue);
    }
    }

    public class UI_Slider : MonoBehaviour
    {
    public SliderItem MainSlider;
    public SliderItem LockSlider;
    public TextMeshProUGUI ValueText;
    public bool ShowFullValue;
    [ShowIf("ShowFullValue")]
    public string ValueSeparator = "/";

    public void SetSlider(float _Value, float _MaxValue, bool _AnimateSlider, float _LockValue = 0)
    {
    if (ValueText != null)
    ValueText.text = ShowFullValue ? _Value.ToString() + ValueSeparator + _MaxValue.ToString() : _Value.ToString();
    if (MainSlider.SliderMain != null)
    MainSlider.SetSlider(_Value, _MaxValue, _LockValue, _AnimateSlider, this);
    if (LockSlider.SliderMain != null)
    LockSlider.SetSlider(_LockValue, _MaxValue+_LockValue, 0, false, this);
    }
    }
    }
    Post edited by hellwalker on
  • The Slider and Abilities codes updated to fix some issues.

    Here is Status Effect similar to abilities


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

    namespace Animmal.YezugaRPG
    {
    public class CharacterSummaryStatusEffectList : CharacterSummaryItemBase
    {
    public CharacterSummaryStatusEffectItem StatusEffectItemPrefab;
    public Transform StatusEffectItemParent;
    public List<StatusEffectTypesEnum> StatusEffectTypes = new List<StatusEffectTypesEnum>();

    List<CharacterSummaryStatusEffectItem> StatusEffectItems = new List<CharacterSummaryStatusEffectItem>();


    protected override void ActivateListeners()
    {
    if (SavedCombatant != null)
    SavedCombatant.Status.Effects.Changed += Effects_Changed;
    }

    private void Effects_Changed()
    {
    UpdateStatusEffects();
    }

    protected override void DeActivateListeners()
    {
    if (SavedCombatant != null)
    SavedCombatant.Status.Effects.Changed -= Effects_Changed;
    }

    protected void UpdateStatusEffects()
    {
    if (SavedCombatant == null)
    return;

    List<StatusEffect> _StatusEffects = new List<StatusEffect>();
    _StatusEffects = SavedCombatant.Status.Effects.GetEffects();

    List<StatusEffect> _FilteredStatusEffects = new List<StatusEffect>();

    for (int i = 0; i < _StatusEffects.Count; i++)
    {
    for (int j = 0; j < StatusEffectTypes.Count; j++)
    {
    if (_StatusEffects[i].TypeID == (int)StatusEffectTypes[j])
    _FilteredStatusEffects.Add(_StatusEffects[i]);
    }
    }

    for (int i = 0; i < _FilteredStatusEffects.Count; i++)
    {
    if (i >= StatusEffectItems.Count)
    {
    StatusEffectItems.Add(Instantiate(StatusEffectItemPrefab, StatusEffectItemParent) as CharacterSummaryStatusEffectItem);
    }
    StatusEffectItems[i].gameObject.SetActive(true);
    StatusEffectItems[i].Setup(_FilteredStatusEffects[i].ID, CharacterStatsPreviewCore);
    }

    for (int i = _FilteredStatusEffects.Count; i < StatusEffectItems.Count; i++)
    {
    StatusEffectItems[i].gameObject.SetActive(false);
    }
    }

    protected override void CombatantChanged(Combatant _Combatant)
    {
    DeActivateListeners();
    SavedCombatant = _Combatant;
    ActivateListeners();
    UpdateStatusEffects();
    }
    }
    }


    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    using TMPro;
    using ORKFramework;

    namespace Animmal.YezugaRPG
    {
    public class CharacterSummaryStatusEffectItem : CharacterSummaryItemBase
    {
    public RawImage Icon;
    public TextMeshProUGUI NameText;
    public TextMeshProUGUI DescriptionText;
    public bool NameAllCaps = true;
    public bool DescriptionAllCaps = true;
    int StatusEffectID;

    public void Setup(int _StatusEffectID, CharacterSummaryCore _SummaryCore)
    {
    CharacterStatsPreviewCore = _SummaryCore;
    SavedCombatant = _SummaryCore.Combatant;

    StatusEffectID = _StatusEffectID;

    string _Name = ORK.StatusEffects.Get(_StatusEffectID).GetName();
    string _Description = ORK.StatusEffects.Get(_StatusEffectID).GetDescription();

    if (Icon != null)
    Icon.texture = ORK.StatusEffects.Get(_StatusEffectID).GetIcon();
    if (NameText != null)
    {
    NameText.text = NameAllCaps ? _Name.ToUpper() : _Name;
    }

    if (DescriptionText != null)
    {
    DescriptionText.text = DescriptionAllCaps ? _Description.ToUpper() : _Description;
    }

    }
    }
    }
Sign In or Register to comment.