playMaker

Author Topic: better Script->fsm.Event integration.  (Read 5102 times)

Andy22

  • Junior Playmaker
  • **
  • Posts: 99
better Script->fsm.Event integration.
« on: December 19, 2013, 10:22:00 AM »
Hi,

while trying to solve the problem how to correctly send a event + data from a gameobject/component to a single and multiple FSM's, using the default FSMEvent system some things where unexpected and could be improved.


Our main goals we wanted to achieve:

1) Able to send a fsm events similar to the unity message or UFPS event system.
2) The ability to hand over generic data to the "sendEvent" call, without directly interfering with the default Fsm.EventData member/logic.
3) Ensure that the actual event has its own copy of the event data to send, so it can be properly delayed, via default Invoke and vp_timers.
4) The ability to directly send Fsm events to a collection.
5) Ensure proper sentByInfo from script, see A)
6) Getting a warning if u try to send a unknown FSM event.
7) Make it clear to the script caller what event target will be used, so avoiding the SetEventTarget action.


Example goals:
Code: [Select]
Invoke(() => gameObject.BroadcastFsmEvent("MyEventname", ObjList, floatdata), delay);

vp_Timer.In(delay, () => gameObject.SendFsmEvent("MyEventname", targetGameobject, customDataRef));



Here is a problem we had and could not yet solve:

A) There is currently no way to set a gameobject as Event sender, even while the final usable action "Get Event Info" actually only returns a gameobject sender, instead of a Fsm.

Thats because FsmEventData only has a  "public Fsm SentByFsm;" field.


Problems we run into, while trying to connect our script event system, with FSMEvents:

B) The two Fsm calls, ProcessEvent() and BroadcastEventToGameObject(), take a FsmEventData parameter, which would indicate that they also use this parameter. They both actually only use the sentbyInfo out of this structure, which caused quite some confusion on our part. So this should be better reflected, for example by better splitting the actual event data, from the sentByInfo or by making those calls private/internal.


The main thing we had to understand was: How PM handles event data and that those are global, as compared to local/per event and what this means in practice.


Here is the core extension function that achieved most of those goals:

Code: [Select]
public static void Event([NotNull] this Fsm inFsm, [NotNull] FsmEvent inFsmEvent, [CanBeNull] FsmEventData inToUseEventData = null)
{
if (inFsm == null) {
throw new ArgumentNullException("inFsm");
}
if (FsmEvent.IsNullOrEmpty(inFsmEvent)) {
throw new ArgumentNullException("inFsmEvent");
}

if (inToUseEventData == null) {
Fsm.SetEventDataSentByInfo(); // ProcessEvent will override this correctly if data is given!
}
else {
Fsm.EventData = inToUseEventData; // NOTE: needed since, ProcessEvent wont actually set the data, it only sets the sentByInfo
}

inFsm.ProcessEvent(inFsmEvent, inToUseEventData);
// TODO: copy&paste from org. whats happening here and why compared to what ProcessEvent is already doing?
if (FsmExecutionStack.ExecutingFsm != inFsm) {
FsmExecutionStack.PushFsm(inFsm);
inFsm.UpdateStateChanges();
FsmExecutionStack.PopFsm();
}
}


Here is a example gameobject extension version we now use from script:

Code: [Select]
// optimized version for collections
public static void BroadcastFsmEvent([NotNull] this GameObject inSender, [NotNull] string inEventName, [NotNull] IEnumerable<GameObject> inReceivers, [CanBeNull] System.Object inData = null)
{
if (inSender == null) {
throw new ArgumentNullException("inSender");
}
if (inEventName.IsNullOrEmpty()) {
throw new ArgumentNullException("inEventName");
}
if (inReceivers == null) {
throw new ArgumentNullException("inReceivers");
}

FsmEvent fsmEvent = PlayMakerUtils.GetFsmEventEx(inEventName);
FsmEventData eventData = PlayMakerUtils.GetAsEventData(inData);

foreach (GameObject go in inReceivers) {
foreach (PlayMakerFSM fsm in go.TryGetComponents<PlayMakerFSM>(true).Where(f => f.Fsm != null)) {
fsm.Fsm.Event(fsmEvent, eventData);
}
}
}


I hope this might get u a idea what we needed and maybe u can add/change some things around to allow better integration, by default.

bye Andy


PS: If interested i could also send the stripped down extension and utility calls we use/added?


« Last Edit: December 19, 2013, 10:29:53 AM by Andy22 »

jeanfabre

  • Administrator
  • Hero Member
  • *****
  • Posts: 15500
  • Official Playmaker Support
Re: better Script->fsm.Event integration.
« Reply #1 on: December 19, 2013, 01:19:25 PM »
Hi,

 I would recommand you totally ditch the default event data structure, it doesn't scale at all. I created a special event properties system so that you can pass any number of key/value with an event, it's definitly more powerful, and in there you can define a standard key for the sender, and you are good to go without trying to hack around.

http://hutonggames.com/playmakerforum/index.php?topic=4296.msg20172#msg20172

also, messing with execution stack is dangerous, I would personnally not go there at all.

 To solve this problem all together, I took a different approach by inserting a middle man Fsm between my script and PlayMaker Environment.

Basically, I use a fsm to fire events, and then the sender is obviously that fsm, so for a given script, I pair an Fsm, and all these problems go away.

What's really missing in m opinion is delegates to catch when events are fired, it^s somethig you can't do with PlayMaker Api, and yet, using a fsm proxy you can still achieve it, tho it's not flexible because you have to hard code events you want to listen too, in some occasions, I would like to listen to events sent a specific gameobject, or fsm or simply broadcasted and filter them manually within my script.

 Bye,

 Jean

Andy22

  • Junior Playmaker
  • **
  • Posts: 99
Re: better Script->fsm.Event integration.
« Reply #2 on: January 07, 2014, 06:20:57 AM »
Hi,

 I would recommand you totally ditch the default event data structure, it doesn't scale at all.

http://hutonggames.com/playmakerforum/index.php?topic=4296.msg20172#msg20172

Back from holidays yay!

I checked your system, basically u wrap a string map into the default objectData member, if i see this correctly. This is ofc more flexible and if needed i will rewrite our logic to use this system.

I also noticed those FSM proxies at other systems, the problem remains that if a gameobject is sending a event to a FSM, if i need a sender i always want the org. sender not the proxy. So u have to wrap and reconstruct/set this as well.

Can u also give a example on how to use your system directly from script? I don't see any new script usable function to actually send a event + data?
How do u send a event + data with your code and how would u handle delayed events via Invoke?

The main goal for us was to make communicating with a FSM from script via events simple and similar to the other event system's we use. In our components we need the ability to quickly switch from unity messages to UFPS events or FSM events. Thats because the logical event "wiring" should stay the same and it should not matter to what receiver system a event + data is actually send to.

Thx Andy


PS: I'm also confused on how such FSM features/changes are handled inside hutonggames.com. Your system is a improvement in flexibility and scalability and i don't see a huge compatibility issue. So why are quite a few FSM packages "hidden" inside the forum and not added to the playmaker core?
« Last Edit: January 07, 2014, 06:31:07 AM by Andy22 »

jeanfabre

  • Administrator
  • Hero Member
  • *****
  • Posts: 15500
  • Official Playmaker Support
Re: better Script->fsm.Event integration.
« Reply #3 on: January 10, 2014, 07:19:05 AM »
Hi,

It's like the asset store, some packages you can get there are mandatory for 99% of my projects, but they never will be part of Unity, simply because it's too much. The same thing applies within PlayMaker. I don't think it would make sense to add too much to the default PlayMaker set.

 now yes, it would be good that at some point something move convenient takes place to manage all these custom actions and packages.

To use it in scripts, simply copy how I do it in the custom actions themselves, that's all you need. Can you get back to me on this next week and I'll do working sample using scripts and delayed call.

Bye,

 Jean