Forum begins after the advertisement:

 


[Part 24] Character select screen not working properly

Home Forums Video Game Tutorial Series Creating a Rogue-like Shoot-em Up in Unity [Part 24] Character select screen not working properly

Viewing 6 posts - 1 through 6 (of 6 total)
  • Author
    Posts
  • #18084
    Jukers
    Level 9
    Former Patron
    Helpful?
    Up
    0
    ::

    I just finished part 24 and i noticed some major errors on my project, tried to find the source of it but didnt get lucky. I managed to make the generation of the characters on screen with the button on Character Selector Background, but when i play the game and select (click on) the character, nothing happens (click on the character and nothing happens, even if i play the game, the default character is the one chosen) The default character works, its description and everything works, but i cant change the character ill leave my scripts here if it helps

    UIStatsDisplay.cs

    using System.Text;
    using System.Reflection;
    using UnityEngine;
    using TMPro;
    
    public class UIStatDisplay : MonoBehaviour
    {
    
        public PlayerStats player; // The player that this stat display is rendering stats for.
        public CharacterData character;
        public bool displayCurrentHealth = false;
        public bool updateInEditor = false;
        TextMeshProUGUI statNames, statValues;
    
        // Update this stat display whenever it is set to be active.
        void OnEnable()
        {
            UpdateStatFields();
        }
    
        void OnDrawGizmosSelected()
        {
            if(updateInEditor) UpdateStatFields();
        }
    
        public CharacterData.Stats GetDisplayedStats()
        {
            if(player) return player.Stats;
            else if(character) return character.stats;
            return new CharacterData.Stats();
        }
    
        public void UpdateStatFields()
        {
            if (!player && !character) return;
    
            // Get a reference to both Text objects to render stat names and stat values.
            if (!statNames) statNames = transform.GetChild(0).GetComponent<TextMeshProUGUI>();
            if (!statValues) statValues = transform.GetChild(1).GetComponent<TextMeshProUGUI>();
    
            // Render all stat names and values.
            // Use StringBuilders so that the string manipulation runs faster.
            StringBuilder names = new StringBuilder();
            StringBuilder values = new StringBuilder();
    
            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)
            {
                // Add the stat name.
                names.AppendLine(field.Name);
    
                // Get the stat value.
    
                object val = field.GetValue(GetDisplayedStats());
                float fval = val is int ? (int)val : (float)val;
    
                // Print it as a percentage if it has an attribute assigned and is a float.
                PropertyAttribute attribute = (PropertyAttribute)field.GetCustomAttribute<RangeAttribute>() ?? field.GetCustomAttribute<MinAttribute>();
                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();
            }
        }
    
        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();
        }
    
        void Reset()
        {
            player = FindObjectOfType<PlayerStats>();
        }
    }

    CharacterData.cs

    using UnityEngine;
    
    [CreateAssetMenu(fileName = "Character Data", menuName = "2D Top-down Rogue-like/Character Data")]
    public class CharacterData : ScriptableObject
    {
        [SerializeField]
        Sprite icon;
        public RuntimeAnimatorController animator;
        public Sprite Icon { get => icon; private set => icon = value; }
    
        [SerializeField]
        new string name;
        public string Name { get => name; private set => name = value; }
    
        [SerializeField]
        string fullName;
        public string FullName {get => fullName; private set => fullName = value;}
    
        [SerializeField]
        [TextArea]
        string characterDescription;
        public string CharacterDescription { get => characterDescription; private set => characterDescription = value; }
    
    
        [SerializeField]
        WeaponData startingWeapon;
        public WeaponData StartingWeapon { get => startingWeapon; private set => startingWeapon = value; }
    
        [System.Serializable]
        public struct Stats
        {
            public float maxHealth, recovery, armor;
            [Range(-1, 10)] public float moveSpeed, might, area;
            [Range(-1, 5)] public float speed, duration;
            [Range(-1, 10)] public int amount;
            [Range(-1, 1)] public float cooldown;
            [Range(-1, 10)] public float expGain;
            [Min(-1)] public float luck, growth, greed, curse;
            public float magnet;
            public int revival;
    
            public static Stats operator +(Stats s1, Stats s2)
            {
                s1.maxHealth += s2.maxHealth;
                s1.recovery += s2.recovery;
                s1.armor += s2.armor;
                s1.moveSpeed += s2.moveSpeed;
                s1.might += s2.might;
                s1.area += s2.area;
                s1.speed += s2.speed;
                s1.duration += s2.duration;
                s1.amount += s2.amount;
                s1.cooldown += s2.cooldown;
                s1.expGain += s2.expGain;
                s1.luck += s2.luck;
                s1.growth += s2.growth;
                s1.greed += s2.greed;
                s1.curse += s2.curse;
                s1.magnet += s2.magnet;
                return s1;
            }
            public static Stats operator *(Stats s1, Stats s2)
            {
                s1.maxHealth *= s2.maxHealth;
                s1.recovery *= s2.recovery;
                s1.armor *= s2.armor;
                s1.moveSpeed *= s2.moveSpeed;
                s1.might *= s2.might;
                s1.area *= s2.area;
                s1.speed *= s2.speed;
                s1.duration *= s2.duration;
                s1.amount *= s2.amount;
                s1.cooldown *= s2.cooldown;
                s1.luck *= s2.luck;
                s1.growth *= s2.growth;
                s1.greed *= s2.greed;
                s1.curse *= s2.curse;
                s1.magnet *= s2.magnet;
                return s1;
            }
        }
        public Stats stats = new Stats
        {
            maxHealth = 100, moveSpeed = 1, might = 1, amount = 0,
            area = 1, speed = 1, duration = 1, cooldown = 1, expGain = 1,
            luck = 1, greed = 1, growth = 1, curse = 1
        };
    }

    UICharacterSelector.cs

    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    using UnityEditor;
    using TMPro;
    
    public class UICharacterSelector : MonoBehaviour
    {
        public CharacterData defaultCharacter;
        public static CharacterData selected;
        public UIStatDisplay statsUI;
    
        [Header("Template")]
        public Toggle toggleTemplate;
        public string characterNamePath = "Charater Name";
        public string weaponIconPath = "Weapon Icon";
        public string characterIconPath = "Character Icon";
        public List<Toggle> selectableToggles = new List<Toggle>();
    
        [Header("DescriptionBox")]
        public TextMeshProUGUI characterFullName;
        public TextMeshProUGUI characterDescription;
        public Image selectedCharacterIcon;
        public Image selectedCharacterWeapon;
    
        void Start()
        {
            if(defaultCharacter) Select(defaultCharacter);
        }  
    
        //public CharacterData characterData;
    
        public static CharacterData[] GetAllCharacterDataAssets()
        {
            List<CharacterData> characters = new List<CharacterData>();
    
                //ramddomly pick a character if we are playing from the Game Scene
    #if UNITY_EDITOR
                string[] allAssetPaths = AssetDatabase.GetAllAssetPaths();
                foreach (string assetPath in allAssetPaths)
                {
                    if (assetPath.EndsWith(".asset"))
                    {
                        CharacterData characterData = AssetDatabase.LoadAssetAtPath<CharacterData>(assetPath);
                        if (characterData != null)
                        {
                            characters.Add(characterData);
                        }
                    }
                }
    #else
            Debug.LogWarning("This function cannot be called on builds.");
    
    #endif  
            return characters.ToArray();
        }
    
        public static CharacterData GetData()
        {
            //use the selected chosen in the Select() function
            if(selected)
            return selected;
    
            else
            {
                //randomly pick a character if we are playing from the editor
                CharacterData[] characters = GetAllCharacterDataAssets();
                if(characters.Length > 0) return characters[Random.Range(0, characters.Length)];
            }
            return null;
        }
    
        public void Select(CharacterData character)
        {
            Debug.Log($"Character selected: {character.FullName}");
            //update stat fields in character select screen
            if (statsUI == null)
            {
                Debug.LogError("statsUI is not assigned in the UICharacterSelector.");
                return;
            }
    
            if (characterFullName == null) Debug.LogError("characterFullName is not assigned.");
            if (characterDescription == null) Debug.LogError("characterDescription is not assigned.");
            if (selectedCharacterIcon == null) Debug.LogError("selectedCharacterIcon is not assigned.");
            if (selectedCharacterWeapon == null) Debug.LogError("selectedCharacterWeapon is not assigned.");
            selected = statsUI.character = character;
            statsUI.UpdateStatFields();
    
            characterFullName.text = character.FullName;
            characterDescription.text = character.CharacterDescription;
            selectedCharacterIcon.sprite = character.Icon;
            selectedCharacterWeapon.sprite = character.StartingWeapon.icon;
        }
    
    }

    Maybe i missed something on the inspector? Re-watched the video but got no success

    #18116
    Terence
    Level 31
    Keymaster
    Helpful?
    Up
    0
    ::

    Can you add the highlighted portion to your code and see if it produces the highlighted message in the Console when you select a character?

    public void Select(CharacterData character)
    {
        Debug.Log($"Character selected: {character.FullName}");
        //update stat fields in character select screen
        if (statsUI == null)
        {
            Debug.LogError("statsUI is not assigned in the UICharacterSelector.");
            return;
        }
    
        if (characterFullName == null) Debug.LogError("characterFullName is not assigned.");
        if (characterDescription == null) Debug.LogError("characterDescription is not assigned.");
        if (selectedCharacterIcon == null) Debug.LogError("selectedCharacterIcon is not assigned.");
        if (selectedCharacterWeapon == null) Debug.LogError("selectedCharacterWeapon is not assigned.");
        selected = statsUI.character = character;
        statsUI.UpdateStatFields();
    
        characterFullName.text = character.FullName;
        characterDescription.text = character.CharacterDescription;
        selectedCharacterIcon.sprite = character.Icon;
        selectedCharacterWeapon.sprite = character.StartingWeapon.icon;
        Debug.Log($"Current selected on UICharacterSelector: {selected}");
    }
    #18125
    Jukers
    Level 9
    Former Patron
    Helpful?
    Up
    0
    ::

    Added it and it doenst appear on the console, only the first log that i have, as i highlighted bellow

        public void Select(CharacterData character)
        {
            Debug.Log($"Character selected: {character.FullName}");
            //update stat fields in character select screen
            if (statsUI == null)
            {
                Debug.LogError("statsUI is not assigned in the UICharacterSelector.");
                return;
            }
    
            if (characterFullName == null) Debug.LogError("characterFullName is not assigned.");
            if (characterDescription == null) Debug.LogError("characterDescription is not assigned.");
            if (selectedCharacterIcon == null) Debug.LogError("selectedCharacterIcon is not assigned.");
            if (selectedCharacterWeapon == null) Debug.LogError("selectedCharacterWeapon is not assigned.");
            selected = statsUI.character = character;
            statsUI.UpdateStatFields();
    
            characterFullName.text = character.FullName;
            characterDescription.text = character.CharacterDescription;
            selectedCharacterIcon.sprite = character.Icon;
            selectedCharacterWeapon.sprite = character.StartingWeapon.icon;
            Debug.Log($"Current selected on UICharacterSelector: {selected}");
        }
    #18133
    Terence
    Level 31
    Keymaster
    Helpful?
    Up
    0
    ::

    That means something is stopping the function from executing entirely, so your selected variable is never assigned.

    Are any of the highlighted lines below appearing in your Console as well?

    public void Select(CharacterData character)
    {
        Debug.Log($"Character selected: {character.FullName}");
        //update stat fields in character select screen
        if (statsUI == null)
        {
            Debug.LogError("statsUI is not assigned in the UICharacterSelector.");
            return;
        }
    
        if (characterFullName == null) Debug.LogError("characterFullName is not assigned.");
        if (characterDescription == null) Debug.LogError("characterDescription is not assigned.");
        if (selectedCharacterIcon == null) Debug.LogError("selectedCharacterIcon is not assigned.");
        if (selectedCharacterWeapon == null) Debug.LogError("selectedCharacterWeapon is not assigned.");
        selected = statsUI.character = character;
        statsUI.UpdateStatFields();
    
        characterFullName.text = character.FullName;
        characterDescription.text = character.CharacterDescription;
        selectedCharacterIcon.sprite = character.Icon;
        selectedCharacterWeapon.sprite = character.StartingWeapon.icon;
        Debug.Log($"Current selected on UICharacterSelector: {selected}");
    }
    #18134
    Jukers
    Level 9
    Former Patron
    Helpful?
    Up
    0
    ::

    Not really, i only get this error

    NullReferenceException: Object reference not set to an instance of an object UIStatDisplay.UpdateStatFields () (at Assets/Scripts/UI/UIStatsDisplay.cs:49) UICharacterSelector.Select (CharacterData character) (at Assets/Scripts/UICharacterSelector.cs:88) UICharacterSelector.Start () (at Assets/Scripts/UICharacterSelector.cs:28)

    that points to UIStatsDisplay.cs UpdateStatFields Function

        public void UpdateStatFields()
        {
            if (!player && !character) return;
    
            // Get a reference to both Text objects to render stat names and stat values.
            if (!statNames) statNames = transform.GetChild(0).GetComponent<TextMeshProUGUI>();
            if (!statValues) statValues = transform.GetChild(1).GetComponent<TextMeshProUGUI>();
    
            // Render all stat names and values.
            // Use StringBuilders so that the string manipulation runs faster.
            StringBuilder names = new StringBuilder();
            StringBuilder values = new StringBuilder();
    
            if(displayCurrentHealth)
            {
                names.AppendLine("Health");
                values.AppendLine(player.CurrentHealth.ToString()); //This line
            }
    
            FieldInfo[] fields = typeof(CharacterData.Stats).GetFields(BindingFlags.Public | BindingFlags.Instance);
            foreach (FieldInfo field in fields)
            {
                // Add the stat name.
                names.AppendLine(field.Name);
    
                // Get the stat value.
    
                object val = field.GetValue(GetDisplayedStats());
                float fval = val is int ? (int)val : (float)val;
    
                // Print it as a percentage if it has an attribute assigned and is a float.
                PropertyAttribute attribute = (PropertyAttribute)field.GetCustomAttribute<RangeAttribute>() ?? field.GetCustomAttribute<MinAttribute>();
                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();
            }
        }

    Which i don’t think it relates to the malfunctioning i am experiencing

    Also, the default character is not setted up as it should be when entering the select character screen (the values are the example values i made when creating the screens) When i created the characters in the editor (UI Character Selector button to generate characters) the template changed to the first character created, this is not shown in the video, so i don’t know if it is the expected behavior

    #18135
    Jukers
    Level 9
    Former Patron
    Helpful?
    Up
    0
    ::

    Update: I forgot to remove the “Display Current Health” on the UIStatsDisplay of the character selector, and the error on the console doesn’t appear anymore.

    and this Log appears and updates the description box (set the description box and UIStats to the default character) but when i click a character it doesn’t update the info neither selects the character (when i enter the game, the character seems to be random) Log Below:

        public void Select(CharacterData character)
        {
            Debug.Log($"Character selected: {character.FullName}");
            //update stat fields in character select screen
            if (statsUI == null)
            {
                Debug.LogError("statsUI is not assigned in the UICharacterSelector.");
                return;
            }
    
            if (characterFullName == null) Debug.LogError("characterFullName is not assigned.");
            if (characterDescription == null) Debug.LogError("characterDescription is not assigned.");
            if (selectedCharacterIcon == null) Debug.LogError("selectedCharacterIcon is not assigned.");
            if (selectedCharacterWeapon == null) Debug.LogError("selectedCharacterWeapon is not assigned.");
            selected = statsUI.character = character;
            statsUI.UpdateStatFields();
    
            characterFullName.text = character.FullName;
            characterDescription.text = character.CharacterDescription;
            selectedCharacterIcon.sprite = character.Icon;
            selectedCharacterWeapon.sprite = character.StartingWeapon.icon;
            Debug.Log($"Current selected on UICharacterSelector: {selected}");
        }
    
    }
Viewing 6 posts - 1 through 6 (of 6 total)
  • You must be logged in to reply to this topic.

Go to Login Page →


Advertisement below: