News › Forums › RAIN › Sample Projects, How To, and Code › Trapwire/Trigger system, read fields from entity
Tagged: Custom Action, custom aspect, trigger
This topic contains 7 replies, has 2 voices, and was last updated by binary 6 months, 1 week ago.
-
AuthorPosts
-
July 17, 2022 at 7:19 am #38636
Hi,
i could use some tips who to do a trapwire trigger system with Rain. Currently i do it simply with colliders, if an AI hits one, it reads some values from the other collider and adjusts based on those values.
But i’d like to have that in the behavior tree, so the idea is to have a very small sensor area and entity’s with values.
How can add fields to entity’s and read them via the sensor into the behavior tree (preferably without the global AI.WorkingMemory) ?July 17, 2022 at 1:50 pm #38639Well you can use a Custom Aspect combined with a Custom Sensor or Custom Action depending on what you want to do.
Since even the behavior tree works with the memory directly, it may be hard to avoid that specifically, but perhaps you can get it more within the behavior tree so all your logic is in the same place (I tend to try to do this as well).
So here is the code to do a custom aspect, I threw in a couple values just for example:
using RAIN.Entities.Aspects; using RAIN.Serialization; using UnityEngine; [RAINSerializableClass] public class MyCustomAspect : VisualAspect { [RAINSerializableField] private float _someFloat = 0.1f; [RAINSerializableField] private GameObject _someObject = null; public float SomeFloat { get { return _someFloat; } } public GameObject SomeObject { get { return _someObject; } } }
I inherited from VisualAspect to save some trouble of creating a whole new Aspect Type (built in there are visual and audio).
Here is a custom action I put together to detect and then read the values out:
using RAIN.Action; using RAIN.Entities.Aspects; using RAIN.Perception.Sensors; using RAIN.Representation; using System.Collections; using System.Collections.Generic; using UnityEngine; [RAINAction] public class ReadCustomAspect : RAINAction { // These will show up in the editor, with the expectation that you will put a variable // name in them to be filled (much like the target field in some of our built in nodes) public Expression SomeFloatVariable = new Expression(); public Expression SomeObjectVariable = new Expression(); public override ActionResult Execute(RAIN.Core.AI ai) { // The BEST match type will only return 1 aspect if it returns any IList<RAINAspect> tAspects = ai.Senses.Sense("MySensor", "MyCustomAspect", RAINSensor.MatchType.BEST); if (tAspects.Count == 0) return ActionResult.FAILURE; // If you want to use the values in the behavior tree you will have to do something like this if (SomeFloatVariable.IsValid) ai.WorkingMemory.SetItem<float>(SomeFloatVariable.VariableName, ((MyCustomAspect)tAspects[0]).SomeFloat); else ai.WorkingMemory.SetItem<float>("someFloatVariable", ((MyCustomAspect)tAspects[0]).SomeFloat); if (SomeObjectVariable.IsValid) ai.WorkingMemory.SetItem<GameObject>(SomeObjectVariable.VariableName, ((MyCustomAspect)tAspects[0]).SomeObject); else ai.WorkingMemory.SetItem<GameObject>("someObjectVariable", ((MyCustomAspect)tAspects[0]).SomeObject); // If you don't need them in the behavior tree but instead want to do something directly to the ai // you have a lot of options // Change my speed //ai.Motor.DefaultSpeed = ((MyCustomAspect)tAspects[0]).SomeFloat; // Switch bodies?! //ai.Body = ((MyCustomAspect)tAspects[0]).SomeObject; return ActionResult.SUCCESS; } }
If you have questions (or problems) let me know. Full disclosure, I only compiled these, didn’t actually try them out.
July 20, 2022 at 4:53 am #38654That’s perfect, thank you!
Is there a elegant solution to have those one shot? Like onTriggerEnter?
July 20, 2022 at 10:41 am #38655Not built in, you could probably do something like this though:
using RAIN.Action; using RAIN.Entities.Aspects; using RAIN.Perception.Sensors; using System.Collections; using System.Collections.Generic; [RAINAction] public class ReadCustomAspect : RAINAction { private MyCustomAspect _currentTrigger = null; public override void Start(RAIN.Core.AI ai) { base.Start(ai); _currentTrigger = null; } public override ActionResult Execute(RAIN.Core.AI ai) { // If we don't have a trigger, try and grab it if (_currentTrigger == null) { IList<RAINAspect> tAspects = ai.Senses.Sense("MySensor", "MyCustomAspect", RAINSensor.MatchType.BEST); if (tAspects.Count == 0) return ActionResult.FAILURE; _currentTrigger = (MyCustomAspect)tAspects[0]; // Any code happening here would be the same as OnTriggerEnter return ActionResult.RUNNING; } // If we can't see the trigger anymore, it means we exited else if (ai.Senses.IsDetected(_currentTrigger, _currentTrigger.AspectName) == null) { // Any code happening here would be the same as OnTriggerExit return ActionResult.SUCCESS; } else { // Any code happening here would be the same as OnTriggerStay return ActionResult.RUNNING; } } }
- This reply was modified 6 months, 2 weeks ago by Sigil. Reason: fixed code block
July 22, 2022 at 3:20 am #38706Thanks, i’ll give it a try.
July 23, 2022 at 4:26 am #38725Okay, its getting closer.
The thing is, the trigger can issue a stop command which leaves the sensor sitting on the aspect. And it should still be able to be triggered by other (moving) aspects. So in this situation, in the current version it acts not like OnTriggerEnter but like OnTriggerStay in the first block. The detected aspect should be ignored until !IsDetected.Best what i could come up with, was adding flag to the aspect that gets set on detection and then it will be ignored.
IList<RAINAspect> tAspects = ai.Senses.SenseAll(); if (tAspects.Count == 0) return ActionResult.FAILURE; currentTrigger = null; for (int i = 0; i < tAspects.Count; i++){ if(!((MyAspect)tAspects[i]).DidExecute){ currentTrigger = (MyAspect)tAspects[i]; break; } } if(currentTrigger == null){ return ActionResult.FAILURE; }else if ( currentTrigger != null){ currentTrigger.DidExecute = true; [...] } [...]
This is not only not nice, but i also cant figure out, how and where to reset the aspect to undetected (currentTrigger.DidExecute = false; ).
July 23, 2022 at 9:24 am #38726Ah, it looks like the code I originally posted isn’t very useful if you ever have more than one trigger at the same time. So I took that one and expanded it, this should get you closer to what you need if I understand the problem correctly. I renamed the class to be DetectTriggers, since that seems more appropriate.
using RAIN.Action; using RAIN.Entities.Aspects; using RAIN.Perception.Sensors; using System.Collections; using System.Collections.Generic; [RAINAction] public class DetectTriggers : RAINAction { private List<MyCustomAspect> _currentTriggers = new List<MyCustomAspect>(); public override void Start(RAIN.Core.AI ai) { base.Start(ai); _currentTriggers.Clear(); } public override ActionResult Execute(RAIN.Core.AI ai) { // Test our current triggers for (int i = _currentTriggers.Count - 1; i >= 0; i--) { // If the aspect is no longer detected, remove it if (ai.Senses.IsDetected(_currentTriggers[i], _currentTriggers[i].AspectName) == null) { // This is OnTriggerExit _currentTriggers.RemoveAt(i); } } // Now look for new ones (and possibly old ones too) IList<RAINAspect> tAspects = ai.Senses.Sense("MySensor", "MyCustomAspect", RAINSensor.MatchType.ALL); if (tAspects.Count == 0) return ActionResult.FAILURE; // Go through our aspects, and treat them like triggers for (int i = 0; i < tAspects.Count; i++) { MyCustomAspect tCustomAspect = (MyCustomAspect)tAspects[i]; // We already detected it if (_currentTriggers.Contains(tCustomAspect)) { // This would be OnTriggerStay now } // New trigger else { // This would be OnTriggerEnter now _currentTriggers.Add(tCustomAspect); } } // And we are guaranteed that we still have aspects at this point, so continue RUNNING return ActionResult.RUNNING; } }
You shouldn’t have to use DidExecute anymore using this, as anything already in your list has been executed, anything added to the list needs to be executed, and anything removed from the list can be executed on next detect. Does this sound like it will work?
July 29, 2022 at 2:45 am #38772Beautiful! Happy! Thank you!
-
AuthorPosts
You must be logged in to reply to this topic.