As I uderstand, to prevent accidental switching of actions, carefully crafted scoring system is used.
Scoring system doesn’t matter, that’s still hysteresis. You have to bias (implement inertia of some kind) and lock to prevent switching into new actions, not craft scoring - that would be an endless task. An Action should be locking itself as current until it is complete, an action sufficiently surpasses it for suitability, or aggregate emergency Criteria have been met.
Inertia hystereris handling at line 32, emergency exits are elsewhere, but that’s pretty trite.
When one selected to ‘go north’ he will not accidentally go south because it is implemented as on/off switch
so another decision is required to do this, is it correct?
That doesn’t even sound related, that’s just bad Action design - I think you’re thinking too micro, North/South don’t matter it’s “Where do I have to go?” -> “Do I want to go there?” -> “I have a path, now I will follow it”. North/South is neural net thinking. The door problem you state is the same deal.
I see that the scoring (ranking) system looks a lot like decision/behavior tree and could be represented by
a tree, I wonder if anybody did that… Or probably I did not understand the concept.
If your “utility” setup isn’t at least sort of similar (or conceptually mappable) to the pseudocode below, then you probably don’t get it (note this is raw, and not “fact” just if it doesn’t come as “well yeah” or “duh” then you don’t get it):
struct Criteria {
ResponseCurve weightCurve_;
virtual float GetNormalizedValue() = 0;
float GetCriteriaWeight();
}
struct Action {
Criteria[] selectionCriteria;
float GetWeight(float weightToBeat, ...);
}
struct ActionSet {
int tag_;
Action[] actions_;
ActionResult GetBestAction(float weightToBeat, ...);
}
Criteria is the fumbling point usually. Getting that part right is useful because it can be reused by pretty much anything, such as being used for predicates in decision/behaviour trees, FSMs, ID3 code-generators, etc. Minimalist “Distance From” Criteria from my Unity implementation (C++ implementation is nasty).
Or if the target is chair, sit on it.
Your objective is to sit: you find an unused chair to sit on, you go to the chair, and you sit on it … and basically all of your ActionSets and Actions become invalid except the one to leave the chair (or play the flute, sitting down - whatever madness occurs while seated).
But walk to target is the same for both cases.
That’s not an action, that something an action may do to fulfill dependencies. MeleeAttack must walk into range to even complete, that’s a fundamental part of the action, once in range it can perform it’s final execution. It’s one action, “I’m going to walk up to him and bash him on the head.”
How to implement such choices with Utility AI so that the sequience is not broken in illogical way?
You don’t. That’s not what it’s for. The action may be an atomic action, or may be a complete sequence. You’re thinking like neural-nets/decision-trees. Utility was for selecting an action, everything about how long to continue with that action is outside of it, you could just lock on any single action until it is complete - an Action may be as trite as shooting a target or as complicated as walking across half of a dungeon to activate a shrine.
Switching actions is only for responsiveness to environmental changes, not a matter of sequence - though it could be if you wrote it that way … that’d be “fun” to debug.