Let’s say my player character can have a lot of potential states, eg. standing, sneaking, walking, running, armed, unarmed, clean, bloody etc etc. If certain states are true then the player is considered suspicious and npcs can react to them. However the only way I can think of to do this is grouping the states into multiple enums and writing a long if statement to check if the player is suspicious, because some of the states can overlap (eg. the player can be armed and bloody at the same time). Is there a better way to do this?
Enums never really scale well. Ideally you do this in some kind of composed or data driven way. A data-driven way makes the most sense, I think. For example, your states express a collection of traits, and outside entities simply check for one or more of certain traits.
These could be defined in a variety of ways, scriptable objects being the most straight-forward.
I don’t get it, wdym composed or data-driven way? How am I supposed to use scriptable objects for this? Can you give an example
Composed, as in, being able to express one or more properties or behaviour on an object. This could be with C# interfaces, or simply a collection of objects usually of a common/base type but not always.
Data driven simply means using data to drive your logic, rather than writing your code to react to certain data.
Here’s some important videos to watch on the topic, particularly pertaining to scriptable objects:
You can easily compare Unity objects for equality. One entity can reference one or more scriptable objects via the inspector, and can simply check for these in the player’s current state.
Super cut down example:
[CreateAssetMenu]
public class StateTrait : ScriptableObject { }
public abstract class PlayerStateBase
{
[SerializeField]
private List<StateTrait> _stateTraits = new();
public bool HasStateTrait(StateTrait trait) => _stateTraits.Contains(_stateTraits);
}
// usage
bool isSuspicious = player.CurrentState.HasStateTrait(bloodyTrait);
Mind you this doesn’t actually need to be in the state themselves. This could be managed by a separate component, and the player’s current state could add/remove when entering/exiting them.
You’re trying to abstract all the actual data away, these are not states so much as they are properties.
A character can be standing and running at the same time or standing and walking, you don’t want to switch one state to the next for arbitrary properties like that, in my opinion cleanliness should be a float or some other value that is represented in a proper way.
//Consider making this a bitwise because you can have one or more of these states
public enum Movement
{
Standing,
Sneaking,
Walking,
Running
}
public enum Combat
{
Armed,
Unarmed
}
public enum Cleanliness
{
Clean,
Bloody
}
//or
public byte bloodAmount;
//or use bitwise to set more than one value,
[Flags]
public enum StatusEffect
{
None = 0,
Poisoned = 1 << 0,
Stunned = 1 << 1,
Burning = 1 << 2,
Frozen = 1 << 3,
Cursed = 1 << 4
}
My main point is you’re trying to lump “states” that don’t belong together together.
ngl I just started out, I don’t understand a single thing in this thread. Are you telling me scriptable objects are overkill and I should just use this bitwise thing instead or what?
They were responding to you not so much criticising my approach.
Though what you’re asking is stepping out of beginner stuff and into the more intermediate and advanced territory.
I wouldn’t consider this advanced at all, this is just basic properties and how to construct data.
OP SO (Scriptable objects) have their place in setting data, but what you’re asking is how to construct data. You’re mistaking states for properties; when a player can be armed and run at the same time those are not states that you switch between they are properties of your player object.
An enum will give you a single value you switch between, a bitwise will allow you to have one or many values. For things like blood values you should have a number value to represent it so you can add or remove textures. Even walking / running should be a velocity number as you want to be able to switch your animations by the value.
So treat your data accordingly, in terms of what you should do is learn everything we’ve gone over here since you need it anyways in your journey, and apply what works with you.
Ok…but what about checking if the player is suspicious?? How is either method supposed to help with that
By using a property, not a method.
public float suspicionValue;
a method would be
public void SetSuspicion(float f){
if((f + suspicionValue)) DoSomething();
I would suggest taking the time to go back on the basics of C# if this is new to you.
I mean I quite literally had a code example.
Again it boils down to just checking for certain data. Define what can constitute as suspicious in data, and look for that data.
Actually yknow what, never mind. Found a better working example online. Anyway thanks, I guess I’ll go with the scriptable object method since it seems more straightforward.