playMaker

Author Topic: Code: best and simplest way to interact with Unity events and delegates?  (Read 2749 times)

Broken Stylus

  • Beta Group
  • Hero Member
  • *
  • Posts: 824
Actions stay active as long as they're being used, which requires both a constant update (OnUpdate) and not leaving the state that contains them.

Suppose I find a piece of code that says I have to subscribe to a given event.
Is it possible to create a way to manage this within Playmaker, or do I absolutely need an external script, like one attached as a component to some active GameObject to communicate with a FSM, or an instance of a source one floating somewhere in the memory? Proxy?

I understand that it's very close to asking how to emulate what is essentially something not accessible to PM users, Global Events.

djaydino

  • Administrator
  • Hero Member
  • *****
  • Posts: 7618
    • jinxtergames
Re: Code: best and simplest way to interact with Unity events and delegates?
« Reply #1 on: October 20, 2023, 06:27:50 AM »
Hi.
you will probably need to create a custom action,
where you can subscribe

Then have a method to subscribe to, and make sure to unsubscribe when exiting the action (onExit).

Here is 2 sample scripts :

Code: [Select]
using UnityEngine;

namespace HutongGames.PlayMaker.Actions
{
[ActionCategory(ActionCategory.Application)]

using UnityEngine;

    namespace HutongGames.PlayMaker.Actions
    {
        [ActionCategory(ActionCategory.Events)]
        [Tooltip("Send event when script event is triggered")]
        public class SubscribeToMyScriptEvent : FsmStateAction
        {
            // The FSM event to be sent when the script event is triggered
            public FsmEvent sendEvent;

            public override void OnEnter()
            {
                // Subscribing to a event on a Global Script (static)
                MyScript.OnMyEvent += SendFsmEvent;
            }

            private void SendFsmEvent()
            {
                // send FSM Event when Unity Event is triggered
                Fsm.Event(sendEvent);
                Finish();
            }

            public override void OnExit()
            {
                // Unsubscribing the event
                MyScript.OnMyEvent -= SendFsmEvent;
            }
        }
    }
}

This one works if the target script is global (Static)

Code: [Select]
using UnityEngine;

namespace HutongGames.PlayMaker.Actions
{
[ActionCategory(ActionCategory.Application)]

using UnityEngine;

    namespace HutongGames.PlayMaker.Actions
    {
        [ActionCategory(ActionCategory.Events)]
        public class SubscribeToMyScriptEvent : FsmStateAction
        {
            // The FSM event to be sent when the script event is triggered
            public FsmEvent sendEvent;

            public MyScript myScriptDirect;

            public override void OnEnter()
            {
                // Subscribing to a event on a Global Script (static)
                myScriptDirect.OnMyEvent += SendFsmEvent;
            }

            private void SendFsmEvent()
            {
                // send FSM Event when Unity Event is triggered
                Fsm.Event(sendEvent);
                Finish();
            }

            public override void OnExit()
            {
                // Unsubscribing the event
                myScriptDirect.OnMyEvent -= SendFsmEvent;
            }
        }
    }
}

this one you need to drag in the script with the unity event.

Broken Stylus

  • Beta Group
  • Hero Member
  • *
  • Posts: 824
Re: Code: best and simplest way to interact with Unity events and delegates?
« Reply #2 on: October 20, 2023, 10:50:44 AM »
Very nice and thank you. I hope other people will find this helpful.
I wasn't expecting an action to be able to manage that all alone to be frank.
This is a problem I must overcome because I have to control an SDK initialization with a callback so the rest of the program can know when the initialization is complete. They require subscribing to an Action.


The two scripts seem to be almost identical though, except for one [Tooltip] attribute. Is there any reason to that?

I'm puzzled by the presence of declaration of two namespaces in the same script such as

Code: [Select]
namespace HutongGames.PlayMaker.Actions
{
[ActionCategory(ActionCategory.Application)]

using UnityEngine;

    namespace HutongGames.PlayMaker.Actions
    {

Is this not going to create a conflict? or is it to have the action displayed in two categories at once?

Quote
This one works if the target script is global (Static)

And public?

djaydino

  • Administrator
  • Hero Member
  • *****
  • Posts: 7618
    • jinxtergames
Re: Code: best and simplest way to interact with Unity events and delegates?
« Reply #3 on: October 22, 2023, 05:38:57 PM »

Sorry i accidentally placed 2 namespaces

Code: [Select]
using UnityEngine;

namespace HutongGames.PlayMaker.Actions
{
    [ActionCategory(ActionCategory.Events)]
    [Tooltip("Send event when script event is triggered")]
    public class SubscribeToMyScriptEvent : FsmStateAction
    {
        // The FSM event to be sent when the script event is triggered
        public FsmEvent sendEvent;

        public override void OnEnter()
        {
            // Subscribing to a event on a Global Script (static)
            MyScript.OnMyEvent += SendFsmEvent;
        }

        private void SendFsmEvent()
        {
            // send FSM Event when Unity Event is triggered
            Fsm.Event(sendEvent);
            Finish();
        }

        public override void OnExit()
        {
            // Unsubscribing the event
            MyScript.OnMyEvent -= SendFsmEvent;
        }
    }
}

Code: [Select]
using UnityEngine;

namespace HutongGames.PlayMaker.Actions
{
    [ActionCategory(ActionCategory.Events)]
    public class SubscribeToMyScriptEvent : FsmStateAction
    {
        // The FSM event to be sent when the script event is triggered
        public FsmEvent sendEvent;

        public MyScript myScriptDirect;

        public override void OnEnter()
        {
            // Subscribing to a event on a Global Script (static)
            myScriptDirect.OnMyEvent += SendFsmEvent;
        }

        private void SendFsmEvent()
        {
            // send FSM Event when Unity Event is triggered
            Fsm.Event(sendEvent);
            Finish();
        }

        public override void OnExit()
        {
            // Unsubscribing the event
            myScriptDirect.OnMyEvent -= SendFsmEvent;
        }
    }
}

The main difference is MyScript and myScriptDirect.

If the script that you need to subscribe to is Globally accessible you can use the 1st script.

The 2nd one you need to drag/drop the gameobject/component with the script into "myScriptDirect" variable

Broken Stylus

  • Beta Group
  • Hero Member
  • *
  • Posts: 824
Re: Code: best and simplest way to interact with Unity events and delegates?
« Reply #4 on: October 25, 2023, 05:40:42 AM »
Ok I see.
They use a slightly different method because they want the users to actual call a two separate static functions to subscribe and unsubscribe in another static class. The argument to pass through them is an Action (in c#), which means that in my Playmaker action (yeah, I've got to make a distinction here for the sake of clarity for anyone who reads this) I will simply have the method that needs to be triggered that will have the same signature. Not a big deal.
I had a expected a complicated structure but going with the solution where everything stays within one action is much better, it remains more FSM-ish.

However, I expected the action to have to remain active. I couldn't see how the script that defines an action could receive any callback if the action had already ran its course.
I mean, wouldn't the part that is waiting for a signal (the subscriber to the c# event delegate) need to be in OnUpdate(){} block?
When the subscriber is part of a script that is glued as a component to a game object and said game object remains active in the scene, then the script and therefore its code portion that is the subscriber remains receptive to any signal. Once the game object is deactivated, the script becomes dormant and can't hear anything.

In your example, how could SendFsmEvent() be "triggerable" at any time since the action will be be read only once (one cycle) and then deactivate itself as the flow continues its course through the hosting state and then outside of said state?

Something like this:

Code: [Select]
using UnityEngine;

namespace HutongGames.PlayMaker.Actions
{
    [ActionCategory(ActionCategory.Events)]
    public class SubscribeToMyScriptEvent : FsmStateAction
    {
        // The FSM event to be sent when the script event is triggered
        public FsmBool abort;
        public FsmEvent sendEvent;

        public MyScript myScriptDirect;

        public override void Reset()
        {
            abort = false;
        }

        public override void OnEnter()
        {
            // Subscribing to a event on a Global Script (static)
            myScriptDirect.OnMyEvent += SendFsmEvent;
        }

        public override void OnUpdate()
        {
            // Keeps the action active to receive the callback
            // Quits if FsmBool abort becomes true
            if (abort.Value)
            {
                  Finish();
            }
        }

        private void SendFsmEvent()
        {
            // send FSM Event when Unity Event is triggered
            Fsm.Event(sendEvent);
            Finish();
        }

        public override void OnExit()
        {
            // Unsubscribing the event
            myScriptDirect.OnMyEvent -= SendFsmEvent;
        }
    }
}

I suppose technically that each action we shove into a FSM state is an instance of the class that defines said action. So we shouldn't approach this as if the original action script were a scriptable object?
I'm new to this but I may understand that such a script (scriptable object) is a kind of static static, or super static script that doesn't even need to be in a scene, it's directly accessible in the assets. Big IF on my part there. I could be totally wrong about it.
So the subscriber belongs to the action that's in a state, it doesn't belong to the mother script. That's why I try to keep the action active at all time, and that's also why I considered going with a proxy solution where a script acts as a component on a game object and communicates with a FSM, preferably on the same game object.
« Last Edit: November 07, 2023, 07:00:43 AM by Broken Stylus »

Broken Stylus

  • Beta Group
  • Hero Member
  • *
  • Posts: 824
Re: Code: best and simplest way to interact with Unity events and delegates?
« Reply #5 on: October 25, 2023, 05:56:45 AM »
Regarding the proxy component solution, which can be considered for a moment even if it requires using an external element to a FSM, I suppose it would come with a field which would be the event delegate to subscribe to, and a second one that would be for the targeted FSM to trigger with a Playmaker Global Event.
Eventually we may even narrow the second field to force it to point to a FSM by making it of a "HuttongGame FSM" type, or something of that order.

The first field would require the user to point to another script, perhaps on another game object.

Mm... so there should actually be two more fields that point to game objects, one that hosts the script that contains the code that will manage and send the event, and one that is host to the targeted FSM.

Broken Stylus

  • Beta Group
  • Hero Member
  • *
  • Posts: 824
Re: Code: best and simplest way to interact with Unity events and delegates?
« Reply #6 on: November 20, 2023, 07:03:11 AM »
Coming back to this.

I noticed a few things regarding the way the code works in Playmaker.

An active action that has an OnUpdate() loop is green in the state that holds it. Once the flux gets out of the loop, the action turns to white/gray. While this seems to indicate that the action is not active anymore, and for this I assume that strictly Playmaker methods Reset(), OnEnter(), OnUpdate(), OnExit() are indeed going to be ignored once Finish() is used, if an action script holds a piece of code that stands out of this (as if it were part of a basic Unity monobehavior script), the action will remain "ghostly" active as long as the flux stays in the state that holds it.

Whether one uses Finish() (that triggers the default FINISHED transition) or uses a custom event, not leaving the state keeps the non-Playmaker code running. That is why the code responsible for waiting to be triggered after the subscription was actually getting triggered and sending me debug information, as I put some debug stuff in the relevant portions of the code to monitor what was going on inside: This part of the action wasn't deactivated even after hitting the Finish() clause in the OnUpdate() loop, contrary to the rest of the Playmaker-code that composed the action.