This page is a repository of interesting answers provided by Playmaker's author in the official Unity forums.
Finite State Machines
An FSM is designed to be a self contained behavior controlled entirely by Events and Variables. The Events can come from other FSMs or scripts, it doesn't care - think of the FSM as a black box that has a control panel with Event buttons and Variable sliders etc.
Logic Actions
You can use Float/Int/Bool Compare to send events based on the value of a variable.
You can use set event data to send data with an event. Inside the same FSM you don't need to - any state can see the variable. And you can get/set variables directly on other FSMs using the Get/Set State Machine Actions.
The first thing you need to do with an fsm is determine the discrete states you need. In your space ship example, you may only need one state: ControlSpaceShip: GetAxis + AddForce actions. Remember states aren't functions that you call, they're more like a mode that the object is in... and events trigger transitions to different modes.
So if you decide that colliding with an object temporarily disables the controls, you can add a Disabled state: Play Sound (power down sound fx) + Create Object (particle effects) + Wait. Now you can return to the ControlSpaceShip state on FINISHED. Or to a PowerUp state and then back to ControlSpaceShip...
Or you add a fire button. You can get the button event in ControlSpaceShip state and send an event to transition to a FireBullet state. FireBullet plays a sound effect, spawns an object, gives it a velocity, then returns immediately to ControlSpaceShip - all in the same frame.
Controls
Q. For example, if I'm trying to make player controls for a space ship, I would use the GetAxes action and store the result in a float variable. But how to I send an event to another state that actually makes the space ship move?
A. You could use the GetAxisVector Action, store the Vector in a Vector3 variable (eg movement). Then get a Translate Action and use for Vector the Vector3 variable (movement) and set the Space to World. Set the Per Second on. Put all this in one State.
Follow
Q. how to Smooth follow an object? The Transform Action causes a jittery movement.
A. MoveTowards in combination with SmoothLookAt works well. You could do it "manually" with Vector3 Intepolate with EaseInOut. SmoothLookAt will work well for smooth rotations.
TRIGGER ENTER
Sent when the owner of the fsm enters ANY trigger. Any ALL CAPS events are sent automatically by the system. Most of the time you shouldn't send them yourself from actions unless you're sure its what you want...
In your example, you should make a custom event (e.g., hitPlatform) and send that when you collide with the platform.
Alternatively, in State2 you could use Get Trigger Info, and decide what to do based on the game object hit. For example you could use Tag Switch to do different things based on the Tag of the trigger.
It depends if you have lots of different trigger tags and want to do different things for each, or if you're only interested in collision with one trigger tag...
Accessing External Scripts (Behaviors):
Here's a very simple example. Your behaviour:
Code:
using UnityEngine;
public class MyBehavior : MonoBehaviour
{
public float testFloat;
}
A custom action that accesses MyBehavior:
Code:
namespace HutongGames.PlayMaker.Actions
{
[ActionCategory(ActionCategory.ScriptControl)]
[Tooltip("Shows how to interface with a behavior on a game object")]
public class MyBehaviorAction : FsmStateAction
{
public MyBehavior myBehavior;
[UIHint(UIHint.Variable)]
public FsmFloat getTestFloat;
public override void OnUpdate()
{
getTestFloat.Value = myBehavior.testFloat;
}
}
}
Drag the an object with MyBehavior onto the action. Assign a float variable to Get Test Float and turn on Debug. Hit play and change testFloat and the fsm variable should update.
Now other actions can use the variable that you got from MyBehavior.
Accessing a FSM from a custom script:
Code:
using UnityEngine;
using HutongGames.PlayMaker;
public class UseFSM : MonoBehaviour
{
public PlayMakerFSM behavior;
void Update ()
{
// getting named fsm variables
FsmFloat test = behavior.FsmVariables.GetFsmFloat("test");
// setting named fsm variables
test.Value = 0.5f;
// sending events
behavior.Fsm.Event("test");
}
}
With this example, you would just drag a PlayMakerFSM component onto the Behavior field then access its variables as shown.
Execution Flow
Internally actions have 3 main phases: OnEnter, OnUpdate, and OnExit.
You can transition through multiple states in OnEnter all in the same frame, but at some point you have to let an update happen. You can use a NextFrameEvent action in a loop to do this...
Or (more commonly) you should organize the FSM so each state has it's own actions (On state + On actions and Off state + Off actions).
Mouse Actions
Q.: I'm trying to create a simple action where the player can click and drag a cube in the x-y plane with an orthographic camera.
A.: GetMouseX/Y gets the screen coordinates of the mouse. You probably want the mouse deltas, which you can get using GetAxis with "Mouse X" and "Mouse Y" (see unity docs). Then use Translate to move the cube.
You might also look at MousePick to get the world coordinates where you clicked.
If you don't hit an object the Point would probably reset to (0,0,0). You can test if you hit an object with Did Pick Object... or make sure you're always hitting an object (fill the field of view with pickable objects).
GUI Actions
Normalized in GUI actions means screen coordinates are defined as 0-1 instead of in pixels. E.g., 0 = screen left, 1 = screen right. The idea is to be resolution independent. If you uncheck normalized then coordinates are in pixels, which can be fine if you have a fixed target resolution, or if you're doing some math on them.
Damage/Health System
Arrange things to try and keep each FSM as independent as possible (i.e., not having to know each other's variable names). For example:
The Health FSM waits for a Hit event in an Idle state. When it gets Hit, it uses Get Event Info to see how much it should subtract from health. After subtracting this amount it returns to Idle.
Other objects can use Set Event Data to define how much damage they do, and then Send Event To FSM to send a Hit event to the Health FSM.
There are other ways to do this of course. But in general you want to reduce the amount that FSMs need to know about each other's internal workings. This makes them easier to maintain and more portable across projects.
Targeting other objects
In Unity:
- A scene object can reference other scene objects & prefabs
- A prefab can only references other prefabs and can not reference scene objects (even though it looks like it can, that reference will be lost on save/load)
In general this means you should use Find Game Object, e.g., in the start state so you only do it once, then use that as the target in Send Event To FSM. Or if an event is more widely interesting you could use Broadcast Event.
Debugging
Whenever variables aren't behaving the way you'd expect, use the Debug check box in the State Inspector to examine their values at runtime.
Sending an Animation Event to an FSM
Use the function SendEvent and pass the event name as a string. For a related example see Sending Animation Events to an FSM. It's basically the same idea with SendMessage.