playMaker

Author Topic: Go To Any State action  (Read 52834 times)

ClaudioFreda

  • Playmaker Newbie
  • *
  • Posts: 9
Go To Any State action
« on: February 19, 2014, 08:09:05 PM »
First of all, thanks for this great tool. My team is finding it ever-useful and I finally can talk game logic with the designers!

I'm having the need to go to a state by name.

Basically it's the same as the "Go to previous" action, but I need to store the state name in a variable and then switch it later.

This is because I have multiple global transitions going on at the same time, with states that last more than one frame, and I need to record the state where each of the global transitions was sent.

It would be very simple for me to code, but unfortunately the Fsm.SwitchState method is set to private.

This is what the action would look like if the method was public (does not work in current Playmaker):

Code: [Select]
using UnityEngine;

namespace HutongGames.PlayMaker.Actions
{
[ActionCategory(ActionCategory.StateMachine)]
[Tooltip("Immediately switch to a state with the selected name.")]
public class GotoStateByName : FsmStateAction
{

[RequiredField]
FsmString stateName;

public override void Reset()
{
stateName = null;
}

public override void OnEnter()
{
Log("Goto State: " + stateName.Value);

Fsm.SwitchState(stateName.Value);

Finish();
}
}
}

Just changing this SwitchState method to public in the next update would help.

(in the meantime I'm using and ugly workaround but having the real thing would be awesome)
« Last Edit: February 19, 2014, 08:32:28 PM by ClaudioFreda »

ClaudioFreda

  • Playmaker Newbie
  • *
  • Posts: 9
Re: Go To Any State action
« Reply #1 on: February 20, 2014, 03:34:39 AM »
Ok, I was wrong on one thing.

SwitchState accepts a FsmState parameter, not a String.

Anyway, I was able to access the private method by using reflection. This is the (working in current Playmaker, not tested thoroughly) action.

It would be safer, of course, if SwitchState was public.

Code: [Select]
using UnityEngine;
using System;
using System.Reflection;

namespace HutongGames.PlayMaker.Actions
{
[ActionCategory(ActionCategory.StateMachine)]
[Tooltip("Immediately switch to a state with the selected name.")]
public class GotoStateByName : FsmStateAction
{

[RequiredField]
public FsmString stateName;

public override void Reset()
{
stateName = null;
}

public override void OnEnter()
{

FsmState targetState = null;
foreach (FsmState state in Fsm.States)
{
if (state.Name == stateName.Value)
{
targetState = state;
break;
}
}


if (targetState != null)
{
MethodInfo switchState = Fsm.GetType().GetMethod("SwitchState", BindingFlags.NonPublic | BindingFlags.Instance);
Log("Goto State: " + stateName.Value);
switchState.Invoke(Fsm, new object[] { targetState });
}
else LogError("State Switch: State does not exist.");

Finish();
}
}
}
« Last Edit: February 20, 2014, 03:44:11 AM by ClaudioFreda »

Tibo

  • Playmaker Newbie
  • *
  • Posts: 5
Re: Go To Any State action
« Reply #2 on: November 01, 2014, 04:32:19 PM »
Thank you for this action.
I am using also global events for transitionning, as I find it more readable and this action is exactly what I needed.

jeanfabre

  • Administrator
  • Hero Member
  • *****
  • Posts: 15622
  • Official Playmaker Support
Re: Go To Any State action
« Reply #3 on: November 11, 2014, 02:15:52 AM »
Hi,

 ok, I reworked this action a bit and it's now available on the ecosystem, you can download it directly as well here.

 This action will now allow you to target a different fsm on a different gamobject, as well as sending event if state was found or not.

Bye,

 Jean

Nairobiette

  • Playmaker Newbie
  • *
  • Posts: 5
Re: Go To Any State action
« Reply #4 on: May 02, 2024, 11:18:02 AM »
Heyo, it seems that this action no longer works. No matter what I do, I always end up with a "Object reference not set to an instance of an object"...

craigz

  • Beta Group
  • Full Member
  • *
  • Posts: 235
    • Haven Made
Re: Go To Any State action
« Reply #5 on: March 06, 2026, 02:19:37 AM »
Updated the action, I use this quite often for things like upgrade managers, was surprised to find it MIA on ecosystem and not functional!  :o




Code: [Select]
// (c) Copyright HutongGames, LLC 2010-2014. All rights reserved.
/*--- __ECO__ __ACTION__ ---*/
// Updated for newer Unity / PlayMaker compatibility.

using UnityEngine;
using System;
using System.Reflection;

namespace HutongGames.PlayMaker.Actions
{
[ActionCategory(ActionCategory.StateMachine)]
[Tooltip("Immediately switch to a state with the selected name.")]
public class GoToStateByName : FsmStateAction
{
[RequiredField]
[Tooltip("The GameObject that owns the FSM")]
public FsmOwnerDefault gameObject;

[UIHint(UIHint.FsmName)]
[Tooltip("Name of FSM on Game Object. Leave to none to target this FSM.")]
public FsmString fsmName;

[RequiredField]
[Tooltip("The name of the state to go to")]
public FsmString stateName;

[Tooltip("Event sent if the state was found and the switch was attempted.")]
public FsmEvent stateFoundEvent;

[Tooltip("Event sent if the state was not found, the FSM was not found, or the switch method could not be resolved.")]
public FsmEvent stateNotFoundEvent;

public override void Reset()
{
gameObject = null;
fsmName = new FsmString { UseVariable = true };
stateName = null;
stateFoundEvent = null;
stateNotFoundEvent = null;
}

public override void OnEnter()
{
DoGotoState();
Finish();
}

private void DoGotoState()
{
var go = Fsm.GetOwnerDefaultTarget(gameObject);
if (go == null)
{
Fsm.Event(stateNotFoundEvent);
return;
}

Fsm sourceFsm = Fsm;

if (!fsmName.IsNone && !string.IsNullOrEmpty(fsmName.Value))
{
PlayMakerFSM playMakerFsm = ActionHelpers.GetGameObjectFsm(go, fsmName.Value);
if (playMakerFsm == null || playMakerFsm.Fsm == null)
{
Debug.LogWarning(
"GoToStateByName: Could not find FSM '" + fsmName.Value + "' on GameObject '" + go.name + "'.",
go);
Fsm.Event(stateNotFoundEvent);
return;
}

sourceFsm = playMakerFsm.Fsm;
}

if (sourceFsm == null)
{
Debug.LogWarning("GoToStateByName: sourceFsm is null.");
Fsm.Event(stateNotFoundEvent);
return;
}

if (stateName == null || string.IsNullOrEmpty(stateName.Value))
{
Debug.LogWarning("GoToStateByName: stateName is null or empty.");
Fsm.Event(stateNotFoundEvent);
return;
}

FsmState targetState = null;
foreach (FsmState state in sourceFsm.States)
{
if (state != null && state.Name == stateName.Value)
{
targetState = state;
break;
}
}

if (targetState == null)
{
Debug.LogWarning(
"GoToStateByName: State '" + stateName.Value + "' was not found in FSM '" + sourceFsm.Name + "'.",
go);
Fsm.Event(stateNotFoundEvent);
return;
}

if (TrySwitchState(sourceFsm, targetState))
{
Fsm.Event(stateFoundEvent);
}
else
{
Debug.LogError(
"GoToStateByName: Could not resolve a compatible SwitchState method on PlayMaker FSM '" +
sourceFsm.Name + "'.", go);
Fsm.Event(stateNotFoundEvent);
}
}

private static bool TrySwitchState(Fsm sourceFsm, FsmState targetState)
{
if (sourceFsm == null || targetState == null)
{
return false;
}

Type fsmType = sourceFsm.GetType();

BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

// Most likely signature.
MethodInfo switchStateMethod = fsmType.GetMethod(
"SwitchState",
flags,
null,
new Type[] { typeof(FsmState) },
null);

if (switchStateMethod != null)
{
switchStateMethod.Invoke(sourceFsm, new object[] { targetState });
return true;
}

// Fallback: find any SwitchState overload that accepts one parameter assignable from FsmState.
MethodInfo[] methods = fsmType.GetMethods(flags);
for (int i = 0; i < methods.Length; i++)
{
MethodInfo method = methods[i];
if (method.Name != "SwitchState")
{
continue;
}

ParameterInfo[] parameters = method.GetParameters();
if (parameters.Length != 1)
{
continue;
}

if (parameters[0].ParameterType.IsAssignableFrom(typeof(FsmState)))
{
method.Invoke(sourceFsm, new object[] { targetState });
return true;
}
}

return false;
}
}
}