Is there a way to keep declared enum values based on their name, not their index in their array?

Lets say i have an enum set in a skill to be “armor” and it’s the second index of an enum aray, but later I add a new property like evade, and I add it in slot 2 for alphabetical reasons or because i’ve removed a previous entry and the order is now shuffled so armor is now in slot 3.

Currently if I do this, it offsets off all my values, the assets that I assigned to “armor” will have swapped to be “evade”.

Is there a way to get updated enums that have entries NOT shifted after the code compiles?

I’m thinking if this isn’t possible, i’ll just have to manually type the upgrade type in, and cycle through until it matches the enum string value, but I’m hoping there’s a way around this as it would keep the entire pipeline a lot cleaner.

Thanks in advance!

The real answer is not to use enums for anything that needs to be scalable. You should look to use scriptable objects instead. And by doing so, you can put any information into said scriptable object.

2 Likes

You can be explicit about the backing value of the enum:

public enum Thing {
    A = 15,
    B = 12,
    D = 2347563,
}

For serialized enums, it’s a good idea to always do this, so you can add or remove elements without breaking the meaning of serialized data.

3 Likes

Thanks for the reply. I tried making a scriptable object, but I ended up Using an enum on it because I wasn’t sure what else to do.

I added a new “test” entry in the enum and I got the same shifting.

7795263--984669--upload_2022-1-10_3-45-36.png

I don’t mean use a scriptable object to store the enum, I mean use scriptable objects instead of using an enum.

1 Like

This did the trick! Thanks so much. My naivety of coding is really showing through, this gamedev thing is such a journey and I lack a lot of core code understanding. I feel a little silly typing in pseudorandom numeric identifiers and I’m starting too think too hard of a system to make it so i can add new entries with #'s that don’t overlap, but for now just smashing the numpad a few times is working great.

I haven’t posted in this forum in forever, can’t believe how much help you guys have given and how fast. Thanks a ton. I’ll be sure to poke around here and help others from time to time to repay the favor.

Right, but I wasn’t sure what you meant. A scriptable object is a container that doesn’t allow for modifications through things like prefabs if I understand them correctly, I wasn’t sure what to replace the enum with that would serve the same purpose: an array from which i could choose a specifically named upgrade.

I don’t quite agree that the ScriptableObject approach necessarily is better here. It’d require quite a bit of editor coding to get right, and it’d be slower (though probably too marginally slower to make an actual difference).

The idea would be to have a “marker” ScriptableObject for each of your Bonus types. So instead of checking if the bonus is equal to eg. Bonus.Armor, you’d check if the bonus is the same object as the Armor.

That’d save you from reordering errors - since it’s direct references to objects, ordering doesn’t exist. It’d also allow you to attach metadata to the bonuses - you could for example have the description for a Bonus be on the Bonus instead of having to exist somewhere else.

The downside is that instead of a dropdown, you’d instead have to select the bonus from a list, or write the editor code to get the dropdown back. Things like asset bundles might get a bit harder to deal with due to objects getting copied, and it’s a bit more of a hassle to figure out what bonus used to be there when you find an object with a deleted bonus.

So there’s upsides and downsides to both approaches, but I don’t think the upside is large enough that you absolutely should do the switch.

1 Like

You can certainly modify the data of scriptable objects if you so choose. In the editor that data will persist, and in builds it will persist so long as its referenced in the scene, before resetting to default values once no longer referenced.

Think of them as code as assets. Whether that be a source of immutable data, or a temporary means to transfer data between two things that could do without a hard reference to one another, even across scene, because they live at the asset level.

If we’re talking scriptable objects as power ups, let me drop some quick code. FYI this was typed up very quickly to give you a general idea:

First you define a base class for the scriptable object powerups:

using UnityEngine;

public abstract class PowerupObjectBase : ScriptableObject
{
    #region Base Inspector Fields

    [SerializeField]
    protected string _powerupName;

    [SerializeField]
    protected string _powerupDescription;

    #endregion

    #region Base Properties

    public string PowerupName { get { return _powerupName; } }

    public string PowerupDescription { get { return _powerupDescription; } }

    #endregion

    #region Abstract Methods

    public abstract void PerformPowerup(GameObject entity); //we pass in a gameobject so as to have something to do the effect on

    #endregion
}

And say you want an armour/defence powerup, which is easy:

using UnityEngine;

[CreateAssetMenu(menuName = "Powerups/Defense Powerup")]
public class DefensePowerup : PowerupObjectBase
{
    #region Powerup Inspector Fields

    [SerializeField]
    private float _powerupDuration = 5f;

    [SerializeField, Range(0, 1)]
    private float _powerupStrength = 0.5f;

    [SerializeField]
    private GameObject _defenceEffectPrefab;

    #endregion

    #region Override Methods

    public override void PerformPowerup(GameObject entity)
    {
        // spawn effect around player and do other defense-y stuff
    }

    #endregion
}

And a pickup for a powerup can be as simple as:

using UnityEngine;

public class PowerupPickup : MonoBehaviour
{
    #region Inspector Fields

    [SerializeField]
    private PowerupObjectBase _pickupPowerupObject;

    #endregion

    #region Unity Callbacks
  
    private void Awake()
    {
        if (!_pickupPowerupObject)
        {
            enabled = false;
            //other error checking stuff here
        }
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.CompareTag("Player")) //doesn't have to be just for the player of course
        {
            _pickupPowerupObject.PerformPowerup(other.gameObject);
            Destroy(this.gameObject);
        }
    }

    #endregion
}

With this approach you can make as many damn powerups as you need and not worry about upsetting anything. The trick is to make each inherited powerup class more general (ergo, a defence powerup, a speed powerup), and design them in such a way that they can be distinct from one another (fields for different particle effects, and other visuals prefabs).

In this case the objects would be immutable, read only. For any mutable data about them, you can just wrapped them up in another class/object.

That said if you’re deep into working on your game, totally up to you whether you do the switch or not. You could easily design a new system in parallel to test it out first.

And lastly, what is perhaps a contentious point, get yourself an inspector addon such as Odin Inspector! It’s going to save you more money in time than the initial payment.

1 Like

Ok, i think I understand what you guys are getting at, and I appreciate the quickly whipped up code samples that are better planned than what I labor over for hours. The idea would be that since each upgrade is unique, and has its own code to do its unique upgrade thing, I could just compare the actual object since each would be unique to the upgrade and the array would be all the “upgrades” that are code assets all put together in the array.

This makes a lot of sense and if i were to do this all again maybe i’d go this direction! My setup is mostly working so I’m not about to overhaul it, but these are good concepts to keep in mind for the future. There were a lot of other technical issues that governed why I set up things up as I did but my brain melts when I try to think of all of them ranging from easily accessing them from the player to being able to easily work on them all in one place in a single script.

One of the big issues I had was trying to get an external powerup or purchase to incriment an upgrade level by 1. I wasn’t sure how to get a script to incriment player.speed with player.upgradeName where speed is said upgrade. I ended up using playerprefs and that’s where the enums come in, they use the string to assign its level and it cycles through the list and grabs the value from the playerprefs and assigns it to itself. There’s probably an easier way to do this but this was the only solution I was able to finagle, and it’s working now, so no point fixing what isn’t broken, but i’m curious if there’s a more standard way of doing this. You’ve both been really helpful and my issue is sorted, this would just be icing on the cake solving another big issue that I feel as though I solved, but solved in a really dumb way.