PlayMaker Feedback > Feature Requests

Inter FSM event list and FSM -> Message with method call please?

<< < (2/4) > >>

KJIB:
OK, I've been trying to improve things a bit (with a windowed version) which I thought I'd done and then I tried it in project with lots of FSMs and where I found that I then have a problem where stuff is not initialised. Due to that, I put this line of code in place:


--- Code: ---if (fsm.FsmStates[s].IsInitialized == true)
--- End code ---

before doing this:


--- Code: ---int numActions = fsm.FsmStates[s].Actions.Length;
--- End code ---

The top line of code stopped the problems in that second line of code but then when I went back I found that I got no results because the top line is always returning false.

I'd put that top line in because where as I wasn't getting problems in the initial project I did with the larger project. In the small project it does not seem to matter that things are not initialized. How should I be getting the actions for a given state please (i.e. in a way that they will be initialized etc.)?

Thank you.

KJIB:
OK, things seem to be fine now.

The tool now pops a window up with quick refresh filters etc. and you can also get the output in the console still by pressing the inspector button (when you have the PlayMakerCommsTool object selected).

If you get errors when you start the tool, close the window it pops up, start the Playmaker editor (from the Playmaker menu) and then try again.

I have moved the tool so it's now under the Playmaker->Tools menu as "Windowed Comm Tool".

There are now 3 files, 2 go in the Assets/Editor folder and the other not in there but some place you like to stick scripts under Assets.

Put this somewhere under Assets (but NOT the Editor folder).
You have to call it "PlayMakerCommsTool.cs"

--- Code: ---using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using HutongGames.PlayMaker;

// Show SendEvent and SendMessage actions in FSMs so you can track down what's going on in multi-FSM designs more easily.
// Put the PlayMakerCommsTool.cs script in with your normal code (NOT in the Assets/Editor directory).
// Put the PlayMakerCommsToolEditor and PlayMakerCommsToolWindow in the Assets/Editor directory.
//
// To use:
// Once the code is in the right place you will find a new menu item "Comm Tool Window" under
// the Playmaker Tools menu. Click this, you only need to do it once in a scene. Then when
// you want to see the details for SendEvent and SendMessage select the
// "PlayMakerCommsTool" from the Hierarchy and then in the inspector click the
// "Re-output Playmaker Comms (to Console)" button. Now you should see a new entry
// added to the Console, click it to get more detail. If you change stuff and want
// to see the detail just click the button again. You can delete the PlayMakerCommsTool
// object from your Hierarchy when finished.


public class PlayMakerCommsTool : MonoBehaviour {

public bool ShowSendMessage = true;
public string FilterMethodName = "";
public bool ShowSendEvent = true;
public bool EventMessageTogether = false;
public string FilterStateName = "";
public string FilterFsmName = "";

// Results in a crude string form
private string results;

// Results in a list - if you want to stick them in a nice tree control or something.
public List<PlaymakerCommsEntry> commsList = null;

[HideInInspector]
public int numFsms = 0;

public void Rebuild(bool showInConsole){
// An array of all FSMs
PlayMakerFSM [] fsms = FindObjectsOfType(typeof(PlayMakerFSM)) as PlayMakerFSM[];
commsList = null;
numFsms = 0;

if (fsms != null)
{
results = "PlayMakerCommsTool: " + fsms.Length + " FSMs found. CLICK THIS in top panel to expand in bottom panel.\n";

commsList = new List<PlaymakerCommsEntry>();

numFsms = fsms.Length;
int ourFsmIndex = 0;
foreach(PlayMakerFSM fsm in fsms)
{
if ((FilterFsmName.Length > 0) && (fsm.name.Contains(FilterFsmName) == false)) continue;

if (fsm.FsmStates != null) fsm.FsmStates.Initialize();

for (int s = 0; s<fsm.FsmStates.Length; ++s)
{
if ((FilterStateName.Length > 0) && (fsm.FsmStates[s].Name.Contains(FilterStateName) == false)) continue;

if (EventMessageTogether == true)
{
// Show SendEvent and SendMessage as we find them
foreach(FsmStateAction action in fsm.FsmStates[s].Actions)
{
if (ShowSendEvent == true) HandleSendEvent(ourFsmIndex, fsm, fsm.FsmStates[s], action);
if (ShowSendMessage == true) HandleSendMessage(ourFsmIndex, fsm, fsm.FsmStates[s], action);
}
}
else
{
// Show the SendEvent and then the SendMessage text
if (ShowSendEvent == true)
{
int numActions = fsm.FsmStates[s].Actions.Length;
for (int actionNum=0; actionNum < numActions; ++actionNum)
{
//foreach(FsmStateAction action in fsm.FsmStates[s].Actions) HandleSendEvent(ourFsmIndex, fsm, fsm.FsmStates[s], action);
HandleSendEvent(ourFsmIndex, fsm, fsm.FsmStates[s], fsm.FsmStates[s].Actions[actionNum]);
}
}

if (ShowSendMessage == true)
{
foreach(FsmStateAction action in fsm.FsmStates[s].Actions) HandleSendMessage(ourFsmIndex, fsm, fsm.FsmStates[s], action);
}
}
}
++ourFsmIndex;
}

// Show what filters affected the output
if (FilterMethodName.Length > 0) results += "Filter Method Name = " + FilterMethodName + "\n";
if (FilterStateName.Length > 0) results += "Filter State Name = " + FilterStateName + "\n";
if (FilterFsmName.Length > 0) results += "Filter FSM Name = " + FilterFsmName + "\n";
if ((ShowSendMessage == false) && (ShowSendEvent == false)) results += "Unable to show anything useful with both ShowSendMessage false and ShowSendEvent false!\n";
if (showInConsole == true)
{
Debug.Log(results);
}
}
else
{
if (showInConsole == true)
{
Debug.Log("PlayMakerCommsTool: No FSMs found");
}
}
}

private void HandleSendMessage(int ourFsmIndex, PlayMakerFSM fsm, FsmState state, FsmStateAction action)
{
if (action.GetType() == typeof(HutongGames.PlayMaker.Actions.SendMessage))
{
HutongGames.PlayMaker.Actions.SendMessage sm = (HutongGames.PlayMaker.Actions.SendMessage)(action);
string funcName = sm.functionCall.FunctionName;
GameObject targetGO = sm.Fsm.GetOwnerDefaultTarget(sm.gameObject);
string gameObjectName = ((targetGO != null) ? targetGO.name : "UNKNOWN");

if ((FilterMethodName.Length > 0) && (funcName.Contains(FilterMethodName) == false)) return;

results += "SendMessage " + fsm.ToString() + " State " + state.Name + " calls method " + funcName + " on GameObject " + gameObjectName +"\n";

commsList.Add(new PlaymakerCommsEntry(ourFsmIndex, fsm, state, action));
}
}

private void HandleSendEvent(int ourFsmIndex, PlayMakerFSM fsm, FsmState state, FsmStateAction action)
{
if (action.GetType() == typeof(HutongGames.PlayMaker.Actions.SendEvent))
{
HutongGames.PlayMaker.Actions.SendEvent se = (HutongGames.PlayMaker.Actions.SendEvent)(action);
results += "SendEvent " + fsm.ToString() + " State " + state.Name + " sends " + se.sendEvent.Name +
" to (FSM called) " + se.eventTarget.fsmName +
" on GameObject " + se.eventTarget.gameObject.GameObject.ToString() +
"\n";
commsList.Add(new PlaymakerCommsEntry(ourFsmIndex, fsm, state, action));
}
}
}

public class PlaymakerCommsEntry
{
public bool showMyFsm = true;
public bool showMyState = true;
public bool showMe = true;
public int ourFsmIndex = 0;
public PlayMakerFSM fsm;
public FsmState state;
public FsmStateAction action;

public PlaymakerCommsEntry(int anFsmIndex, PlayMakerFSM an_fsm, FsmState a_state, FsmStateAction an_action)
{
showMe = showMyState = showMyFsm = true;
ourFsmIndex = anFsmIndex;
fsm = an_fsm;
state = a_state;
action = an_action;
}
}
--- End code ---

Put this in the Assets/Editor folder.
You have to call the file "PlayMakerCommsToolEditor.cs"

--- Code: ---using UnityEngine;
using System.Collections;

using UnityEditor;

// Show SendEvent and SendMessage actions in FSMs so you can track down what's going on in multi-FSM designs more easily.
// Put the PlayMakerCommsTool.cs script in with your normal code (NOT in the Assets/Editor directory).
// Put the PlayMakerCommsToolEditor and PlayMakerCommsToolWindow in the Assets/Editor directory.
//
// To use:
// Once the code is in the right place you will find a new menu item "Comm Tool Window" under
// the Playmaker Tools menu. Click this, you only need to do it once in a scene. Then when
// you want to see the details for SendEvent and SendMessage select the
// "PlayMakerCommsTool" from the Hierarchy and then in the inspector click the
// "Re-output Playmaker Comms (to Console)" button. Now you should see a new entry
// added to the Console, click it to get more detail. If you change stuff and want
// to see the detail just click the button again. You can delete the PlayMakerCommsTool
// object from your Hierarchy when finished.

[CustomEditor (typeof (PlayMakerCommsTool))]
public class PlayMakerCommsToolEditor : Editor {
static void Create(){
GameObject gameObject = new GameObject("PlayMakerCommsTool");
PlayMakerCommsTool s = gameObject.AddComponent<PlayMakerCommsTool>();
s.Rebuild(true);
}

public override void OnInspectorGUI ()
{
PlayMakerCommsTool obj;

obj = target as PlayMakerCommsTool;

if (obj == null)
{
return;
}

base.DrawDefaultInspector();
EditorGUILayout.BeginHorizontal ();

// Rebuild the comms lines when user click the Rebuild button
if (GUILayout.Button("Re-output Playmaker Comms (to Console)")){
obj.Rebuild(true);
}
EditorGUILayout.EndHorizontal ();
}

}

--- End code ---

Put this in the Assets/Editor folder too.
You have to call this file "PlayMakerCommsToolWindow".

--- Code: ---using UnityEngine;
using UnityEditor;

// Show SendEvent and SendMessage actions in FSMs so you can track down what's going on in multi-FSM designs more easily.
// Put the PlayMakerCommsTool.cs script in with your normal code (NOT in the Assets/Editor directory).
// Put the PlayMakerCommsToolEditor and PlayMakerCommsToolWindow in the Assets/Editor directory.
//
// To use:
// Once the code is in the right place you will find a new menu item "Comm Tool Window" under
// the Playmaker Tools menu. Click this, you only need to do it once in a scene. Then when
// you want to see the details for SendEvent and SendMessage select the
// "PlayMakerCommsTool" from the Hierarchy and then in the inspector click the
// "Re-output Playmaker Comms (to Console)" button. Now you should see a new entry
// added to the Console, click it to get more detail. If you change stuff and want
// to see the detail just click the button again. You can delete the PlayMakerCommsTool
// object from your Hierarchy when finished.

public class PlayMakerCommsToolWindow : EditorWindow
{

static PlayMakerCommsTool pmCommsTool = null;

// So we can compare to PlayMakerCommsTool
public bool ShowSendMessage = true;
public string FilterMethodName = "";
public bool ShowSendEvent = true;
public bool EventMessageTogether = false;
public string FilterStateName = "";
public string FilterFsmName = "";

// Add menu named "My Window" to the Window menu
[MenuItem ("PlayMaker/Tools/Windowed Comm Tool")]
static void Init ()
{
// Get existing open window or if none, make a new one:
PlayMakerCommsToolWindow window = (PlayMakerCommsToolWindow)EditorWindow.GetWindow (typeof (PlayMakerCommsToolWindow));

if (pmCommsTool == null)
{
pmCommsTool = FindObjectOfType(typeof(PlayMakerCommsTool)) as PlayMakerCommsTool;
if (pmCommsTool == null)
{
GameObject gameObject = new GameObject("PlayMakerCommsTool");
pmCommsTool = gameObject.AddComponent<PlayMakerCommsTool>();
}
}
pmCommsTool.Rebuild(true);
}

// Scroll position
Vector2 scrollPos = Vector2.zero;
void OnGUI ()
{
if (pmCommsTool == null) return;

if ((pmCommsTool.commsList == null) || (pmCommsTool.commsList.Count == 0))
{
GUILayout.Label ("Nothing to show? MAKESURE YOU START Playmaker, from the menu: PlayMaker->Playmaker Editor", EditorStyles.boldLabel);
GUILayout.Label ("Remember; Filters are case sensitive.", EditorStyles.boldLabel);
}

GUILayout.Label ("Playmaker Comms", EditorStyles.boldLabel);

// Remember what was set
ShowSendMessage = pmCommsTool.ShowSendMessage;
FilterMethodName = pmCommsTool.FilterMethodName;
ShowSendEvent = pmCommsTool.ShowSendEvent;
EventMessageTogether = pmCommsTool.EventMessageTogether;
FilterStateName = pmCommsTool.FilterStateName;
FilterFsmName = pmCommsTool.FilterFsmName;


pmCommsTool.FilterFsmName =  EditorGUILayout.TextField ("Filter FSM Name (optional)", pmCommsTool.FilterFsmName);
pmCommsTool.FilterStateName =  EditorGUILayout.TextField ("Filter State Name (optional)", pmCommsTool.FilterStateName);

pmCommsTool.ShowSendMessage = EditorGUILayout.Toggle ("Show Send Message", pmCommsTool.ShowSendMessage);
if (pmCommsTool.ShowSendMessage == true)
{
pmCommsTool.FilterMethodName =  EditorGUILayout.TextField ("Filter Method Name (optional)", pmCommsTool.FilterMethodName);
}
pmCommsTool.ShowSendEvent = EditorGUILayout.Toggle ("Show Send Event", pmCommsTool.ShowSendEvent);
pmCommsTool.EventMessageTogether = EditorGUILayout.Toggle ("Event & Messages together", pmCommsTool.EventMessageTogether);

// Did something change?
bool changed = (
(ShowSendMessage != pmCommsTool.ShowSendMessage) ||
(FilterMethodName != pmCommsTool.FilterMethodName) ||
(ShowSendEvent != pmCommsTool.ShowSendEvent) ||
(EventMessageTogether != pmCommsTool.EventMessageTogether) ||
(FilterStateName != pmCommsTool.FilterStateName) ||
(FilterFsmName != pmCommsTool.FilterFsmName)
);

if (changed)
{
pmCommsTool.Rebuild(false);
}
else
{
if ((pmCommsTool.ShowSendEvent == false) && (pmCommsTool.ShowSendMessage == false))
{
GUILayout.Label ("Nothing useful to show if you set both SendMessage & SendEvent false!", EditorStyles.boldLabel);
}
else
if (GUILayout.Button("Re-generate Playmaker communication data (inc to Console)")){
pmCommsTool.Rebuild(true);
}
}

// The position of the window
Rect windowRect = new Rect(0,0, Screen.width, Screen.height);
// Set up a scroll view
scrollPos = GUI.BeginScrollView (
new Rect(0, 160, position.width, position.height), // NOTE; Magic number for Rect.top in use to position the data, TODO: get the actual position of where there is space below the GUI items all ready put in.
scrollPos,
new Rect (0, 0, 1000, 100000), // TODO: set vertical scroll bar based on size of window it controls!
true,
true
);
// Same code as before - make a window. Only now, it's INSIDE the scrollview
BeginWindows ();
windowRect = GUILayout.Window (1, windowRect, DoWindow, pmCommsTool.numFsms + " FSMs enabled in scene");
EndWindows ();
// Close the scroll view
GUI.EndScrollView ();
}
void DoWindow(int n)
{
if ((FilterMethodName.Length > 0) || (FilterStateName.Length > 0) || (FilterFsmName.Length > 0))
{
GUILayout.Label ("Note: CASE SENSITIVE filter(s) are in use.", EditorStyles.boldLabel);
EditorGUILayout.Space();
}

if ((pmCommsTool.ShowSendEvent == true) && (pmCommsTool.ShowSendMessage == true))
GUILayout.Label ("Only FSMs with a SendMessage or SendEvent here:", EditorStyles.boldLabel);
else
{
if (pmCommsTool.ShowSendEvent == true)
GUILayout.Label ("Only FSMs with a SendEvent action are here:", EditorStyles.boldLabel);
else
if (pmCommsTool.ShowSendMessage == true)
GUILayout.Label ("Only FSMs with a SendMessage action are here:", EditorStyles.boldLabel);
}


int currentFsmIndex = -1;
for (int i=0; i<pmCommsTool.commsList.Count; ++i)
{
if (pmCommsTool.commsList[i].ourFsmIndex > currentFsmIndex)
{
currentFsmIndex = pmCommsTool.commsList[i].ourFsmIndex;

EditorGUILayout.Space();
GUILayout.Label ("(FSM name)= " + pmCommsTool.commsList[i].fsm.name, EditorStyles.boldLabel);

pmCommsTool.commsList[i].showMyFsm = EditorGUILayout.Toggle (
"Show this FSM?",
pmCommsTool.commsList[i].showMyFsm);

if (pmCommsTool.commsList[i].showMyFsm == true)
{
GUILayout.Label ("States with entry:-", EditorStyles.label);
string state = "";
bool showState = true;
for (int ii=i; ii<pmCommsTool.commsList.Count; ++ii)
{
if (pmCommsTool.commsList[ii].ourFsmIndex == currentFsmIndex)
{
if (state != pmCommsTool.commsList[ii].state.Name)
{
state = pmCommsTool.commsList[ii].state.Name;

showState = pmCommsTool.commsList[ii].showMyState = EditorGUILayout.Toggle (
(pmCommsTool.commsList[ii].state.Name + ":"),
pmCommsTool.commsList[ii].showMyState);
}

if (showState == true)
{
if (pmCommsTool.commsList[ii].action.GetType() == typeof(HutongGames.PlayMaker.Actions.SendEvent))
{
HutongGames.PlayMaker.Actions.SendEvent se = (HutongGames.PlayMaker.Actions.SendEvent)(pmCommsTool.commsList[ii].action);

GUILayout.Label (
"SentEvent:" + se.sendEvent.Name +
" to (FSM called) " + se.eventTarget.fsmName +
" on GameObject " + se.eventTarget.gameObject.GameObject.ToString()
, EditorStyles.label);
}
else
if (pmCommsTool.commsList[ii].action.GetType() == typeof(HutongGames.PlayMaker.Actions.SendMessage))
{
HutongGames.PlayMaker.Actions.SendMessage sm = (HutongGames.PlayMaker.Actions.SendMessage)(pmCommsTool.commsList[ii].action);
string funcName = sm.functionCall.FunctionName;
GameObject targetGO = sm.Fsm.GetOwnerDefaultTarget(sm.gameObject);
string gameObjectName = ((targetGO == null) ? "UNKNOWN NAME" : targetGO.name);

GUILayout.Label (
"SendMessage to GameObject " + gameObjectName +
" method=" + funcName
, EditorStyles.label);
}
}
}
else
{
i = ii-1;
break;
}
}
}
}
}
}
}
--- End code ---

I hope you like it and if you don't, feel free to fix / improve stuff.

 :)

KJIB:
The one thing I forgot was to update the "To use" bit in the comments. Now there's a window you don't need to fiddle about so much and it will be clearer what to do (I hope).

I guess that the "To use" section should be something like:

--- Code: ---// To use:
// Once the code is in the right place you will find a new menu item "Comm Tool Window" under
// the Playmaker -> Tools menu. Click this, you only need to do it once in a scene.
// You can delete the PlayMakerCommsTool object from your Hierarchy when finished.

--- End code ---

KJIB:
I have now modified the tool so that you can also filter on target FSM and target Event (i.e. from SendEvent use).

If anyone is wanting updates let me know and I will put them here (or say where to get them) but perhaps no one else has found a use for this tool yet. If you start using lots of FSMs that talk to eachother or to scripts then you might find this is just the ticket when you're scratching your head wondering how something happened & you know you set something up somewhere (or someone else on your team did) but now it's lost & your alternative is going through every FSM, State and Action hoping that you don't miss it.

I was kind of hoping I could pass this on to you guys at Hutong Games because other wise there's always the potential maintenance issue when you do an update & I'm obviously wanting to use Playmaker to save me time & don't want to add to my workload... so if you want to take it, it's yours, (let me know & I will provide the latest - it would be easy for you to make far nicer), if not, perhaps you can point to any issues you perceive in how I'm digging the information out.

I've currently tested this on Unity V3.5.6f4 (pro) and V4.0.1 (free) both with Playmaker V1.4.3f5 and I suspect the only issue that you may get is if you have not run the Playmaker editor before you start the tool, (in which case close the Comms Tool window, start the Playmaker editor (from the menu) and then start this tool again). I'd test it on the latest Playmaker but have to wait for work to be ready to try it!

Thanks.

KJIB:
Oh, now I've added a target game object filter too. Let me know if you want it  :)

Navigation

[0] Message Index

[#] Next page

[*] Previous page

Go to full version