Skip to content

Commit

Permalink
chore: docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Tonetfal committed Jan 13, 2024
1 parent ffdb1d3 commit c45a9d1
Show file tree
Hide file tree
Showing 4 changed files with 207 additions and 49 deletions.
180 changes: 141 additions & 39 deletions Docs/Labels.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,53 +38,19 @@ easiest ones.
The client code that makes a state be executed can also tell the starting label gameplay tag. There are different
ways of doing this, and certain methods are applicable in very strict situations.
If you have the finite state machine or machine state object reference you can use that to call the following methods:
If you have the finite state machine or machine state object reference, you can use that to call the following methods:
```c++
bool GotoState(TSubclassOf<UMachineState> InStateClass, FGameplayTag Label = TAG_StateMachine_Label_Default, bool bForceEvents = true);
TCoroutine<> PushState(TSubclassOf<UMachineState> InStateClass, FGameplayTag Label = TAG_StateMachine_Label_Default, bool* bOutPrematureResult = nullptr);
TCoroutine<> PushStateQueued(TSubclassOf<UMachineState> InStateClass, FGameplayTag Label = TAG_StateMachine_Label_Default, FFSM_PushRequestHandle* OutHandle = nullptr);
TCoroutine<> PushStateQueued(FFSM_PushRequestHandle& OutHandle, TSubclassOf<UMachineState> InStateClass, FGameplayTag Label = TAG_StateMachine_Label_Default);
```

In both the cases you can use the second argument to alter the starting label from the default one to any other.

There are some macros that users are **heavily encouraged** to use inside labels, as they cover some boilerplate that
might change with the plugin development. Using regular variants will result into undefined behavior.

```c++
GOTO_STATE_LABEL(STATE_NAME, LABEL_NAME, ...)
GOTO_STATE_CLASS_LABEL(STATE_CLASS, LABEL_NAME, ...)
PUSH_STATE_LABEL(STATE_NAME, LABEL_NAME)
PUSH_STATE_CLASS_LABEL(STATE_CLASS, LABEL_NAME)
PUSH_STATE_QUEUED_LABEL(STATE_NAME, LABEL_NAME)
PUSH_STATE_QUEUED_CLASS_LABEL(STATE_CLASS, LABEL_NAME)
```
Unlike the methods these are a bit trickier to use:
```c++
TCoroutine<> Label_Default()
{
// If the GotoState will success this label is going to terminate immediately
GOTO_STATE_LABEL(UMyMachineState_Demo, MyCoolLabel);
GOTO_STATE_CLASS_LABEL(UMyMachineState_Demo::StaticClass(), MyCoolLabel);
// The code is going to wait upon SUCCESSFULLY pushing a new state to the stack up until this state becomes the
// top-most one once again
PUSH_STATE_LABEL(UMyMachineState_Demo, MyCoolLabel);
PUSH_STATE_CLASS_LABEL(UMyMachineState_Demo::StaticClass(), MyCoolLabel);
// The code is going to wait the push result, as long as the arguments were valid, and the pushed state's
// subsequent deactivation. It means that the state might've not been pushed immediately, but it might be at some
// point, for instance, after the active state is changed to something that doesn't prevent the request from executing
PUSH_STATE_QUEUED_LABEL(UMyMachineState_Demo, MyCoolLabel);
PUSH_STATE_QUEUED_CLASS_LABEL(UMyMachineState_Demo::StaticClass(), MyCoolLabel);
}
```

These can be used outside the labels, but in that case co_await is impossible as only labels support coroutines by
default. Creating your own non-label coroutines will result into undefined behavior since the plugin comes with a
`StopLatentExecution()` which stops ALL labels (even those running in other states).
There are some macros that users are **heavily encouraged** to use **inside** the labels.
Using regular variants most likely is going to lead into misusing the framework.
Learn more about [them](#macro-wrappers).

### Internal switch

Expand Down Expand Up @@ -161,3 +127,139 @@ TCoroutine<> UPatrolState::Label_Default()
}
}
```

## Macro wrappers

The framework covers a lot of features that are not natively supported in C++, and to make users not to write
boilerplate code and avoid mistakes, the most common state actions were wrapped in macros. Their usage inside labels
are **heavily encouraged**.

Macros that are within the same section do the same thing, but using different ways of getting the arguments.

### RegisterState

`REGISTER_LABEL(LABEL_NAME)`

Associate a label with a gameplay tag.

- **LABEL_NAME** - Part of the label that comes after `TAG_StateMachine_Label_`.

### GotoLabel

`GOTO_LABEL(LABEL_NAME)`

Switch to a given label. If the call succeeds, this immediately terminates the label it's called from.

- **LABEL_NAME** - Part of the label that comes after `TAG_StateMachine_Label_`.

### GotoState

Activate a state at a specified label. If there's any active state, it'll deactivated. If the call succeeds, this
immediately terminates the label and the state it's called from.

`GOTO_STATE(STATE_NAME)`

- **STATE_NAME** - Name of a state. Can only take non-string text. Example: `UMyMachineState`.

`GOTO_STATE_CLASS(STATE_CLASS)`

- **STATE_CLASS** - Class of a state. Can only take objects. Example: `UMyMachineState::StaticClass()`.

`GOTO_STATE_LABEL(STATE_NAME, LABEL_NAME, ...)`

- **STATE_NAME** - Name of a state. Can only take non-string text. Example: `UMyMachineState`.
- **LABEL_NAME** - Part of the label that comes after `TAG_StateMachine_Label_`.
- **...** - optional bool argument. It's the `bForceEvents` of the `GotoState()`.

`GOTO_STATE_CLASS_LABEL(STATE_CLASS, LABEL_NAME, ...)`

- **STATE_CLASS** - Class of a state. Can only take objects. Example: `UMyMachineState::StaticClass()`.
- **LABEL_NAME** - Part of the label that comes after `TAG_StateMachine_Label_`.
- **...** - optional bool argument. It's the `bForceEvents` of the `GotoState()`.

### PushState

Push a state at a specified label on top of the stack. If the call succeeds, this pauses the code execution for the
time this state is inactive.

`PUSH_STATE(STATE_NAME)`

- **STATE_NAME** - Name of a state. Can only take non-string text. Example: `UMyMachineState`.

`PUSH_STATE_CLASS(STATE_CLASS)`

- **STATE_CLASS** - Class of a state. Can only take objects. Example: `UMyMachineState::StaticClass()`.

`PUSH_STATE_LABEL(STATE_NAME, LABEL_NAME)`

- **STATE_NAME** - Name of a state. Can only take non-string text. Example: `UMyMachineState`.
- **LABEL_NAME** - Part of the label that comes after `TAG_StateMachine_Label_`.

`PUSH_STATE_CLASS_LABEL(STATE_CLASS, LABEL_NAME)`

- **STATE_CLASS** - Class of a state. Can only take objects. Example: `UMyMachineState::StaticClass()`.
- **LABEL_NAME** - Part of the label that comes after `TAG_StateMachine_Label_`.

### PushStateQueued

Push a state at a specified label on top of the stack. If the operation is not possible to execute for
any reason that might change in the future, it'll queued, and apply it as soon as it becomes possible following
the existing queue order. If the call succeeds, this pauses the code execution up until the request is pending, and
until this state is inactive.

`PUSH_STATE_QUEUED(STATE_NAME)`

- **STATE_NAME** - Name of a state. Can only take non-string text. Example: `UMyMachineState`.

`PUSH_STATE_QUEUED_CLASS(STATE_CLASS)`

- **STATE_CLASS** - Class of a state. Can only take objects. Example: `UMyMachineState::StaticClass()`.

`PUSH_STATE_QUEUED_LABEL(STATE_NAME, LABEL_NAME)`

- **STATE_NAME** - Name of a state. Can only take non-string text. Example: `UMyMachineState`.
- **LABEL_NAME** - Part of the label that comes after `TAG_StateMachine_Label_`.

`PUSH_STATE_QUEUED_CLASS_LABEL(STATE_CLASS, LABEL_NAME)`

- **STATE_CLASS** - Class of a state. Can only take objects. Example: `UMyMachineState::StaticClass()`.
- **LABEL_NAME** - Part of the label that comes after `TAG_StateMachine_Label_`.

### PopState

Pop the top-most state from stack. If the call succeeds, this immediately terminates the label and the state it's called
from.

`POP_STATE()`

### RunLatentExecution

`RUN_LATENT_EXECUTION(FUNCTION, ...)`

- **FUNCTION** - co_awaitable function name. Example: `UE5Coro::Latent::Seconds`, `AI::AIMoveTo`. Note the
lack of parenthesis.
- **...** - Arguments the function takes. If the function has default values, you can avoid writing them. Example:
`AI::AIMoveTo()` has a lot of arguments, and the majority has default values. You're only obligated to specify
the `AIController` and the `Target` (either `FVector` or `AActor*`).

### Examples

```c++
TCoroutine<> Label_Default()
{
// If the GotoState will success this label is going to terminate immediately
GOTO_STATE_LABEL(UMyMachineState_Demo, MyCoolLabel);
GOTO_STATE_CLASS_LABEL(UMyMachineState_Demo::StaticClass(), MyCoolLabel);

// The code is going to wait upon SUCCESSFULLY pushing a new state to the stack up until this state becomes the
// top-most one once again
PUSH_STATE_LABEL(UMyMachineState_Demo, MyCoolLabel);
PUSH_STATE_CLASS_LABEL(UMyMachineState_Demo::StaticClass(), MyCoolLabel);

// The code is going to wait the push result, as long as the arguments were valid, and the pushed state's
// subsequent deactivation. It means that the state might've not been pushed immediately, but it might be at some
// point, for instance, after the active state is changed to something that doesn't prevent the request from executing
PUSH_STATE_QUEUED_LABEL(UMyMachineState_Demo, MyCoolLabel);
PUSH_STATE_QUEUED_CLASS_LABEL(UMyMachineState_Demo::StaticClass(), MyCoolLabel);
}
```
56 changes: 56 additions & 0 deletions Docs/States.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,59 @@ back and forth depending on your specific needs.

There are two different types of states: [normal](NormalState.md) and [global](GlobalState.md). Regardless of the state
type, each one of them has something in common: [State Data](StateData.md) and [Labels](Labels.md).

## Manipulating the states stack

The states stack of an FSM can be manipulated using either the component itself, or the internally from the machine
states. The signatures of the methods are the same.

### GotoState

```c++
bool GotoState(TSubclassOf<UMachineState> InStateClass, FGameplayTag Label = TAG_StateMachine_Label_Default, bool bForceEvents = true);
```
Activate a state at a specified label. If there's any active state, it'll deactivated.
- **InStateClass** - State to go to.
- **Label** - Label to start the state at.
- **bForceEvents** - In case of switching to the same state we're in: If true, fire end & begin events, otherwise do
not.
- **Return Value** - If true, label has been activated, false otherwise.
### PushState
```c++
TCoroutine<> PushState(TSubclassOf<UMachineState> InStateClass, FGameplayTag Label = TAG_StateMachine_Label_Default, bool* bOutPrematureResult = nullptr);
```

Push a state at a specified label on top of the stack.

- **InStateClass** - State to push.
- **Label** - Label to start the state with.
- **bOutPrematureResult** - Output parameter. Boolean to use when you want to use the function result before it returns
code execution.

### PushStateQueued

```c++
TCoroutine<> PushStateQueued(FFSM_PushRequestHandle& OutHandle, TSubclassOf<UMachineState> InStateClass, FGameplayTag Label = TAG_StateMachine_Label_Default);
```
Push a state at a specified label on top of the stack. If the operation is not possible to execute for
any reason that might change in the future, it'll queued, and apply it as soon as it becomes possible following
the existing queue order.
- **OutHandle** - Output parameter. Push request handle used to interact with the request.
- **InStateClass** - State to push.
- **Label** - Label to start the state at.
### PopState
```c++
bool PopState();
```

Pop the top-most state from stack.

- **Return Value** - If true, a state has been popped, false otherwise.
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ class UE5FSM_API UFiniteStateMachine
void SetGlobalState(TSubclassOf<UMachineState> InStateClass);

/**
* Go to a requested state at a requested label.
* Activate a state at a specified label. If there's any active state, it'll deactivated.
* @param InStateClass state to go to.
* @param Label label to start the state at.
* @param bForceEvents in case of switching to the same state we're in: If true, fire end & begin events,
Expand All @@ -177,15 +177,15 @@ class UE5FSM_API UFiniteStateMachine
bool bForceEvents = true);

/**
* Go to a requested label using the active state.
* Go to a label using the active state.
* @param Label label to go to.
* @return If true, label has been activated, false otherwise.
* @note The requested label will be activated the next tick, if not changed with a subsequent GotoLabel call.
*/
bool GotoLabel(FGameplayTag Label);

/**
* Push a specified state at a requested label on top of the stack.
* Push a state at a specified label on top of the stack.
* @param InStateClass state to push.
* @param Label label to start the state at.
* @param bOutPrematureResult output parameter. Boolean to use when you want to use the function result before it
Expand All @@ -195,7 +195,7 @@ class UE5FSM_API UFiniteStateMachine
bool* bOutPrematureResult = nullptr);

/**
* Push a specified state at a requested label on top of the stack. If the operation is not possible to execute for
* Push a state at a specified label on top of the stack. If the operation is not possible to execute for
* any reason that might change in the future, it'll queued, and apply it as soon as it becomes possible following
* the existing queue order.
* @param OutHandle output parameter. Push request handle used to interact with the request.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -393,12 +393,12 @@ class UE5FSM_API UMachineState

public:
/**
* Go to a requested state at a requested label.
* Activate a state at a specified label. If there's any active state, it'll deactivated.
* @param InStateClass state to go to.
* @param Label label to start the state with.
* @return If true, state has been successfully switched, false otherwise.
* @param bForceEvents in case of switching to the same state we're in: If true, fire end & begin events,
* otherwise do not.
* @return If true, state has been successfully switched, false otherwise.
* @note Unlike Unreal 3, when succeeds, it doesn't interrupt latent code execution the function is called
* from (if any). It is the caller obligation to call co_return (or simply not have any code after a successful
* GotoState).
Expand All @@ -407,24 +407,24 @@ class UE5FSM_API UMachineState
bool bForceEvents = true);

/**
* Go to a requested label.
* Go to a label using the active state.
* @param Label label to go to.
* @return If true, label has been activated, false otherwise.
*/
bool GotoLabel(FGameplayTag Label);

/**
* Push a specified state at a requested label on top of the stack.
* Push a state at a specified label on top of the stack.
* @param InStateClass state to push.
* @param Label label to start the state with.
* @param bOutPrematureResult output parameter. Boolean to use when you want to use the function result before it
* returns code execution.
* returns code execution order.
*/
TCoroutine<> PushState(TSubclassOf<UMachineState> InStateClass, FGameplayTag Label = TAG_StateMachine_Label_Default,
bool* bOutPrematureResult = nullptr);

/**
* Push a specified state at a requested label on top of the stack. If the operation is not possible to execute for
* Push a state at a specified label on top of the stack. If the operation is not possible to execute for
* any reason that might change in the future, it'll queued, and apply it as soon as it becomes possible following
* the existing queue.
* @param OutHandle output parameter. Push request handle used to interact with the request.
Expand Down

0 comments on commit c45a9d1

Please sign in to comment.