Forum begins after the advertisement:
[Part 18] Tooltips Break the UI Stats Display
Home › Forums › Video Game Tutorial Series › Creating a Rogue-like Shoot-em Up in Unity › [Part 18] Tooltips Break the UI Stats Display
- This topic has 7 replies, 2 voices, and was last updated 1 week, 6 days ago by Terence.
-
AuthorPosts
-
November 18, 2024 at 7:06 pm #16397::
Hi,
So that it’d be easier to understand some of the variables in CharacterData.Stats, I added some Tooltips to them, such as stating that it’s a percentage that should be entered as a decimal.
For example:[Tooltip("0.1 = 10%")] [Range(-1,10)] public float moveSpeed; [Tooltip("0.1 = 10%")] [Range(-1,10)] public float might; [Tooltip("0.1 = 10%")] [Range(-1,10)] public float area;
Unfortunately, I later realised this broke the code as it was, I believe, taking the tooltips as fields to enter into the UI Stats Display prefab’s text fields.
Is there any way to improve the UIStatsDisplay.cs code so that it doesn’t include tooltips?
Kind Regards,
SeanNovember 18, 2024 at 7:07 pm #16398::The error that comes up, for those who’d like to know, is:
AmbiguousMatchException: Multiple custom attributes of the same type found. System.Attribute.GetCustomAttribute (System.Reflection.MemberInfo element, System.Type attributeType, System.Boolean inherit) (at <834b2ded5dad441e8c7a4287897d63c7>:0) System.Attribute.GetCustomAttribute (System.Reflection.MemberInfo element, System.Type attributeType) (at <834b2ded5dad441e8c7a4287897d63c7>:0) UIStatsDsiplay.UpdateStatsFields () (at Assets/Scripts/UI/UIStatsDisplay.cs:58) UIStatsDsiplay.OnEnable () (at Assets/Scripts/UI/UIStatsDisplay.cs:17) UnityEngine.GameObject:SetActive(GameObject, Boolean) GameManager:PauseGame() (at Assets/Scripts/GameManager.cs:186) GameManager:CheckForPauseAndResume() (at Assets/Scripts/GameManager.cs:203) GameManager:Update() (at Assets/Scripts/GameManager.cs:98)
November 18, 2024 at 7:22 pm #16400::Sean, can you share your code for
UIStatsDisplay
here? The[Tooltip]
attribute shouldn’t be causing any error that you are seeing.November 18, 2024 at 8:09 pm #16408::Sure thing!
using System.Collections; using System.Collections.Generic; using System.Reflection; using System.Text; using TMPro; using UnityEngine;
public class UIStatsDisplay : MonoBehaviour {
public PlayerStats player; public bool displayCurrentHealth = false; public bool updateInEditor = false; TextMeshProUGUI statNames; TextMeshProUGUI statValues; private void OnEnable() { UpdateStatsFields(); } private void OnDrawGizmosSelected(){ if (updateInEditor) { UpdateStatsFields(); } } public void UpdateStatsFields() { if (!player) { return; } if (!statNames) { statNames = transform.GetChild(0).GetComponent<TextMeshProUGUI>(); } if (!statValues) { statValues = transform.GetChild(1).GetComponent<TextMeshProUGUI>(); } //Use StringBuilders so that the string manipulation runs faster. StringBuilder names = new StringBuilder(); StringBuilder values = new StringBuilder(); //Add the current health to the stat box. if (displayCurrentHealth) { names.AppendLine("Health"); values.AppendLine(player.CurrentHealth.ToString()); } FieldInfo[] fields = typeof(CharacterData.Stats).GetFields(BindingFlags.Public | BindingFlags.Instance); foreach (FieldInfo field in fields) { //Render stat names. names.AppendLine(field.Name); //Get the stat value. object value = field.GetValue(player.Stats); float fvalue = value is int ? (int)value : (float)value; //Print it as a percentage if it has an attribute assigned and is a float. PropertyAttribute attribute = (PropertyAttribute)PropertyAttribute.GetCustomAttribute(field, typeof(PropertyAttribute)); if (attribute != null && field.FieldType == typeof(float)) { float percentage = Mathf.Round(fvalue * 100 - 100); //If the stat value is 0, just put a dash. if (Mathf.Approximately(percentage, 0)) { values.Append('-').Append('\n'); } else { if (percentage > 0) { values.Append('+'); } values.Append(percentage).Append('%').Append('\n'); } } else { values.Append(fvalue).Append('\n'); } //Update the fields with the strings we built. statNames.text = PrettifyNames(names); statValues.text = values.ToString(); } } public static string PrettifyNames(StringBuilder input) { //Return an empty string if StringBuilder is empty. if (input.Length <= 0) { return string.Empty; } StringBuilder result = new StringBuilder(); char last = '\0'; for (int i = 0; i < input.Length; i++) { char c = input[i]; //Check when to uppercase or add spaces to a character. if (last == '\0' || char.IsWhiteSpace(last)) { c = char.ToUpper(c); } else if (char.IsUpper(c)) { result.Append(' '); //Insert space before capital letter } result.Append(c); last = c; } return result.ToString(); } private void Reset() { player = FindObjectOfType<PlayerStats>(); }
}
has upvoted this post. November 19, 2024 at 2:41 pm #16419::You’re right that there is an issue with the
UIStatsDisplay
script. The issue is in line 58:foreach (FieldInfo field in fields) { // Render stat names. names.AppendLine(field.Name);
// Get the stat value. object val = field.GetValue(player.Stats); float fval = val is int ? (int)val : (float)val; // Print it as a percentage if it has a Range or Min attribute assigned and is a float. <mark>PropertyAttribute attribute = (PropertyAttribute)PropertyAttribute.GetCustomAttribute(field, typeof(PropertyAttribute));</mark> if (attribute != null && field.FieldType == typeof(float)) { float percentage = Mathf.Round(fval * 100 - 100); // If the stat value is 0, just put a dash. if (Mathf.Approximately(percentage, 0)) { values.Append('-').Append('\n'); } else { if (percentage > 0) values.Append('+'); values.Append(percentage).Append('%').Append('\n'); } } else { values.Append(fval).Append('\n'); } // Updates the fields with the strings we built. statNames.text = PrettifyNames(names); statValues.text = values.ToString();
}
Specifically, we need to change this line:
PropertyAttribute attribute = (PropertyAttribute)PropertyAttribute.GetCustomAttribute(field, typeof(PropertyAttribute));
To this line:
PropertyAttribute attribute = (PropertyAttribute)field.GetCustomAttribute<RangeAttribute>() ?? field.GetCustomAttribute<MinAttribute>();
Previously, when you added the [Tooltip] attribute, this caused the
PropertyAttribute.GetCustomAttribute()
method to break because there were multiple attributes, and this function is coded to throw an error if there are multiple attributes.field.GetCustomAttribute()
doesn’t cause the same error, because it works likeGetComponent()
and only picks out relevant attributes, instead of the first attribute likePropertyAttribute.GetCustomAttribute()
does.Thanks for finding this bug! Here’s a badge for your account:
November 20, 2024 at 8:44 am #16435November 20, 2024 at 8:55 am #16438::Unfortunately, I got all excited, implemented it, and then both functions of “GetCustomAttribute()” threw an error.
They both show:
“Error (active) CS0411 The type arguments for method ‘CustomAttributeExtensions.GetCustomAttribute<T>(Assembly)’ cannot be inferred from the usage. Try specifying the type arguments explicitly. Assembly-CSharp G:\Unity Projects\Vampire Survivor – Space Clone\Assets\Scripts\UI\UIStatsDisplay.cs 58”November 20, 2024 at 10:15 am #16439 -
AuthorPosts
- You must be logged in to reply to this topic.
Advertisement below: