GS2-StateMachine
GS2-Quest provided a mechanism to manage the start and end of quests and receive rewards upon completion according to the quests started. However, GS2-Quest had a problem handling game specifications where ingame randomness is strong and rewards are difficult to specify in advance.
GS2-StateMachine was developed to manage ingame state at a finer granularity.
State Machine
The state machine is used to manage ingame state.
If you are a programmer, you have probably received a flowchart like the one above from your planner. Such a representation of state transitions is a state machine.
It keeps track of which state the player is currently in and what variables are available in the state machine. The state machine will eventually transition to the end state, and the rewards can be determined by the state variables that the state machine has at that time.
Events and transitions
Transitions are what connect the states of a state machine. Transitions set the conditions for moving from one state to the next.
The condition can be the receipt of an event, and each type of event can change the next state to transition to. Events can be issued by scripts running in the state machine, or they can be issued by the player. This allows the process to branch according to the choices made by the player or the outcome of the game.
Events can be parameterized, so there is no need to prepare an event for each choice. The state machine can be kept simple by passing an event that says “selected from the choices” and a parameter that says “what was selected”.
State Machine Definition Language
The GS2 States Language (GSL), originally developed by GS2, is used to define the state machine. GSL is written in the following notation.
StateMachine MainStateMachine {
Variables {
int turn;
int choiceSkill;
array skills;
}
EntryPoint Initialize;
Task Initialize() {
Event Pass();
Event Error(string reason);
Script grn:gs2:{region}:{ownerId}:script:statemachine-script:script:MainStateMachine_Initialize
}
SubStateMachineTask ChoiceSkill {
using ChoiceSkill;
in (turn <- turn);
out (choiceSkill -> choiceSkill);
}
WaitTask InGame {
Event Pass();
Event Fail();
Event Error(string reason);
}
Task NextTurn() {
Event Next();
Event Exit();
Event Error(string reason);
Script grn:gs2:{region}:{ownerId}:script:statemachine-script:script:MainStateMachine_NextTurn
}
PassTask Pass;
ErrorTask Error(string reason);
Transition Initialize handling Pass -> ChoiceSkill;
Transition Initialize handling Error -> Error;
Transition ChoiceSkill handling Pass -> InGame;
Transition InGame handling Pass -> NextTurn;
Transition InGame handling Fail -> Pass;
Transition InGame handling Error -> Error;
Transition NextTurn handling Next -> ChoiceSkill;
Transition NextTurn handling Exit -> Pass;
Transition NextTurn handling Error -> Error;
}
For detailed specification, please refer to About GS2 States Language Specification.
Example implementation
Starting the state machine
Starting a state machine cannot be handled by the SDK for game engines. It should be set up as a reward for GS2-Quest or other microservices.
Send an event to the state machine.
var result = await gs2.StateMachine.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Status(
statusName: status1.Name
).EmitAsync(
eventName: "event-0001",
args: "{\"value1\": \"value1\", \"value2\": 2.0, \"value3\": 3}"
);
var item = await result.ModelAsync();
const auto Domain = Gs2->StateMachine->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Status(
"$status1.name" // statusName
);
const auto Future = Domain->Emit(
"event-0001",
"{\"value1\": \"value1\", \"value2\": 2.0, \"value3\": 3}" // args
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError())
{
return false;
}
// obtain changed values / result values
const auto Future2 = Future->GetTask().Result()->Model();
Future2->StartSynchronousTask();
if (!TestFalse(WHAT, Future2->GetTask().IsError())) return false;
const auto Result = Future2->GetTask().Result();
Get state machine state.
var item = await gs2.StateMachine.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Status(
statusName: status1.Name
).ModelAsync();
const auto item = Gs2->StateMachine->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Status(
"$status1.name" // statusName
).Model();