NodeCanvas Forums › General Discussion › NodeCanvas and Factory & Observer patterns › Reply To: NodeCanvas and Factory & Observer patterns
Hello and thanks a lot 🙂
NodeCanvas is really focused on working on top of your design and there are various ways you can go about using the observer pattern.
Most commonly you will want to use an event as a condition task within the behaviour tree instead of an action. So lets suppose that you have this example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class Player : MonoBehaviour{ public delegate void DamageEventHandler(float damage); public event DamageEventHandler DamageDealtToPlayer; public void ReceiveDamage(float damage){ //some code... if (DamageDealtToPlayer != null) DamageDealtToPlayer(damage); } } |
Then you can create a ConditionTask as such:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public class PlayerDamaged : ConditionTask{ private bool playerDamaged; //Init and error check protected override string OnInit(){ agent.DamageDealtToPlayer += this.DamageReceived; return null; } //Condition check protected override bool OnCheck(){ if (playerDamaged){ playerDamaged = false; return true; } return false; } void DamageReceived(float damage){ playerDamaged = true; } } |
You can now assign this Condition Task on a Condition node, an Accessor Decorator, an Interruptor Decorator or even an FSM transition depending on the design you are after. Notice that this condition will latch the event received until the condition is checked. Another probably better way if you want the condition to only return true in the same frame that the event was raised would be this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class PlayerDamaged : ConditionTask{ //Init and error check protected override string OnInit(){ agent.DamageDealtToPlayer += this.DamageReceived; return null; } //Condition check protected override bool OnCheck(){ return false; } void DamageReceived(float damage){ YieldReturn(true); } } |
The YieldReturn function is a special one that helps you do just what we are after here. This above condition will return true only the frame in which the event was raised. This is also what is used in the Trigger, Collision etc conditions by the way.
You can of course further expand this condition task to also store the damage amount received in a blackboard float variable if you want to.
I would also like to let you know that under ‘Script Control’ category there is a reflection based event subscriber condition called “Check CSharpEvent”. This lets you subscribe to an event of a MonoBehaviour through reflection, but the downside is that currently it supports only events without parameters. I can certainly improve this to accept parameters as well.
Let me know if that is what you are after.
Regarding the factory pattern example. Currently the way this can be done is to use a SubTree for each of those different upgradable behaviours within the minions ‘master’ behaviour tree. Based on conditions that determine whether or not the minion has that ability/behaviour, the SubTree can be filtered to execute or not. Probably the best way to achieve this would be to use an Accessor Decorator above each of those subtrees, like in the attached image. Notice that the Accessor Decorator, can be assigned any Condition Task.
Note that in next versions, the target behaviour of the SubTree node will become a variable able to be selected from the blackboard. As such you will be able to “inject” the target subtree at runtime, stored in the blackboard, and thus dynamicaly alter the behaviour far easier than it now is. This way, as you correctly noticed, SubTrees will also be able to be used with lists and the use of the ‘Iterate List’ Decorator for example.
Don’t hesitate to ask any further questions that you may have.
Cheers!
Join us on Discord: https://discord.gg/97q2Rjh