Giter Club home page Giter Club logo

Comments (5)

acdamiani avatar acdamiani commented on July 24, 2024

Hey there! I appreciate the in depth explanation of your problem.

The problem you are describing is solved by Conditional Aborts. Conditional nodes should run on NodeStatus.Running nodes if the "Aborts Type" property on the conditional is set to "Self" or "Both". Basically, setting the conditional to abort itself means that any node in the subtree of the node attached to the conditional will be exited when the conditional evaluates to a value described by the "Aborts When" property. I can explain this more in depth should you need it, but for the sake of brevity I'll skip it for now. The engine marks these conditionals as those needed to be evaluated every Tick regardless of the node currently running. (Of course, this is how it should work. It's not unlikely that I've screwed up somewhere; I haven't tested this before writing out this explanation)

You can see an example of this in the Example tree included in the project. The enemy AI is set to "wander" throughout the map using a NavMesh. Upon seeing the player, the tree immediately pulls execution to the subtree designed to "chase" the player throughout the map. Note that this conditional will trigger even when the enemy is en route to a destination, thereby demonstrating the interrupt behavior you are describing. It's on my list to improve the documentation and provide a more in depth explanation of this at some point as soon as I have more free time.

The error on the LoopUntil modifier is a bug. I'll patch it out when I get the chance.

Let me know if you have any other questions!

from schema.

ijisthee avatar ijisthee commented on July 24, 2024

Okay, it works. And it works well. I've found my mistake and it does not seem to be possible with the actual logic to handle that.

EDIT: It does not work. It is called but it does not have an impact.

Look at this:

image
image

It only works, when the distance is updated before the conditional is checked. Since the DistanceToPlayer Node does not run as long as the Wander Node is in status Running, the distance is not updated and the check fails. It has the last distance before the Wander Node has started.

image

I guess the only way around that is a custom CompareDistance Conditional that takes two transforms and checks the distance and decides to fail or succeed based on the given parameters.

Or maybe you have another solution on that?

Thank you very much.

from schema.

ijisthee avatar ijisthee commented on July 24, 2024

The CompareDistance Evaluate method is called every Tick. Great. But the result does not seem to have any impact.

Look at this picture:
image

And please have a look at the code:

see CompareDistance class
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;

namespace Schema.Builtin.Conditionals
{
    [DarkIcon("Conditionals/d_Compare")]
    [LightIcon("Conditionals/Compare")]
    public class CompareDistance : Conditional
    {
        public enum ComparisonType
        {
            Equal,
            GreaterThan,
            GreaterThanOrEqual,
            LessThan,
            LessThanOrEqual
        }
    
        [Tooltip("LHS Vector 1 for the Distance check")] public BlackboardEntrySelector<Vector3> firstVector;
        [Tooltip("LHS Vector 2 for the distance check")] public BlackboardEntrySelector<Vector3> secondVector;
        [Tooltip("RHS of the comparison")] public BlackboardEntrySelector<float> distanceToCheck;
    
        [Tooltip("The comparison type for this operation")]
        public ComparisonType comparisonType;
    
        public override bool Evaluate(object decoratorMemory, SchemaAgent agent)
        {
            float distance = Vector3.Distance(firstVector.value, secondVector.value);
    
            switch (comparisonType)
            {
                case ComparisonType.Equal:
                    Debug.Log("distance: " + distance + " " + comparisonType + " distanceToCheck:" + distanceToCheck.value + "; result: " + (distance == distanceToCheck.value));
                    return distance == distanceToCheck.value;
                case ComparisonType.GreaterThan:
                    Debug.Log("distance: " + distance + " " + comparisonType + " distanceToCheck:" + distanceToCheck.value + "; result: " + (distance > distanceToCheck.value));
                    return distance > distanceToCheck.value;
                case ComparisonType.GreaterThanOrEqual:
                    Debug.Log("distance: " + distance + " " + comparisonType + " distanceToCheck:" + distanceToCheck.value + "; result: " + (distance >= distanceToCheck.value));
                    return distance >= distanceToCheck.value;
                case ComparisonType.LessThan:
                    Debug.Log("distance: " + distance + " " + comparisonType + " distanceToCheck:" + distanceToCheck.value + "; result: " + (distance < distanceToCheck.value));
                    return distance < distanceToCheck.value;
                case ComparisonType.LessThanOrEqual:
                    Debug.Log("distance: " + distance + " " + comparisonType + " distanceToCheck:" + distanceToCheck.value + "; result: " + (distance <= distanceToCheck.value));
                    return distance <= distanceToCheck.value;
            }
    
            return false;
        }
    
        public override GUIContent GetConditionalContent()
        {
            StringBuilder sb = new StringBuilder();
    
            sb.AppendFormat("If distance (<color=red>{0},{1}</color>) is ", firstVector.name, secondVector.name);
    
            string cName = Regex.Replace(comparisonType.ToString(), "(\\B[A-Z])", " $1").ToLower();
    
            switch (comparisonType)
            {
                case ComparisonType.Equal:
                case ComparisonType.LessThanOrEqual:
                case ComparisonType.GreaterThanOrEqual:
                    sb.AppendFormat("{0} to ", cName);
                    break;
                case ComparisonType.GreaterThan:
                case ComparisonType.LessThan:
                    sb.AppendFormat("{0} ", cName);
                    break;
            }
    
            sb.AppendFormat("<color=red>{0}</color>", distanceToCheck.name);
    
            return new GUIContent(sb.ToString());
        }
    }
}

As you can see, it works like your CompareTo code with some small adaptions.

I have digged a bit deeper and found some possible reasons for that. However I don't understand the algorithm completely.

In ExecutableNode.RunDynamicConditionals the status from bool status = c.Evaluate(conditionalMemory[id][j], context.agent); (line 187) is never the same status like the last status that is checked here (lines 198-202):

if (status != last
    && ((status && abortOnSuccess) || (!status && abortOnFailure))
    && ((isSubAbort && isSub) || (isPriorityAbort && isPriority))
   )
    return true;

Same with ExecutableNode.DoAction (lines 335-345):

if (context.last.index != index)
    for (int j = 0; j < action.conditionals.Length; j++)
    {
        run = action.conditionals[j].Evaluate(conditionalMemory[id][j], context.agent);
        run = action.conditionals[j].invert ? !run : run;

        lastConditionalStatus[j] = run;

        if (!run)
            break;
    }

This code also does not run since last context index is same like index.

I hope it helps to provide a fix or at least an explanation. :)

Thx again

from schema.

acdamiani avatar acdamiani commented on July 24, 2024

Thanks again for looking into this! Yes, this seems like a bug; I've managed to reproduce it on my system. I'll work on a fix this weekend. To make sure this thread doesn't get too crowded, I've separated each concern detailed here into other issues: see #7, #8, #9, and #10.

To your question about custom conditional logic: yes, creating custom conditionals is the best solution right now since the engine does not allow for concurrent execution. It's something I could look into for a future release. Feel free to reach out if you notice anything else. I'll work on getting these problems fixed in the coming days.

from schema.

ijisthee avatar ijisthee commented on July 24, 2024

Thank you very much. I appreciate it a lot!

from schema.

Related Issues (13)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.