Comments (5)
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.
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:
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.
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.
The CompareDistance Evaluate method is called every Tick. Great. But the result does not seem to have any impact.
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.
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.
Thank you very much. I appreciate it a lot!
from schema.
Related Issues (13)
- Documentation is not clear on Conditional aborts HOT 1
- Connections and some nodes are gone after update to commit 15b26ec4829f64418e47cd99410415604fe16ac7 HOT 3
- BlackboardEntry references are gone after adding an entry to Blackboard HOT 9
- RotateTowardsVector resets references when I hit play HOT 1
- Conditional is evaluated even if corresponding node is not running HOT 3
- I get this error when searching for components. HOT 5
- Missing OnNodeExit() Call on conditional node abort HOT 3
- entryType is NULL HOT 5
- OverlapSphere does not check against Internal Types HOT 2
- Node execution in parallel
- `BlackboardEntrySelector` cannot currently be used in a Modifer
- Conditionals do not abort lower priority nodes on failure HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from schema.