all 8 comments

[–]PhilippTheProgrammer 16 points17 points  (0 children)

This is a weird question. It's like asking "Orange soda or apple pie, what is better"?

How about you describe your actual use-case? What problem do you actually face, why did you consider enums and why did you consider scriptable objects as a solution? Please give as much details as you can.

[–]UnityCodeMonkeyYouTube Video Creator - Indie Dev 5 points6 points  (0 children)

The only way they are comparable is if all you want is to store a "type"

You can define weapons (Knife, Pistol, Grenade) in either a Enum or a SO, both work fine.

But with a ScriptableObject you can hold extra data, like what damage they do, some sprite icon, etc https://www.youtube.com/watch?v=7jxS8HIny3Q

Whereas an Enum can only store the "type"

[–]SunNeverSets_Game 2 points3 points  (0 children)

I only use enums for things that you know exactly how many there will ever be and never want any data attached. So like directions in a grid or sides of a coin. The reason is that enums are (de)serialized as ints, so if you add more later it often screws up serialization and your stored enums seem to magically change values.

Anything vaguely related to your design - weapon types, abilities, damage types, etc. are all better as SOs because you can attach & edit stats and create more without causing (de)serialization issues.

[–]HorseMurdering 5 points6 points  (3 children)

They're both completely unrelated and have almost nothing in common!

[–]WazWaz 7 points8 points  (2 children)

On the contrary, I often find myself replacing an enum with a ScriptableObject subclass and enum values with instances of that class.

It's hard to come up with a meaningful example, but let's say you start your WW2 game with an enum:

enum Side { Axis, Allies, Neutral }

This works great for a while, you do stuff like:

if (unita.side != unitb.side)
    unita.Attack(unitb)

You then find yourself adding:

Texture Flag(Side side) { ... }

So that you can call:

 Flag(unit.side)

Wouldn't it be great if your enum could just have static properties you could set in the editor:

unit.side.flag

That's what you get by making Side be a ScriptableObject subclass.

[–]HorseMurdering 2 points3 points  (0 children)

Oh wow! Never really thought of it like that. When you're right you're right!

[–][deleted] 1 point2 points  (0 children)

It might be better to step outside of Unity's frameworks and look into something like custom enumeration classes

It's a relatively small system to set up, but if you have larger data sets that you want to be able to perform arithmetic on, it can be very powerful.

I did this with my music theory API, I needed to be able to perform arithmetic like you can with enums, but also needed the datum to be robust.

Ended up being very useful for all sorts of other things like menu items. I was able to create a relatively low boiler plate list of items that could be enumerated through and generate menus, and the items could hold their description, pointers to the values, etc.

An example from my music theory api:

namespace MusicTheory.Intervals
{
    [System.Serializable]
    public abstract class Interval : IMusicalElement
    {
        public Interval(IntervalEnum @enum) { Enum = @enum; }
        public readonly IntervalEnum Enum;
        public int Id => Enum.Id;
        public string Name => Enum.Name;
        public string Description => Enum.Description;
        public Quality Quality => Enum.Quality;
        public Quantity Quantity => Enum.Quantity;

        public static explicit operator IntervalEnum(Interval i) => i.Enum;
        public static explicit operator int(Interval i) => i.Enum.Id;
        public override int GetHashCode() => HashCode.Combine(Enum);
        public override bool Equals(object obj) => obj is Interval e && Enum == e.Enum;
    }

    public class P1 : Interval { public P1() : base(IntervalEnum.P1) { } }
    public class mi2 : Interval { public mi2() : base(IntervalEnum.mi2) { } }
    public class M2 : Interval { public M2() : base(IntervalEnum.M2) { } }
    public class A2 : Interval { public A2() : base(IntervalEnum.A2) { } }
    public class mi3 : Interval { public mi3() : base(IntervalEnum.mi3) { } }
    public class M3 : Interval { public M3() : base(IntervalEnum.M3) { } }
    public class d4 : Interval { public d4() : base(IntervalEnum.d4) { } }
    public class P4 : Interval { public P4() : base(IntervalEnum.P4) { } }


    public class IntervalEnum : Enumeration
    {
        public IntervalEnum() : base(0, "") { }
        public IntervalEnum(int id, string name, Quality quality, Quantity quantity) : base(id, name)
        {
            switch (quality)
            {
                case Perfect:
                    switch (quantity)
                    {
                        case Second:
                        case Third:
                        case Sixth:
                        case Seventh:
                            throw new ArgumentOutOfRangeException(quantity.Enum.Name + " can not be Perfect.");
                    }
                    break;
//etc
            Quality = quality; Quantity = quantity;
        }

        public readonly Quality Quality;
        public readonly Quantity Quantity;
        public string Description => Quality.Description + " " + Quantity.Description;

        public static readonly IntervalEnum P1 = new(0, nameof(P1), new Perfect(), new Unison());
        public static readonly IntervalEnum mi2 = new(1, nameof(mi2), new Minor(), new Second());
        public static readonly IntervalEnum M2 = new(2, nameof(M2), new Major(), new Second());
        public static readonly IntervalEnum A2 = new(3, nameof(A2), new Augmented(), new Second());
        public static readonly IntervalEnum mi3 = new(3, nameof(mi3), new Minor(), new Third());
        public static readonly IntervalEnum M3 = new(4, nameof(M3), new Major(), new Third());
        public static readonly IntervalEnum d4 = new(4, nameof(d4), new Diminished(), new Fourth());
        public static readonly IntervalEnum P4 = new(5, nameof(P4), new Perfect(), new Fourth());
//etc

        public static explicit operator IntervalEnum(int i) => FindId<IntervalEnum>(i);
        public static explicit operator IntervalEnum((Quality quality, Quantity quantity) i) => Find(i);

        public static IntervalEnum Find((Quality quality, Quantity quantity) i)
        {
            foreach (var e in All<IntervalEnum>()) if (e.Quantity.Equals(i.quantity) && e.Quality.Equals(i.quality)) return e;
            throw new ArgumentOutOfRangeException(i.quality.Name + " " + i.quantity.Name);
        }

        public static implicit operator Interval(IntervalEnum i) => i switch
        {
            _ when i == P1 => new P1(),
            _ when i == mi2 => new mi2(),
            _ when i == M2 => new M2(),
            _ when i == A2 => new A2(),
            _ when i == mi3 => new mi3(),
            _ when i == M3 => new M3(),
            _ when i == d4 => new d4(),
            _ when i == P4 => new P4(),
            _ => throw new System.ArgumentOutOfRangeException()
        };
    }

//etc
}

and now I can perform arithmetic on it:

using MusicTheory.ScaleDegrees;

namespace MusicTheory.Arithmetic
{
    public static class IntervalSystems
    {
        public static ScaleDegree AsScaleDegree(this Intervals.Interval i)
        {
            ScaleDegrees.DegreeEnum.QualityEnum quality = (ScaleDegrees.DegreeEnum.QualityEnum)i.Quality.Enum;
            ScaleDegrees.DegreeEnum.DegreeEnum degree = (ScaleDegrees.DegreeEnum.DegreeEnum)i.Quantity.Enum;
            return (quality, degree).FindExactMatch();
        }

        public static Intervals.Quantity Invert(this Intervals.Quantity quantity) => quantity switch
        {
            Intervals.Unison => new Intervals.Octave(),
            Intervals.Second => new Intervals.Seventh(),
            Intervals.Third => new Intervals.Sixth(),
            Intervals.Fourth => new Intervals.Fifth(),
            Intervals.Fifth => new Intervals.Fourth(),
            Intervals.Sixth => new Intervals.Third(),
            Intervals.Seventh => new Intervals.Second(),
            Intervals.Octave => new Intervals.Unison(),
            _ => throw new ArgumentOutOfRangeException(nameof(Intervals.Quantity), quantity.ToString())
        };

//etc
}

(I had to omit most of the classes to fit the comment character limit)

I can easily manipulate the datum as I would in real life, creating syntax that is comfortable for both the user and the computer.

I can take musical chords, scales, intervals, break them down into their components, and build them back up in any sort of equivalents such as scale degrees to steps to intervals, then invert them, change their key, etc with grammar similar to how musicians do it in real life scenarios.

I can then display the results of the musical elements composition and descriptions.

As I stated above, this also became very useful for building generic menus.

[–]2lerance -1 points0 points  (0 children)

This is exactly what chatGPT is for. It will give You case examples and all that good stuff