SOLVED. I got it working!!
I found out that there are some subtle initialisation procedures to get the FSM with the proper state and events:
1. For RunFSM action, you need to:
- manually set the FSM related to this action;
- call its Awake() from the Actions list of the state after you set it via ActionData.SaveActions() (a clone object must be created in the SaveActions).
// create run template action
RunFSM runAction = new RunFSM() {
finishEvent = finishEvent
};
runAction.fsmTemplateControl.fsmTemplate = templates[i];
state.ActionData.SaveActions(state, new FsmStateAction[] { runAction });
state.Actions[0].Fsm = fsm;
state.Actions[0].Awake();
2. To initialize the FSM to the generated flow in runtime, you can enable and disabled the PlayMakerFSM component:
// force a reset to the fsm
playMaker.enabled = false;
playMaker.enabled = true;
3. For the events, make sure to get the ones pre-configured in the FSM, rather then creating your own (specially for the FINISHED event):
// get pre-configured events in blank FSM
_startEvent = FsmComponent.Fsm.Events.FirstOrDefault((e) => e.Name.CompareTo("START") == 0);
_finishEvent = FsmComponent.Fsm.Events.FirstOrDefault((e) => e.Name.CompareTo("FINISHED") == 0);
With these changes, the FSM diagram is generated and runs perfectly. If anyone else is interested, here follows the prototype code I made to generate this.
public class FsmGenerator : MonoBehaviour {
public PlayMakerFSM FsmComponent;
public List<FsmTemplate> Templates;
protected FsmEvent _finishEvent;
protected FsmEvent _startEvent;
protected void Start() {
// get pre-configured events in blank FSM
_startEvent = FsmComponent.Fsm.Events.FirstOrDefault((e) => e.Name.CompareTo("START") == 0);
_finishEvent = FsmComponent.Fsm.Events.FirstOrDefault((e) => e.Name.CompareTo("FINISHED") == 0);
// initialize FSM with generated flow
InitializeFSM(FsmComponent, Templates, _startEvent, _finishEvent);
}
public void StartCode() {
// force a reset to the fsm
FsmComponent.enabled = false;
FsmComponent.enabled = true;
// send event to start simulation
FsmComponent.SendEvent(_startEvent.Name);
}
protected void InitializeFSM(PlayMakerFSM playMaker, List<FsmTemplate> templates, FsmEvent startEvent, FsmEvent finishEvent) {
Fsm fsm = playMaker.Fsm;
Rect stateRect = new Rect(0, 0, 0, 50);
List<FsmState> states = new List<FsmState>();
// create start state
FsmState startState = new FsmState(fsm) {
Name = "Start",
Position = stateRect
};
// set start state as start in fsm
fsm.StartState = startState.Name;
// traverse and create each template run state
for (int i = 0; i < templates.Count; i++) {
// create template run state
FsmState state = new FsmState(fsm) {
Name = string.Format("Run Template {0}", i),
Position = BumpPosition(ref stateRect)
};
states.Add(state);
// create run template action
RunFSM runAction = new RunFSM() {
finishEvent = finishEvent
};
runAction.fsmTemplateControl.fsmTemplate = templates[i];
state.ActionData.SaveActions(state, new FsmStateAction[] { runAction });
state.Actions[0].Fsm = fsm;
state.Actions[0].Awake();
}
// create end state
FsmState endState = new FsmState(fsm) {
Name = "End",
Position = BumpPosition(ref stateRect),
Transitions = new FsmTransition[] { new FsmTransition { ToState = startState.Name, FsmEvent = finishEvent } }
};
// add next frame event action to end state
endState.ActionData.SaveActions(endState, new FsmStateAction[] { new NextFrameEvent() { sendEvent = finishEvent } });
// create transition from start state to first template run state
startState.Transitions = new FsmTransition[] {
new FsmTransition() {
ToState = states[0].Name,
FsmEvent = startEvent
}
};
// traverse template run states and connect each to the next
for (int i = 1; i < states.Count; i++) {
FsmTransition transition = new FsmTransition() {
ToState = states[i].Name,
FsmEvent = finishEvent
};
states[i - 1].Transitions = new FsmTransition[] { transition };
}
// create transition from last template run state to the end state
states[states.Count - 1].Transitions = new FsmTransition[] {
new FsmTransition() {
ToState = endState.Name,
FsmEvent = finishEvent
}
};
// add start and end state to overall state list and set to fsm
states.Add(startState);
states.Add(endState);
fsm.States = states.ToArray();
// force a reset to the fsm
playMaker.enabled = false;
playMaker.enabled = true;
}
protected Rect BumpPosition(ref Rect rect) {
rect.y += 80f;
return rect;
}
}