Playmaker Forum

PlayMaker Help & Tips => PlayMaker Help => Topic started by: Krileon on December 18, 2013, 10:56:08 PM

Title: Add FSM Template at Runtime? [SOLVED]
Post by: Krileon on December 18, 2013, 10:56:08 PM
I've found the best way to do Debuffs/Buffs and things of the sort is to attach the script then have it remove it self when the debuff/buff has expired. For PlayMaker the best way to do this would be to add a template at runtime anytime you want to add the debuff/buff. Ideally when doing this it should also check to see if the template already exists and if it does optionally replace it or do nothing. This would be EXTREMELY handy.

Is something like this available? Is there API for even adding an FSM at runtime? If it's not available, but API is there to do it I'll g ahead and just make a new action for it.

Note I've tried other alternatives like having an FSM disabled then enabling it, sending an event, or flipping a variable and having it check every frame if the variable changed. All of these work, but none of them are ideal. Sure it works if you have a hand full of debuffs/buffs, but lets say you've dozens upon dozens it quickly begins to fail as an option to do either of those.
Title: Re: Add FSM Template at Runtime?
Post by: jeanfabre on December 19, 2013, 02:45:32 AM
Hi,

 never done it before, but I found a method for this:

Code: [Select]
public FsmTemplate temp;

void Start () {
PlayMakerFSM _comp = this.gameObject.AddComponent<PlayMakerFSM>();
_comp.SetFsmTemplate(temp);
}

and that works :)

bye,

 Jean
Title: Re: Add FSM Template at Runtime?
Post by: Krileon on December 19, 2013, 12:51:32 PM
Awesome, I'll make a playmaker action that does that and see how it goes.
Title: Re: Add FSM Template at Runtime?
Post by: Krileon on December 25, 2013, 12:36:09 PM
Ok, I was able to add the template pretty easily. Performance seams ok thus far. Now I need to destroy the template (I'll have this done from the FSM, it needs to destroy it self). This allows for a debuff to run for a specific amount of time and get rid of it self. This is critical usage as adding a ton of disabled FSMs per debuff per object would be insane for example.
Title: Re: Add FSM Template at Runtime?
Post by: Krileon on December 25, 2013, 02:27:16 PM
Ok, the below action will add a FSM Template at runtime.

It'll also optionally check if the FSM Template should be unique or not. If it's Unique then it's a unique instance and won't bother checking if an instance of the same FSM Template already exists. If it's not unique then it'll check if the FSM name (if supplied) exists or the FSM template exists. If either of them do exist when it's not unique then it won't add the FSM template.

You can optionally set the FSM to be enabled or disabled on creation so you can optionally enable it later as needed.

Last but not least you can optionally send variables to the newly added template. This will initially disable the FSM, add the template, change the variables, then enable the FSM so it can properly start with the variables added.

Code: [Select]
using System.Collections.Generic;
using UnityEngine;

namespace HutongGames.PlayMaker.Actions {
[ActionCategory(ActionCategory.StateMachine)]
public class AddFsmTemplate : FsmStateAction {
[RequiredField]
public FsmOwnerDefault gameObject;
[RequiredField]
public FsmTemplate template;
public FsmString name;
public FsmBool active;
public FsmBool unique;
[CompoundArray("Variables", "Name", "Variable")]
[RequiredField]
public FsmString[] variableNames;
[RequiredField]
public FsmVar[] variables;

private GameObject previousGo;
private List<PlayMakerFSM> fsms;

public override void Reset() {
gameObject = null;
template = null;
name = new FsmString { UseVariable = true };
active = new FsmBool { Value = true };
unique = new FsmBool { Value = false };
variableNames = new FsmString[0];
variables = new FsmVar[0];
}

public override void OnEnter() {
var go = Fsm.GetOwnerDefaultTarget( gameObject );

if ( go == null ) {
return;
}

bool exists = false;

if ( ! unique.Value ) {
if ( go != previousGo ) {
fsms = new List<PlayMakerFSM>();

fsms.AddRange( go.GetComponents<PlayMakerFSM>() );

previousGo = go;
}

if ( fsms.Count > 0 ) foreach ( PlayMakerFSM fsm in fsms ) {
if ( ( ( name.Value != "" ) && ( fsm.FsmName == name.Value ) ) || ( ( fsm.FsmTemplate != null ) && ( fsm.FsmTemplate.name == template.name ) ) ) {
exists = true;
}
}
}

if ( ! exists ) {
PlayMakerFSM newFsm = go.AddComponent<PlayMakerFSM>();

if ( name.Value != "" ) {
newFsm.FsmName = name.Value;
}

if ( ( ! active.Value ) || ( variableNames.Length > 0 ) ) {
newFsm.enabled = false;
}

newFsm.SetFsmTemplate( template );

if ( variableNames.Length > 0 ) {
if ( variableNames.Length > 0 ) for ( int i = 0; i < variableNames.Length; i++ ) {
if ( ! variableNames[i].IsNone ) {
NamedVariable target = newFsm.Fsm.Variables.GetVariable( variableNames[i].Value );

if ( target != null ) {
variables[i].ApplyValueTo( target );
}
}
}

if ( active.Value && ( ! newFsm.enabled ) ) {
newFsm.enabled = true;
}
}

if ( ! unique.Value ) {
fsms.Add( newFsm );
}
}

Finish();
}
}
}

Now all that's left is a "DestroyFSM" action to completely get rid of the FSM component. I have yet to figure this one out yet, but once I do this can be used very effectively for spell debuffs, temporary affects like drag on a rigidbody to simulate "slowing", and much much more.

Regarding performance using the above I saw very little loss, if any at all, so it seams perfectly safe to use as needed.
Title: Re: Add FSM Template at Runtime?
Post by: Krileon on December 25, 2013, 04:22:17 PM
Ok, the below action will delete an FSMs self. I had to loop through the FSMs on the owner then compare to the current FSM to implement this though. Pretty silly to have to actually. The FSM should be updated with a variable that stores its component or at least a utility to more efficiently grab the component. Anyway the below works.

Code: [Select]
using UnityEngine;

namespace HutongGames.PlayMaker.Actions {
[ActionCategory(ActionCategory.StateMachine)]
public class DestroyFSM : FsmStateAction {

public override void OnEnter() {
if ( Owner != null ) {
PlayMakerFSM[] fsms = Owner.GetComponents<PlayMakerFSM>();

if ( fsms.Length > 0 ) foreach ( PlayMakerFSM fsm in fsms ) {
if ( fsm.Fsm.CompareTo( Fsm ) == 0 ) {
Object.Destroy( fsm );
}
}
}

Finish();
}
}
}

I now no longer need to have a dozen objects checking every frame if they've changed variables or waiting for an event. I can now add them, with the needed variables, and remove them automatically. My "Slow" debuff is working fantastically. I don't see any performance loss from using this either.
Title: Re: Add FSM Template at Runtime?
Post by: Alex Chouls on December 25, 2013, 04:38:08 PM
Very cool stuff Krileon!

In DestroyFSM you should be able to just use Object.Destroy(fsm.Owner);
Title: Re: Add FSM Template at Runtime?
Post by: Krileon on December 25, 2013, 08:16:50 PM
Very cool stuff Krileon!

In DestroyFSM you should be able to just use Object.Destroy(fsm.Owner);
That'd destroy the gameObject. I want to just destroy the FSM. The structure is Component.Fsm.etc, but in the action "this" and "Fsm" are both the ".Fsm" part of the structure. I can't find a way to go back by 1 and get the component object so it could be destroyed. Only solution I found thus far was to parse out all the owners FSM components, compare to the component to destroy, once matched destroy it. This works flawlessly, but I believe there has to be another way (in a normal script you'd just destroy "this").

Anyway, this is totally awesome. I have 2 scenarios already implemented in my game. 1 is a "Slow" template that can take a "Drag" variable and apply drag for a set time limit to one of my rigidbodies then restore to normal and remove it self. I have another that is a "Scare" template that causes a rigidbody to reverse its direction by sending a new event state to the controller FSM (another custom action, I wrote a rigidbody collisions action to mimic normal controller usage).
Title: Re: Add FSM Template at Runtime?
Post by: Alex Chouls on December 25, 2013, 10:46:41 PM
In an action fsm.Owner should reference the PlayMakerFSM component.

Owner references the GameObject.

If I remember correctly... not at my computer...
Title: Re: Add FSM Template at Runtime?
Post by: Krileon on December 26, 2013, 08:20:34 AM
Ah, I was trying to use this.Owner. That works perfectly! Below is the final destroy fsm action, works perfectly.

Code: [Select]
using UnityEngine;

namespace HutongGames.PlayMaker.Actions {
[ActionCategory(ActionCategory.StateMachine)]
public class DestroyFSM : FsmStateAction {

public override void OnEnter() {
if ( Fsm.Owner != null ) {
Object.Destroy( Fsm.Owner );
}

Finish();
}
}
}
Title: Re: Add FSM Template at Runtime?
Post by: Krileon on December 27, 2013, 11:07:41 PM
Upgraded Add Fsm Template with a variable to replace a matching template. This basically can be used to refresh a buff, etc..

Code: [Select]
using System.Collections.Generic;
using UnityEngine;

namespace HutongGames.PlayMaker.Actions {
[ActionCategory(ActionCategory.StateMachine)]
public class AddFsmTemplate : FsmStateAction {
[RequiredField]
public FsmOwnerDefault gameObject;
[RequiredField]
public FsmTemplate template;
public FsmString name;
public FsmBool active;
public FsmBool unique;
public FsmBool replace;
[CompoundArray("Variables", "Name", "Variable")]
[RequiredField]
public FsmString[] variableNames;
[RequiredField]
public FsmVar[] variables;

private GameObject previousGo;
private List<PlayMakerFSM> fsms;

public override void Reset() {
gameObject = null;
template = null;
name = new FsmString { UseVariable = true };
active = new FsmBool { Value = true };
unique = new FsmBool { Value = false };
replace = new FsmBool { Value = false };
variableNames = new FsmString[0];
variables = new FsmVar[0];
}

public override void OnEnter() {
var go = Fsm.GetOwnerDefaultTarget( gameObject );

if ( go == null ) {
return;
}

bool exists = false;

if ( ( ! unique.Value ) || replace.Value ) {
if ( go != previousGo ) {
fsms = new List<PlayMakerFSM>();

fsms.AddRange( go.GetComponents<PlayMakerFSM>() );

previousGo = go;
}

if ( fsms.Count > 0 ) foreach ( PlayMakerFSM fsm in fsms ) {
if ( ( ( name.Value != "" ) && ( fsm.FsmName == name.Value ) ) || ( ( fsm.FsmTemplate != null ) && ( fsm.FsmTemplate.name == template.name ) ) ) {
if ( replace.Value ) {
Object.Destroy( fsm );
} else {
exists = true;
}
}
}
}

if ( ! exists ) {
PlayMakerFSM newFsm = go.AddComponent<PlayMakerFSM>();

if ( name.Value != "" ) {
newFsm.FsmName = name.Value;
}

if ( ( ! active.Value ) || ( variableNames.Length > 0 ) ) {
newFsm.enabled = false;
}

newFsm.SetFsmTemplate( template );

if ( variableNames.Length > 0 ) {
if ( variableNames.Length > 0 ) for ( int i = 0; i < variableNames.Length; i++ ) {
if ( ! variableNames[i].IsNone ) {
NamedVariable target = newFsm.Fsm.Variables.GetVariable( variableNames[i].Value );

if ( target != null ) {
variables[i].ApplyValueTo( target );
}
}
}

if ( active.Value && ( ! newFsm.enabled ) ) {
newFsm.enabled = true;
}
}

if ( ( ! unique.Value ) || replace.Value ) {
fsms.Add( newFsm );
}
}

Finish();
}
}
}
Title: Re: Add FSM Template at Runtime?
Post by: Krileon on December 28, 2013, 01:13:34 PM
Upgraded Add Fsm Template even more. Now it can return and store the fsm component object. It can also send an event if the template already exists or when the template is replaced. There's also a new variable under the replace event to force replace to not destroy the fsm. The primary usage for that is if you want your own cleanup inside of a different fsm, but you still want it to add the fsm.

Code: [Select]
using System.Collections.Generic;
using UnityEngine;

namespace HutongGames.PlayMaker.Actions {
[ActionCategory(ActionCategory.StateMachine)]
public class AddFsmTemplate : FsmStateAction {
[RequiredField]
public FsmOwnerDefault gameObject;
[RequiredField]
public FsmTemplate template;
public FsmString name;
public FsmBool active;
public FsmBool unique;
public FsmBool replace;
[CompoundArray("Variables", "Name", "Variable")]
[RequiredField]
public FsmString[] variableNames;
[RequiredField]
public FsmVar[] variables;
[ObjectType(typeof(PlayMakerFSM)), UIHint(UIHint.Variable)]
public FsmObject storeComponent;
[ActionSection("Exists Event")]
public FsmEventTarget existsEventTarget;
public FsmString existsSendEvent;
[ActionSection("Replace Event")]
public FsmBool doNotDestroy;
public FsmEventTarget replaceEventTarget;
public FsmString replaceSendEvent;

private GameObject previousGo;
private List<PlayMakerFSM> fsms;

public override void Reset() {
gameObject = null;
template = null;
name = new FsmString { UseVariable = true };
active = new FsmBool { Value = true };
unique = new FsmBool { Value = false };
replace = new FsmBool { Value = false };
variableNames = new FsmString[0];
variables = new FsmVar[0];
storeComponent = null;
existsEventTarget = null;
existsSendEvent = null;
doNotDestroy = new FsmBool { Value = false };
replaceEventTarget = null;
replaceSendEvent = null;
}

public override void OnEnter() {
var go = Fsm.GetOwnerDefaultTarget( gameObject );

if ( go == null ) {
return;
}

bool exists = false;

if ( ( ! unique.Value ) || replace.Value ) {
if ( go != previousGo ) {
fsms = new List<PlayMakerFSM>();

fsms.AddRange( go.GetComponents<PlayMakerFSM>() );

previousGo = go;
}

if ( fsms.Count > 0 ) foreach ( PlayMakerFSM fsm in fsms ) {
if ( ( ( name.Value != "" ) && ( fsm.FsmName == name.Value ) ) || ( ( fsm.FsmTemplate != null ) && ( fsm.FsmTemplate.name == template.name ) ) ) {
if ( replace.Value ) {
if ( replaceSendEvent.Value != "" ) {
Fsm.Event( replaceEventTarget, replaceSendEvent.Value );
}

if ( ! doNotDestroy.Value ) {
Object.Destroy( fsm );
}
} else {
storeComponent.Value = fsm;
exists = true;
}
}
}
}

if ( ! exists ) {
PlayMakerFSM newFsm = go.AddComponent<PlayMakerFSM>();

if ( name.Value != "" ) {
newFsm.FsmName = name.Value;
}

if ( ( ! active.Value ) || ( variableNames.Length > 0 ) ) {
newFsm.enabled = false;
}

newFsm.SetFsmTemplate( template );

if ( variableNames.Length > 0 ) {
if ( variableNames.Length > 0 ) for ( int i = 0; i < variableNames.Length; i++ ) {
if ( ! variableNames[i].IsNone ) {
NamedVariable target = newFsm.Fsm.Variables.GetVariable( variableNames[i].Value );

if ( target != null ) {
variables[i].ApplyValueTo( target );
}
}
}

if ( active.Value && ( ! newFsm.enabled ) ) {
newFsm.enabled = true;
}
}

if ( ( ! unique.Value ) || replace.Value ) {
fsms.Add( newFsm );
}

storeComponent.Value = newFsm;
} else {
if ( existsSendEvent.Value != "" ) {
Fsm.Event( existsEventTarget, existsSendEvent.Value );
}
}

Finish();
}
}
}
Title: Re: Add FSM Template at Runtime?
Post by: ClaudioFreda on February 20, 2014, 07:35:25 AM
I've noticed that FSM instantiated with this action do not receive TriggerEnter/Stay/Exit events. I've looked at the code and cannot understand why.

Does this problem happen for everybody?
Title: Re: Add FSM Template at Runtime?
Post by: jeanfabre on February 24, 2014, 08:01:47 AM
Hi,

 uhm, likely because Fsm needs to be explicitly told to implement these performant sensitive colliders events.

I never played with this action. Maybe Krileon or you can provide a working sample, then I'll look into this.

bye,

 Jean
Title: Re: Add FSM Template at Runtime?
Post by: Alex Chouls on March 03, 2014, 12:06:27 PM
For performance reasons you have to manually subscribe to these Unity events.

I've added a Unity Event Handling section on the wiki that should help:
https://hutonggames.fogbugz.com/default.asp?W1098

Ideally event settings in the template should bubble up to the running FSM. I'll look into this...
Title: Re: Add FSM Template at Runtime?
Post by: JennaReanne on June 19, 2014, 01:52:03 PM
I know this thread is old but wow, these actions are amazing.  Thank you so much for sharing!
Title: Re: Add FSM Template at Runtime? [SOLVED]
Post by: xunxun on September 25, 2014, 04:45:54 AM
this is awesome. Thanks much!
Title: Re: Add FSM Template at Runtime? [SOLVED]
Post by: ClintonReddie on October 13, 2014, 01:57:22 AM
More thanks.
Title: Re: Add FSM Template at Runtime? [SOLVED]
Post by: joepalos on July 31, 2016, 02:56:20 PM
Thanks a lot for this, amazing.
Title: Re: Add FSM Template at Runtime? [SOLVED]
Post by: MarkD on October 01, 2016, 02:33:38 AM
This is really fantastic - thank you for posting it!

One thing I'm having trouble with however:  I can add an FSM template to my player and then have that FSM destroy itself when the "buff" is finished.  But after that buff has expired, I can't get that same template to be applied again to the player. 

I want it so that it only adds the script again IF the buff has expired.  I don't want to use "exists" to add another template, rather I want the buff to be applied, run its full course, and then be able to be applied again.

Any ideas?  Am I missing something?

Here's my setup in case it's something in my setup.

(http://i1308.photobucket.com/albums/s618/mark_davis18/Buff_zpsr6zlfavw.png)
Title: Re: Add FSM Template at Runtime?
Post by: ransomink on October 08, 2016, 05:28:38 AM
Last but not least you can optionally send variables to the newly added template. This will initially disable the FSM, add the template, change the variables, then enable the FSM so it can properly start with the variables added.

Code: [Select]
using System.Collections.Generic;
using UnityEngine;

namespace HutongGames.PlayMaker.Actions {
[ActionCategory(ActionCategory.StateMachine)]
public class AddFsmTemplate : FsmStateAction {
[RequiredField]
public FsmOwnerDefault gameObject;
[RequiredField]
public FsmTemplate template;
public FsmString name;
public FsmBool active;
public FsmBool unique;
[CompoundArray("Variables", "Name", "Variable")]
[RequiredField]
public FsmString[] variableNames;
[RequiredField]
public FsmVar[] variables;

private GameObject previousGo;
private List<PlayMakerFSM> fsms;

public override void Reset() {
gameObject = null;
template = null;
name = new FsmString { UseVariable = true };
active = new FsmBool { Value = true };
unique = new FsmBool { Value = false };
variableNames = new FsmString[0];
variables = new FsmVar[0];
}

public override void OnEnter() {
var go = Fsm.GetOwnerDefaultTarget( gameObject );

if ( go == null ) {
return;
}

bool exists = false;

if ( ! unique.Value ) {
if ( go != previousGo ) {
fsms = new List<PlayMakerFSM>();

fsms.AddRange( go.GetComponents<PlayMakerFSM>() );

previousGo = go;
}

if ( fsms.Count > 0 ) foreach ( PlayMakerFSM fsm in fsms ) {
if ( ( ( name.Value != "" ) && ( fsm.FsmName == name.Value ) ) || ( ( fsm.FsmTemplate != null ) && ( fsm.FsmTemplate.name == template.name ) ) ) {
exists = true;
}
}
}

if ( ! exists ) {
PlayMakerFSM newFsm = go.AddComponent<PlayMakerFSM>();

if ( name.Value != "" ) {
newFsm.FsmName = name.Value;
}

if ( ( ! active.Value ) || ( variableNames.Length > 0 ) ) {
newFsm.enabled = false;
}

newFsm.SetFsmTemplate( template );

if ( variableNames.Length > 0 ) {
if ( variableNames.Length > 0 ) for ( int i = 0; i < variableNames.Length; i++ ) {
if ( ! variableNames[i].IsNone ) {
NamedVariable target = newFsm.Fsm.Variables.GetVariable( variableNames[i].Value );

if ( target != null ) {
variables[i].ApplyValueTo( target );
}
}
}

if ( active.Value && ( ! newFsm.enabled ) ) {
newFsm.enabled = true;
}
}

if ( ! unique.Value ) {
fsms.Add( newFsm );
}
}

Finish();
}
}
}

Now all that's left is a "DestroyFSM" action to completely get rid of the FSM component. I have yet to figure this one out yet, but once I do this can be used very effectively for spell debuffs, temporary affects like drag on a rigidbody to simulate "slowing", and much much more.

Regarding performance using the above I saw very little loss, if any at all, so it seams perfectly safe to use as needed.

Is there anyone else having trouble getting the variables to update? Adding the template works great, but when I choose variables to use for the template, it does not operate correctly...
Title: Re: Add FSM Template at Runtime? [SOLVED]
Post by: jeanfabre on October 20, 2016, 02:24:33 AM
Hi,

 the variable is only used on Enter, it doesn't udpdate everyframe.

 For this you'll need to use GetFsmXXX and GetHost or GetHostInfo from the Ecosystem, and then your template can get fsm variables this way ( AND set variables too). If you want to decouple the variable name, have as public variable the names of the variables, and then within your template you use them names to access variables, then your template doesn't have to know the variables names of the host.


Bye,

 Jean
Title: Re: Add FSM Template at Runtime? [SOLVED]
Post by: abend on October 02, 2019, 04:09:50 PM
I am using this script to add a template at runtime from a loop to 4 or 5 game objects. I have 1 variable on my template called Target which is a GameObject. When running, sometimes the variable is populated with what I expect, other times all variables are None, and sometimes the middle of the loop objects are populated, bnut the first and last object are none. I see that there are paired variable arrays here. When I step through, it always seems to populate correctly, but when I run it freely, it's hit or miss. Is there another aspect to this that I am not understanding?

UPDATE: I have skipped the variables section in this script and run set fsm variable right after this. It seems to be working now.

Thanks!
Title: Re: Add FSM Template at Runtime? [SOLVED]
Post by: 600 on March 11, 2020, 08:41:26 AM
Thank you for the action!

Would be convenient to have it on Ecosystem ;D
Title: Re: Add FSM Template at Runtime? [SOLVED]
Post by: RDroste on May 21, 2020, 08:42:56 AM
Yes can we get this on the ecosystem please
Title: Re: Add FSM Template at Runtime? [SOLVED]
Post by: jeanfabre on May 26, 2020, 03:04:26 AM
Hi,

 done, it's on the Ecosystem :)

Bye,

 Jean
Title: Re: Add FSM Template at Runtime? [SOLVED]
Post by: Jeremy on July 24, 2020, 11:03:35 PM
I made an Action on my side before seen yours.
My needs wasn't the same and I saw yours after, so I integrated your code to mine because it could be usefull.
The purpose of my Action was to add FSM template at runtime with system events activated. because with a simple Action to add the FSM Template, it works in Editor Playmode but not in Build. With my Action it works fine.
I have just tested MouseEvents because it's what I needed for my project for the moment but I have implemented the other Events for later needs, I think the problem will be like for the MouseEvent and the solution too.

I share it, if you want to take a look and update your Action.

Custom Action Editor: Editor/AddFSMTemplateToGameObjectEditor.cs
Code: [Select]
// action created by Jeremy Sebagh - 20200720
// modified by Jeremy Sebagh - 20200725

using UnityEngine;
using UnityEditor;
using HutongGames.PlayMaker.Actions;
using HutongGames.PlayMakerEditor;

[CustomActionEditor(typeof(AddFSMTemplateToGameObject))]
public class AddFSMTemplateToGameObjectEditor : CustomActionEditor
{
public override void OnEnable()
{
}

public override bool OnGUI()
{
var isDirty = false;
var action = target as AddFSMTemplateToGameObject;

GUILayout.Label("Select the GameObject and the FSM Template to Add:", EditorStyles.boldLabel);

EditField("_GameObject");
EditField("_FSMTemplate");
GUILayout.Space(3);
EditField("_FSMName");
GUILayout.Space(3);
EditField("_StoreComponent");

GUILayout.Space(5);
GUILayout.Label("Access to Optionals setup", EditorStyles.boldLabel);
EditField("_EnableOptions");
if (action._EnableOptions.Value)
{
GUILayout.Space(3);
EditField("active");
EditField("unique");
EditField("replace");
EditField("variableNames");
EditField("variables");
EditField("existsEventTarget");
EditField("existsSendEvent");
EditField("doNotDestroy");
EditField("replaceEventTarget");
EditField("replaceSendEvent");
}

GUILayout.Space(5);
GUILayout.Label("Access to Events Overrides to allow system events during build runtime", EditorStyles.boldLabel);
EditField("_EnableRuntimeEventsOverride");
if (action._EnableRuntimeEventsOverride.Value)
{
GUILayout.Space(3);
EditField("_MouseEvents");
EditField("_ApplicationEvents");
EditField("_TriggerEnter");
EditField("_TriggerExit");
EditField("_TriggerStay");
EditField("_CollisionEnter");
EditField("_CollisionExit");
EditField("_CollisionStay");
EditField("_LateUpdate");
EditField("_FixedUpdate");
}
return isDirty || GUI.changed;
}
}

Action: AddFSMTemplateToGameObject.cs
Code: [Select]
// action created by Jeremy Sebagh - 20200720
// modified by Jeremy Sebagh - 20200725
// added stuff from action by Krileon: https://hutonggames.com/playmakerforum/index.php?topic=5819.msg28493#msg28493


using UnityEngine;
using System.Collections.Generic;

namespace HutongGames.PlayMaker.Actions
{
[ActionCategory("0MyCustomActions")]
[Tooltip("Add a FSM Template to a GameObject")]
public class AddFSMTemplateToGameObject : FsmStateAction
{
[RequiredField]
[CheckForComponent(typeof(GameObject))]
[Tooltip("GameObject")]
public FsmOwnerDefault _GameObject;

[RequiredField]
[CheckForComponent(typeof(FsmTemplate))]
[Tooltip("FSM Template")]
public FsmTemplate _FSMTemplate;

[Tooltip("Enable Options")]
public FsmBool _EnableOptions;

[Tooltip("FSM Name")]
public FsmString _FSMName;

[Tooltip("Store Component")]
[ObjectType(typeof(PlayMakerFSM)), UIHint(UIHint.Variable)]
public FsmObject _StoreComponent;
// =====================================
// Code from Krileon Action
// =====================================
public FsmBool active;
public FsmBool unique;
public FsmBool replace;
[CompoundArray("Variables", "Name", "Variable")]
[RequiredField]
public FsmString[] variableNames;
[RequiredField]
public FsmVar[] variables;
// [ObjectType(typeof(PlayMakerFSM)), UIHint(UIHint.Variable)]
// public FsmObject storeComponent;
[ActionSection("Exists Event")]
public FsmEventTarget existsEventTarget;
public FsmString existsSendEvent;
[ActionSection("Replace Event")]
public FsmBool doNotDestroy;
public FsmEventTarget replaceEventTarget;
public FsmString replaceSendEvent;
// =====================================
// End Code from Krileon Action
// =====================================

private GameObject previousGo;
private List<PlayMakerFSM> fsms;


[Tooltip("Enable Events Override")]
public FsmBool _EnableRuntimeEventsOverride;

[Tooltip("Mouse Events")]
public FsmBool _MouseEvents;

[Tooltip("Application Events")]
public FsmBool _ApplicationEvents;

[Tooltip("Trigger Enter")]
public FsmBool _TriggerEnter;

[Tooltip("Trigger Exit")]
public FsmBool _TriggerExit;

[Tooltip("Trigger Stay")]
public FsmBool _TriggerStay;

[Tooltip("Collision Enter")]
public FsmBool _CollisionEnter;

[Tooltip("Collision Exit")]
public FsmBool _CollisionExit;

[Tooltip("Collision Stay")]
public FsmBool _CollisionStay;

[Tooltip("Collision Stay")]
public FsmBool _LateUpdate;

[Tooltip("Collision Stay")]
public FsmBool _FixedUpdate;

public override void Reset()
{
_GameObject = null;
_FSMTemplate = null;
_EnableOptions = null;
_FSMName = null;
_StoreComponent = null;
_EnableRuntimeEventsOverride = null;
_MouseEvents = false;
_ApplicationEvents = false;
_TriggerEnter = false;
_TriggerExit = false;
_TriggerStay = false;
_CollisionEnter = false;
_CollisionExit = false;
_CollisionStay = false;
_LateUpdate = false;
_FixedUpdate = false;

// =====================================
// Code from Krileon Action
// =====================================
active = new FsmBool { Value = true };
unique = new FsmBool { Value = false };
replace = new FsmBool { Value = false };
variableNames = new FsmString[0];
variables = new FsmVar[0];
existsEventTarget = null;
existsSendEvent = null;
doNotDestroy = new FsmBool { Value = false };
replaceEventTarget = null;
replaceSendEvent = null;
// =====================================
// End Code from Krileon Action
// =====================================
}
public override void OnEnter()
{
GameObject gameObject = Fsm.GetOwnerDefaultTarget(_GameObject);
if (gameObject == null)
{
Finish();
return;
}

PlayMakerFSM FSMTemplate = null;

if (_EnableOptions.Value)
{
// ==============================================================
// Modified Code from Krileon Action (only some variable names)
// ==============================================================

bool exists = false;

if ((!unique.Value) || replace.Value)
{
if (gameObject != previousGo)
{
fsms = new List<PlayMakerFSM>();

fsms.AddRange(gameObject.GetComponents<PlayMakerFSM>());

previousGo = gameObject;
}

if (fsms.Count > 0) foreach (PlayMakerFSM fsm in fsms)
{
if (((_FSMName.Value != "") && (fsm.FsmName == _FSMName.Value)) || ((fsm.FsmTemplate != null) && (fsm.FsmTemplate.name == _FSMTemplate.name)))
{
if (replace.Value)
{
if (replaceSendEvent.Value != "")
{
Fsm.Event(replaceEventTarget, replaceSendEvent.Value);
}

if (!doNotDestroy.Value)
{
Object.Destroy(fsm);
}
}
else
{
_StoreComponent.Value = fsm;
exists = true;
}
}
}
}

if (!exists)
{
FSMTemplate = gameObject.AddComponent<PlayMakerFSM>();

if (_FSMName.Value != "")
{
FSMTemplate.FsmName = _FSMName.Value;
}

if ((!active.Value) || (variableNames.Length > 0))
{
FSMTemplate.enabled = false;
}

FSMTemplate.SetFsmTemplate(_FSMTemplate);

if (variableNames.Length > 0)
{
if (variableNames.Length > 0) for (int i = 0; i < variableNames.Length; i++)
{
if (!variableNames[i].IsNone)
{
NamedVariable target = FSMTemplate.Fsm.Variables.GetVariable(variableNames[i].Value);

if (target != null)
{
variables[i].ApplyValueTo(target);
}
}
}

if (active.Value && (!FSMTemplate.enabled))
{
FSMTemplate.enabled = true;
}
}

if ((!unique.Value) || replace.Value)
{
fsms.Add(FSMTemplate);
}

_StoreComponent.Value = FSMTemplate;
}
else
{
if (existsSendEvent.Value != "")
{
Fsm.Event(existsEventTarget, existsSendEvent.Value);
}
}
// =====================================
// End Modified Code from Krileon Action
// =====================================
}
else
{
FSMTemplate = gameObject.AddComponent<PlayMakerFSM>();
FSMTemplate.SetFsmTemplate(_FSMTemplate);
FSMTemplate.FsmName = _FSMName.Value;
_StoreComponent.Value = FSMTemplate;
}

if (_EnableRuntimeEventsOverride.Value)
{
bool exists = false;

if (_MouseEvents.Value)
{
exists = false;
FSMTemplate.Fsm.MouseEvents = _MouseEvents.Value;
PlayMakerMouseEvents thisMouseEvent;
if (gameObject.GetComponent<PlayMakerMouseEvents>() != null)
{
List<PlayMakerMouseEvents> CheckMouseEvents = new List<PlayMakerMouseEvents>();
CheckMouseEvents.AddRange(gameObject.GetComponents<PlayMakerMouseEvents>());
foreach (PlayMakerMouseEvents MouseEvent in CheckMouseEvents)
{
if (MouseEvent.TargetFSMs.Contains(FSMTemplate))
{
exists = true;
break;
}
}

if (!exists) { thisMouseEvent = gameObject.GetComponent<PlayMakerMouseEvents>(); } else { thisMouseEvent = null; }
}
else
{
thisMouseEvent = gameObject.AddComponent<PlayMakerMouseEvents>();
}
if (thisMouseEvent) thisMouseEvent.TargetFSMs.Add(FSMTemplate);
}
if (_ApplicationEvents.Value)
{
exists = false;
FSMTemplate.Fsm.HandleApplicationEvents = _ApplicationEvents.Value;
PlayMakerApplicationEvents thisApplicationEvent;
if (gameObject.GetComponent<PlayMakerApplicationEvents>() != null)
{
List<PlayMakerApplicationEvents> CheckApplicationEvents = new List<PlayMakerApplicationEvents>();
CheckApplicationEvents.AddRange(gameObject.GetComponents<PlayMakerApplicationEvents>());
foreach (PlayMakerApplicationEvents ApplicationEvent in CheckApplicationEvents)
{
if (ApplicationEvent.TargetFSMs.Contains(FSMTemplate))
{
exists = true;
break;
}
}

if (!exists) { thisApplicationEvent = gameObject.GetComponent<PlayMakerApplicationEvents>(); } else { thisApplicationEvent = null; }
}
else
{
thisApplicationEvent = gameObject.AddComponent<PlayMakerApplicationEvents>();
}
if (thisApplicationEvent) thisApplicationEvent.TargetFSMs.Add(FSMTemplate);
}
if (_TriggerEnter.Value)
{
exists = false;
FSMTemplate.Fsm.HandleTriggerEnter = _TriggerEnter.Value;
PlayMakerTriggerEnter thisTriggerEnter;
if (gameObject.GetComponent<PlayMakerTriggerEnter>() != null)
{
List<PlayMakerTriggerEnter> CheckTriggerEnter = new List<PlayMakerTriggerEnter>();
CheckTriggerEnter.AddRange(gameObject.GetComponents<PlayMakerTriggerEnter>());
foreach (PlayMakerTriggerEnter TriggerEnter in CheckTriggerEnter)
{
if (TriggerEnter.TargetFSMs.Contains(FSMTemplate))
{
exists = true;
break;
}
}

if (!exists) { thisTriggerEnter = gameObject.GetComponent<PlayMakerTriggerEnter>(); } else { thisTriggerEnter = null; }
}
else
{
thisTriggerEnter = gameObject.AddComponent<PlayMakerTriggerEnter>();
}
if (thisTriggerEnter) thisTriggerEnter.TargetFSMs.Add(FSMTemplate);
}
if (_TriggerExit.Value)
{
exists = false;
FSMTemplate.Fsm.HandleTriggerExit = _TriggerExit.Value;
PlayMakerTriggerExit thisTriggerExit;
if (gameObject.GetComponent<PlayMakerTriggerExit>() != null)
{
List<PlayMakerTriggerExit> CheckTriggerExit = new List<PlayMakerTriggerExit>();
CheckTriggerExit.AddRange(gameObject.GetComponents<PlayMakerTriggerExit>());
foreach (PlayMakerTriggerExit TriggerExit in CheckTriggerExit)
{
if (TriggerExit.TargetFSMs.Contains(FSMTemplate))
{
exists = true;
break;
}
}

if (!exists) { thisTriggerExit = gameObject.GetComponent<PlayMakerTriggerExit>(); } else { thisTriggerExit = null; }
}
else
{
thisTriggerExit = gameObject.AddComponent<PlayMakerTriggerExit>();
}
if (thisTriggerExit) thisTriggerExit.TargetFSMs.Add(FSMTemplate);
}
if (_TriggerStay.Value)
{
exists = false;
FSMTemplate.Fsm.HandleTriggerStay = _TriggerStay.Value;
PlayMakerTriggerStay thisTriggerStay;
if (gameObject.GetComponent<PlayMakerTriggerStay>() != null)
{
List<PlayMakerTriggerStay> CheckTriggerStay = new List<PlayMakerTriggerStay>();
CheckTriggerStay.AddRange(gameObject.GetComponents<PlayMakerTriggerStay>());
foreach (PlayMakerTriggerStay TriggerStay in CheckTriggerStay)
{
if (TriggerStay.TargetFSMs.Contains(FSMTemplate))
{
exists = true;
break;
}
}

if (!exists) { thisTriggerStay = gameObject.GetComponent<PlayMakerTriggerStay>(); } else { thisTriggerStay = null; }
}
else
{
thisTriggerStay = gameObject.AddComponent<PlayMakerTriggerStay>();
}
if (thisTriggerStay) thisTriggerStay.TargetFSMs.Add(FSMTemplate);
}
if (_CollisionEnter.Value)
{
exists = false;
FSMTemplate.Fsm.HandleCollisionEnter = _CollisionEnter.Value;
PlayMakerCollisionEnter thisCollisionEnter;
if (gameObject.GetComponent<PlayMakerCollisionEnter>() != null)
{
List<PlayMakerCollisionEnter> CheckCollisionEnter = new List<PlayMakerCollisionEnter>();
CheckCollisionEnter.AddRange(gameObject.GetComponents<PlayMakerCollisionEnter>());
foreach (PlayMakerCollisionEnter CollisionEnter in CheckCollisionEnter)
{
if (CollisionEnter.TargetFSMs.Contains(FSMTemplate))
{
exists = true;
break;
}
}

if (!exists) { thisCollisionEnter = gameObject.GetComponent<PlayMakerCollisionEnter>(); } else { thisCollisionEnter = null; }
}
else
{
thisCollisionEnter = gameObject.AddComponent<PlayMakerCollisionEnter>();
}
if (thisCollisionEnter) thisCollisionEnter.TargetFSMs.Add(FSMTemplate);
}
if (_CollisionExit.Value)
{
exists = false;
FSMTemplate.Fsm.HandleCollisionExit = _CollisionExit.Value;
PlayMakerCollisionExit thisCollisionExit;
if (gameObject.GetComponent<PlayMakerCollisionExit>() != null)
{
List<PlayMakerCollisionExit> CheckCollisionExit = new List<PlayMakerCollisionExit>();
CheckCollisionExit.AddRange(gameObject.GetComponents<PlayMakerCollisionExit>());
foreach (PlayMakerCollisionExit CollisionExit in CheckCollisionExit)
{
if (CollisionExit.TargetFSMs.Contains(FSMTemplate))
{
exists = true;
break;
}
}

if (!exists) { thisCollisionExit = gameObject.GetComponent<PlayMakerCollisionExit>(); } else { thisCollisionExit = null; }
}
else
{
thisCollisionExit = gameObject.AddComponent<PlayMakerCollisionExit>();
}
if (thisCollisionExit) thisCollisionExit.TargetFSMs.Add(FSMTemplate);
}
if (_CollisionStay.Value)
{
exists = false;
FSMTemplate.Fsm.HandleCollisionStay = _CollisionStay.Value;
PlayMakerCollisionStay thisCollisionStay;
if (gameObject.GetComponent<PlayMakerCollisionStay>() != null)
{
List<PlayMakerCollisionStay> CheckCollisionStay = new List<PlayMakerCollisionStay>();
CheckCollisionStay.AddRange(gameObject.GetComponents<PlayMakerCollisionStay>());
foreach (PlayMakerCollisionStay CollisionStay in CheckCollisionStay)
{
if (CollisionStay.TargetFSMs.Contains(FSMTemplate))
{
exists = true;
break;
}
}

if (!exists) { thisCollisionStay = gameObject.GetComponent<PlayMakerCollisionStay>(); } else { thisCollisionStay = null; }
}
else
{
thisCollisionStay = gameObject.AddComponent<PlayMakerCollisionStay>();
}
if (thisCollisionStay) thisCollisionStay.TargetFSMs.Add(FSMTemplate);
}
if (_LateUpdate.Value)
{
exists = false;
FSMTemplate.Fsm.HandleLateUpdate = _LateUpdate.Value;
PlayMakerLateUpdate thisLateUpdate;
if (gameObject.GetComponent<PlayMakerLateUpdate>() != null)
{
List<PlayMakerLateUpdate> CheckLateUpdate = new List<PlayMakerLateUpdate>();
CheckLateUpdate.AddRange(gameObject.GetComponents<PlayMakerLateUpdate>());
foreach (PlayMakerLateUpdate LateUpdate in CheckLateUpdate)
{
if (LateUpdate.TargetFSMs.Contains(FSMTemplate))
{
exists = true;
break;
}
}

if (!exists) { thisLateUpdate = gameObject.GetComponent<PlayMakerLateUpdate>(); } else { thisLateUpdate = null; }
}
else
{
thisLateUpdate = gameObject.AddComponent<PlayMakerLateUpdate>();
}
if (thisLateUpdate) thisLateUpdate.TargetFSMs.Add(FSMTemplate);
}
if (_FixedUpdate.Value)
{
exists = false;
FSMTemplate.Fsm.HandleFixedUpdate = _FixedUpdate.Value;
PlayMakerFixedUpdate thisFixedUpdate;
if (gameObject.GetComponent<PlayMakerFixedUpdate>() != null)
{
List<PlayMakerFixedUpdate> CheckFixedUpdate = new List<PlayMakerFixedUpdate>();
CheckFixedUpdate.AddRange(gameObject.GetComponents<PlayMakerFixedUpdate>());
foreach (PlayMakerFixedUpdate FixedUpdate in CheckFixedUpdate)
{
if (FixedUpdate.TargetFSMs.Contains(FSMTemplate))
{
exists = true;
break;
}
}

if (!exists) { thisFixedUpdate = gameObject.GetComponent<PlayMakerFixedUpdate>(); } else { thisFixedUpdate = null; }
}
else
{
thisFixedUpdate = gameObject.AddComponent<PlayMakerFixedUpdate>();
}
if (thisFixedUpdate) thisFixedUpdate.TargetFSMs.Add(FSMTemplate);
}
}
Finish();
}
}
}

edit: I posted it on the bad topic , it's corrected ;D
edit2: Warning Correction
edit3: New Correction