Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

UniState is a modern, high-performance, scalable state machine package for Unity.

License

NotificationsYou must be signed in to change notification settings

bazyleu/UniState

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Last ReleasesLast Release DateAll TestsLast CommitLicense

UniState is a modern, high-performance, scalable state machine package for Unity. It can serve as a core architecturalpattern or be used to address specific tasks.

  • Performance: optimized for minimal runtime allocations, seeperformance section for details.
  • Modularity: designed to definestates,substates, andstate machines in anisolated way. States can be easily replaced or removed without hidden effects even in big projects.
  • Scalability: memory allocations happen only on demand,performance does not degrade with the number ofstates and state machines.
  • Asynchronous: modern asynchronous API with async-await andUniTask
  • Reliability: allows you to defineglobal error handling at the state machine level,and guarantees that all resources will bedisposed.
  • Simplicity: if you usestate base you have to implement only one method for fast start.
  • Flexibility: everything in framework core is an abstraction. Can be replaced with your own implementation,seestate creating andcreating a state machine sections for details.
  • Testability: UniState is designed to be testable. All abstractions use interfaces that can be easily mocked withNSubstitutes or any other framework. States can be run separately for testingpurposes.
  • DI friendly: hasintegration with most popular DI containers
  • Continuous Testing: fully covered by tests. All tests runautomatically to verify each change.

Table of Contents

Table of Contents

Getting Started

Step 1:Install UniState by adding the following URL to Unity Package Manager:
https://github.com/bazyleu/UniState.git?path=Assets/UniState.
Details on installation are availablehere.

Step 2: Create a state by defining a class that inherits fromStateBase orStateBase<T>. Example transition logic:

publicclassMainMenuState:StateBase{publicoverrideasyncUniTask<StateTransitionInfo>Execute(CancellationTokentoken){// Add your state logic herereturnTransition.GoTo<GameplayState>();}}publicclassGameplayState:StateBase{publicoverrideasyncUniTask<StateTransitionInfo>Execute(CancellationTokentoken){// Add your state logic herereturnTransition.GoBack();}}

Detailed information about creating states isavailablehere.

Step 3: Configure Dependency Injection (DI) by registering the state machine and states in the DI container.

builder.RegisterStateMachine<IStateMachine,StateMachine>();builder.RegisterState<MainMenuState>();builder.RegisterState<GameplayState>();

Additional information on DI configuration is availablehere.

Step 4: Create and run the state machine by specifying the initial state.

publicclassGame{// Note that you must resolve the interface but not the implementationprivatereadonlyIStateMachine_stateMachine;publicGame(IStateMachinestateMachine){_stateMachine=stateMachine;}publicvoidStart(){_stateMachine.Execute<StartGameState>(CancellationToken.None).Forget();}}

More details on running the state machine can be foundhere.

That is it! Your first project with UniState is set up. Intutorials section more detailed tutorial can befound.

Installation

Requirements

  • Requires Unity version 2022.3 or higher.
  • Requires UniTask package installed. Guide regarding UniTask installation can be foundonCysharp/UniTask README.

Option 1: Add package from git URL

You can addhttps://github.com/bazyleu/UniState.git?path=Assets/UniState to Package Manager.

It is a good practice to specify target version, UniState uses the*.*.* release tag so you can specify a versionlike#1.3.0. For examplehttps://github.com/bazyleu/UniState.git?path=Assets/UniState#1.3.0.You can find latest version numberhere.

imageimage

Option 2: Add via manifest.json

You can add"com.bazyleu.unistate": "https://github.com/bazyleu/UniState.git?path=Assets/UniState" (or with versiontaghttps://github.com/bazyleu/UniState.git?path=Assets/UniState#1.3.0) toPackages/manifest.json.

Performance

UniState is the fastest and most efficient asynchronous state machine available for Unity. When compared to statemachine implementations based on MonoBehaviour, UniState delivers a performance boost of over 5000x in execution speedand up to a 10x reduction in allocations.

For typical scenarios involving small to medium state chains - the most common use case - UniState can reduce memoryallocations by a factor ranging between 2x and 10x. In cases where state chains exceed 200 states, the benefits inmemory allocation become less pronounced but execution speed remain consistent with 5000x+ boost.

Measurements for Windows PC (with IL2CPP scripting backend):

Benchmark Mono 10 states: 516.4 ms, 120.83 KBBenchmark Mono 50 states: 2520.9 ms, 150.44 KBBenchmark Mono 200 states: 10033.6 ms, 283.83 KBBenchmark UniState 10 states: 0.1 ms, 13.11 KBBenchmark UniState 50 states: 0.2 ms, 68.81 KBBenchmark UniState 200 states: 0.7 ms, 273.20 KBBenchmark UniState with history 10 states: 0.1 ms, 14.34 KBBenchmark UniState with history 50 states: 0.2 ms, 69.58 KBBenchmark UniState with history 200 states: 0.7 ms, 276.95 KB

Framework Philosophy

Dependency Injection

All dependencies for states, commands, and other entities should be passed through the constructor.UniState supports automatic integration with the most popular DI frameworks for Unity.Refer to theintegration documentation for more details.Dependencies must be registered in your DI framework, and they will automatically be resolved whencreatingstate,state machine.

What is a State?

A state is an abstraction that represents a specific condition or phase of the game, often corresponding to a "screen" that the user interactswith. For example, the main menu is a state, a settings popup is another state, and gameplay itself may take place in aseparateGameplayState. When the user opens a shop popup, they may transition into aShopState. However, states arenot always tied to visual elements. Some states, likeGameLoadingState, may handle background processes such asloading resources.

State class contains all logic related to that state including loading and unloading resources. UniState does not restrict the use of otherframeworks or patterns, meaning you can freely use whatever suits your needs. You could, for example, run controllersand follow an MVC approach, follow MVVM approach, or even execute ECS code within a state.

The key concept of the framework is that once a state is exited, all resources it allocated should be released. Fordetails on how to do this seeDisposables.

It is not recommended to use Unity GameObjects directly inside states, as it reduces testability and increases codecoupling. A better approach is to load GameObjects through an abstraction and use them as an interface (essentially as aView in UniState). Add a handler for unloading to the Disposables of the state that loaded it. All approaches / patternswhich were mentioned above support this, and you can choose any based on your preferences, as this functionality isoutside the scope of UniState.

//Popup prefab (Monobehaviour, view)publicclassSimplePopupView:ISimplePopupView,Monobehaviour{//...}// Simple popup state examplepublicclassSimplePopupState:StateBase{privateISimplePopupView_view;publicoverrideasyncUniTaskInitialize(CancellationTokentoken){_view=LoadPopupView(token);Disposables.Add(UnloadShopView);}publicoverrideasyncUniTask<StateTransitionInfo>Execute(CancellationTokentoken){await_view.Show(token);await_view.WaitForClick(token);returnTransition.GoBack();}publicoverrideasyncUniTaskExit(CancellationTokentoken){await_view.Hide(token);}// The implementation of this method depends on other frameworks/patterns used in the project.privateISimplePopupViewLoadShopView(CancellationTokentoken){// Loading logic}privatevoidUnloadShopView(){// Unloading logic}}

If the popup is complex with multiple features, it could be represented as its own state machine.In cases where you have a complex popup with its own state machine, it’s important to allocate resources specific to the popup before launching the separatestate machine, ensuring they are properly cleaned up after the state machine exits.

// This state loads resources, adds them to Disposables, and runs the internal state machine for ShopPopup.// When the StateMachine completes its execution, RootShopPopupState finishes and releases its resources.publicclassRootShopPopupState:StateBase{publicoverrideasyncUniTaskInitialize(CancellationTokentoken){// Load ShopView (a Unity GameObject) and create an IDisposable handler that// will unload the GameObject after Disposing.// After that, the GameObject will be available as IShopView in internal states.vardisposable=LoadShopView();Disposables.Add(disposable);}publicoverrideasyncUniTask<StateTransitionInfo>Execute(CancellationTokentoken){varstateMachine=StateMachineFactory.Create<StateMachine>();// Run the internal state machine for ShopPopup.// In all states inside this state machine, all resources allocated in this state will be available.awaitstateMachine.Execute<ShopPopupIdleState>(cts.Token);returnTransition.GoBack();}// The implementation of this method depends on other frameworks/patterns used in the project.privateIDisposableLoadShopView(){// Loading logic}}publicclassShopPopupIdleState:StateBase{// IShopView is a Unity GameObject loaded in RootShopPopupState (outside the current state machine).// IShopView will be available as long as RootShopPopupState is running,// meaning throughout the entire internal state machine's operation.privateIShopView_view;publicShopPopupIdleState(IShopViewview){_view=view;}publicoverrideasyncUniTask<StateTransitionInfo>Execute(CancellationTokentoken){varaction=await_view.Show(token);// Transition logic with 'action'}}

API Details and Usage

State

A state is a fundamental unit of logic in an application, often representing different screens or states, such as anidle scene, main menu, popup, or a specific state of a popup.

State Creating

To create your custom state, you can inherit fromStateBase orStateBase<T>. UseStateBase<T> if you need to passparameters to the state.

For highly customized states, you can manually implement theIState<TPayload> interface. However, in mostcases,StateBase will suffice.

// Simple State InheritancepublicclassFooState:StateBase{publicoverrideasyncUniTask<StateTransitionInfo>Execute(CancellationTokentoken){// State logic here}}// State with ParameterspublicclassFooStateWithPayload:StateBase<FooPayload>{publicoverrideasyncUniTask<StateTransitionInfo>Execute(CancellationTokentoken){// Get payloadFooPayloadpayload=Payload;// State logic with payload here}}//Custom State ImplementationpublicclassCustomFooState:IState<MyParams>{publicasyncUniTaskInitialize(CancellationTokentoken){// Initialization logic}publicasyncUniTask<StateTransitionInfo>Execute(MyParamspayload,CancellationTokentoken){// Execution logic with payload}publicasyncUniTaskExit(CancellationTokentoken){// Exit logic}publicvoidDispose(){// Cleanup logic}}

State Lifecycle

The lifecycle of a state consists of four stages, represented by the following methods:

  1. Initialize

    • Used for initializing resources, such as loading prefabs, subscribing to events, etc.
  2. Execute

    • The only method that must be overridden inStateBase. It contains the main logic of the state and remains activeuntil it returns a result with a transition to another state. For example, a state displaying a popup might waitfor button presses and handle the result here. See theState Transitions section for moredetails.
  3. Exit

    • Completes the state's work, such as unsubscribing from buttons and closing the popup (e.g., playing a closinganimation).
  4. Dispose

    • Cleans up resources. If you inherit fromStateBase, this method does not need implementation.
    • Note: If you inherit state from StateBase, do not override the Dispose method. UseDisposablesinstead.

State Transitions

TheExecute method of a state should return aStateTransitionInfo object, which dictates the next actions of thestate machine. To simplify its generation, you can use theTransition property inStateBase. The possible transitionoptions are:

  1. GoTo

    • Used to transition to another state. If the state contains a payload, it should be passed toGoTo.
  2. GoBack

    • Returns to the previous state. If there is no previous state (the current state is the first), it will exit thestate machine. See theState Machine section for more details.
  3. GoBackTo

    • Returns to specified previous state, dropping all intermediate states from theState Machine's History.
    • If specified state isn't found in the history, it will exit the state machine.
    • If multiple states with specified type are present in the history, the latest state will be selected.
  4. GoToExit

    • Exits the current state machine. See theState Machine section for more details.
publicclassExampleState:StateBase{publicoverrideasyncUniTask<StateTransitionInfo>Execute(CancellationTokentoken){vartransition=awaitDoSomeAsyncLogic(token);switch(transition){caseTransitionExample.GoTo:returnTransition.GoTo<FooState>();caseTransitionExample.GoToWithPayload:varpayload=42;returnTransition.GoTo<BarState,int>(payload);caseTransitionExample.GoToAbstract:returnTransition.GoTo<IFooState>();caseTransitionExample.GoBack:returnTransition.GoBack();caseTransitionExample.GoBackTo:returnTransition.GoBackTo<BarState>();caseTransitionExample.GoToExit:returnTransition.GoToExit();default:returnTransition.GoToExit();}}privateUniTask<TransitionExample>DoSomeAsyncLogic(CancellationTokentoken){// Some logic herereturnUniTask.FromResult(TransitionExample.GoTo);}}

Disposables

Disposables are a part ofStateBase that allow users to tieIDisposable references and delegates to state'slifetime, guaranteeing disposal and delegate execution on state'sDispose, without overriding the method

publicclassLoadingState:StateBase<ILoadingScreenView>{privateCancellationTokenSource_loadingCts;publicoverrideasyncUniTask<StateTransitionInfo>Execute(CancellationTokentoken){// State's disposable references_loadingCts=CancellationTokenSource.CreateLinkedTokenSource(token);Disposables.Add(_loadingCts);// Handling of subscriptions with locality of behaviourPayload.CancelClicked+=OnCancelLoadingClicked;Disposables.Add(()=>Payload.CancelClicked-=OnCancelLoadingClicked);try{awaitPayload.PretendToWork(_loadingCts.Token);}catch(OperationCancelledException)when(!token.IsCancellationRequested){returnTransition.GoBack();}returnTransition.GoTo<NextState>();}privatevoidOnCancelLoadingClicked(){_loadingCts.Cancel();}}

State Behavior Attribute

It is possible to customize the behavior of a specific state using theStateBehaviour attribute.

This attribute has the following parameters:

  • ProhibitReturnToState (default value: false): When enabled, this state cannot be returned toviaTransition.GoBack(). The state with this attribute will be skipped, and control will return to the state beforeit. This behavior can be useful for states that represent 'loading', there is no point of returning to loading.

  • InitializeOnStateTransition (default value: false): When enabled, the initialization of the state will beginbefore exiting the previous state. Technically, this meansInitialize() of the state will be called beforeExit()of the previous state. This behavior can be useful for seamless transitions in complex animations, where the staterepresents only part of the animation.

[StateBehaviour(ProhibitReturnToState=true)]publicclassFooState:StateBase{//...}[StateBehaviour(InitializeOnStateTransition=true)]publicclassBarState:StateBase{//...}[StateBehaviour(InitializeOnStateTransition=true,ProhibitReturnToState=true)]publicclassBazState:StateBase{//...}

State Machine

The state machine is the entry point into the framework, responsible for running states.

Creating a State Machine

You can work with the built-inStateMachine class or supply a custom implementation by either deriving fromStateMachine or implementingIStateMachine.Custom interfaces that extendIStateMachine are fully supported and can be registered side-by-side.

publicclassStateMachineWithoutHistory:StateMachine{protectedoverrideintMaxHistorySize=>0;}publicinterfaceIBarMachine:IStateMachine{publicvoidBar();}publicclassBarMachine:StateMachine,IBarMachine{publicvoidBar(){Debug.Log("Bar");}}

Running a State Machine

To use a state machine, resolve it through its interface and invokeExecute<TInitialState>(cancellationToken) with thedesired entry state.

awaitstateMachine.Execute<FooState>(cts.Token);varpayload=newBarPayload();awaitstateMachine.Execute<BarState>(payload,cts.Token);

A state machine supports only one active execution flow.
CallingExecute() again while the current run has not finished raisesAlreadyExecutingException to preventconcurrent execution.

You can determine whether the machine is already running by checking propertyIsExecuting.

Launching Nested State Machines

Any state can launch any number of nested state machines.
Simply inject the machines through the state’s constructor, no additional action required.

publicclassRootGameplayState:StateBase{privatereadonlyIStateMachine_uiMachine;privatereadonlyIStateMachine_logicMachine;publicGameplayState(IStateMachineuiMachine,IStateMachinelogicMachine){_uiMachine=uiMachine;_logicMachine=_logicMachine;}publicoverrideasyncUniTask<StateTransitionInfo>Execute(CancellationTokentoken){// Run UI-related flow in parallel_uiMachine.Execute<UiRootState>(token).Forget();// Run logic and await completionawait_logicMachine.Execute<LogicRootState>(token);returnTransition.GoBack();}}

State Machine History

The state machine maintains a history of transitions between states, allowing for the use ofTransition.GoBack(). Thesize of this history can be customized through theStateMachineLongHistory.MaxHistorySize property (default value is15). If more transitions occur than the history size, only the most recent transitions will be retained, with nooverhead or errors resulting from the limit.

SettingMaxHistorySize = 0 disables the history, causingTransition.GoBack() to exit the state machine directly.

publicclassStateMachineWithDisabledHistory:StateMachine{protectedoverrideintMaxHistorySize=>0;}

State Machine Error Handling

General Error-Handling Principles

In UniState, state machine error handling can be customized to control how exceptions within states are processed. Theprimary mechanism for this is theHandleError() method, which you can override in your custom state machine. Thismethod is called whenever an exception occurs, allowing you to define specific logic to handle errors.

Exceptions are caught and processed internally without propagating further, except forOperationCanceledException,which will stop the state machine.StateMachineErrorData provides metadata related to exceptions, andStateMachineErrorData.State may benull ifStateMachineErrorType is set toStateMachineFail.

publicclassBarStateMachine:StateMachine{protectedoverridevoidHandleError(StateMachineErrorDataerrorData){// Custom logic here}}

To halt state machine execution after an exception, include athrow statement inHandleError():In the example provided, the state machine will terminate after encountering a second exception within the same state in a row.

publicclassFooStateMachine:StateMachine{privateType_lastErrorState;protectedoverridevoidHandleError(StateMachineErrorDataerrorData){varstateType=errorData.State?.GetType();if(stateType!=null&&_lastErrorState==stateType){// Stop state mahine execution and throw an exception outthrownewException($"Second exception in same state.",errorData.Exception);}_lastErrorState=stateType;}}

If an exception is encountered in a state’sInitialize() orExit() methods, the state machine will continue working.However, if an exception occurs in the state’sExecute() method, the state machine defaults to aGoBack() operation, as thoughTransition.GoBack() were returned. You can override this behavior by customizingBuildRecoveryTransition, which receives anIStateTransitionFactory to specify any desired transition for errorrecovery.

When an exception occurs inExecute(),HandleError will be invoked first, followed byBuildRecoveryTransition.

publicclassBarStateMachine:StateMachine{// If exception occurs in the state in the Execute() method, the state machine will go to the ErrorPopupState.protectedoverrideStateTransitionInfoBuildRecoveryTransition(IStateTransitionFactorytransitionFactory)=>transitionFactory.CreateStateTransition<ErrorPopupState>();}
State Machine Specific Exceptions

During the lifetime of UniState state machine may raise state-machine-specific exceptions:

  • AlreadyExecutingException — derived fromInvalidOperationException. Thrown whenExecute() is called while thestate machine is already executing, preventing a second concurrent run and indicating an incorrect lifecycle invocation.

  • NoSubStatesException — derived fromInvalidOperationException. Thrown byDefaultCompositeState if itsExecute() method starts without any SubStates being present.

Built-in Support for DI Scopes

UniState natively supports sub-containers and sub-contexts available in modern DI frameworks.

A state machine uses thecontainer scope in which it was registered:

  • Registered in the root container → its context is the root.
  • Registered in a child container → its context is that child.

All states created by the machine—and every dependency those states request—are resolved through this context.

To switch the context at runtime callSetResolver(ITypeResolver) with a resolver obtained from any container or sub-container:

IObjectResolvercontainer;varnewResolver=container.ToTypeResolver();stateMachine.SetResolver(newResolver);

Custom Type Resolvers

While UniState providesITypeResolver implementations for modern DI frameworks out of the box, you can create custom implementations, tailored to your needs

An example ofITypeResolver with automatic state bindings for Zenject/Extenject:

publicclassZenjectAutoBindTypeResolver:ITypeResolver{    ...publicobjectResolve(Typetype){if(!type.IsAbstract&&!type.IsInterface&&!_container.HasBinding(type)){_container.BindState(type);}return_container.Resolve(type);}}

If you do not have DI framework you have to implement ITypeResolver by your own by manually creating requested states andstate machines (seeWorking Without a DI Framework.

Working Without a DI Framework

UniState is engineered to integrate seamlessly with modern DI containers.
However, if your project does not use a DI framework you can still adopt UniState bysupplying a manual implementation ofITypeResolver.

An example ofITypeResolver without DI framework and state machine running:

publicclassCustomResolver:ITypeResolver{publicobjectResolve(Typetype){if(typeof(BarState)==type){returnnewBarState();}if(typeof(FooState)==type){returnFooState();}if(typeof(StateMachine)==type){returnnewStateMachine();}thrownewNotImplementedException();}}publicclassEntryPoint:MonoBehaviour{publicasyncUniTaskRun(){varresolver=newCustomResolver();varstateMachine=resolver.Resolve<StateMachine>();stateMachine.SetResolver(resolver);awaitstateMachine.Execute<FooState>(CancellationToken.None);}}}

Composite State

Composite State is essential for complex areas of an application likely to be worked on by multiple peoplesimultaneously. They consist of various independent sub states, each with its own logic.

Creating a Composite State

To create a composite state, inherit fromCompositeStateBase (or implement theICompositeState interface for moredetailed control). You can also use the ready-made implementationDefaultCompositeState (seetheDefaultCompositeState section). No additional actions are needed.

SubState

SubStates are states tied to a composite state, created and run simultaneously with it. To create a SubState, inheritfromSubStateBase or implement theISubState interface for greater customization. When creating a sub state, specifythe parent composite state as a generic parameter, e.g.,FooSubState : SubStateBase<BarCompositeState>. In all otheraspects, it functions like a regular state.

Default Composite State

A ready-to-use implementation for a composite state that propagatesInitialize,Execute, andExit methods to allSubStates within it. The result of theExecute method will be the first completedExecute method among all substates.

If you useDefaultCompositeState and it is executed without any SubStates, itsExecute method will throwanInvalidOperationException.

To useDefaultCompositeState, simply inherit your composite state from it. Here's an example:

internalclassFooCompositeState:DefaultCompositeState{}internalclassBazSubState:SubStateBase<DefaultCompositeState>{}internalclassBarSubState:SubStateBase<DefaultCompositeState>{}

Tutorials

Simple Dice Game

Overview

In this hands‑on tutorial you will create a tiny, self‑playingdice game that demonstrates the simple UniStateworkflow — from defining states to wiring everything together withVContainer.

Goal
Roll a six‑sided die until the value is 5 or 6.
StartGameStateRollDiceState
5,6 →WinState → Exit
1,2,3,4 →LostStateRollDiceState

You can find codehere.

Step 1: Create the states

Each state inherits fromStateBase and returns a transition that drives the flow.

internalclassStartGameState:StateBase{publicoverrideasyncUniTask<StateTransitionInfo>Execute(CancellationTokentoken){Debug.Log("Welcome to the game! Your game will be loaded in 2 seconds!");awaitUniTask.Delay(TimeSpan.FromSeconds(2),cancellationToken:token);returnTransition.GoTo<RollDiceState>();}}
publicclassRollDiceState:StateBase{publicoverrideasyncUniTask<StateTransitionInfo>Execute(CancellationTokentoken){Debug.Log("Need to roll 5+. Rolling the dice...");awaitUniTask.Delay(TimeSpan.FromSeconds(2),cancellationToken:token);vardice=Random.Range(0,7);Debug.Log($"Dice is{dice}");if(dice>4)returnTransition.GoTo<WinState>();returnTransition.GoTo<LostState>();}}
publicclassLostState:StateBase{publicoverrideasyncUniTask<StateTransitionInfo>Execute(CancellationTokentoken){Debug.Log("You lost. You will have a another chance in...");Debug.Log("3 seconds");awaitUniTask.Delay(TimeSpan.FromSeconds(1),cancellationToken:token);Debug.Log("2 seconds");awaitUniTask.Delay(TimeSpan.FromSeconds(1),cancellationToken:token);Debug.Log("1 second");awaitUniTask.Delay(TimeSpan.FromSeconds(1),cancellationToken:token);returnTransition.GoBack();}}
publicclassWinState:StateBase{publicoverrideUniTask<StateTransitionInfo>Execute(CancellationTokentoken){Debug.Log("Congratulations! You won this game!");returnUniTask.FromResult(Transition.GoToExit());}}

Step 2: Create entry point

DiceEntryPoint runs on scene start, converts IObjectResolver into an ITypeResolver, creates the state machine, and runsStartGameState.

publicclassDiceEntryPoint:IStartable{privatereadonlyIStateMachine_stateMachine;publicDiceEntryPoint(IStateMachinestateMachine){_stateMachine=stateMachine;}publicvoidStart(){_stateMachine.Execute<StartGameState>(CancellationToken.None).Forget();}}

Step 3: Configure VContainer

DiceScope is a LifetimeScope that registers the state machine and all states.The helper extensions RegisterStateMachine and RegisterState is used for registering.Note that for a state machine you must register an interface (or abstract class) and an implementation, and resolve theinterface, not the implementation.

publicclassDiceScope:LifetimeScope{protectedoverridevoidConfigure(IContainerBuilderbuilder){builder.RegisterEntryPoint<DiceEntryPoint>();builder.RegisterStateMachine<IStateMachine,StateMachine>();builder.RegisterState<StartGameState>();builder.RegisterState<RollDiceState>();builder.RegisterState<LostState>();builder.RegisterState<WinState>();}}

Step 4: Set up the scene

Create a new Unity scene (e.g., DiceGameScene).Add an empty GameObject and attach the DiceScope component.Press Play — all interaction happens in the Console:

Welcometo the game! Your game will be loadedin2 seconds!Need to roll5+. Rolling the dice...Diceis2Youlost.You will haveanother chancein...3 seconds2 seconds1 secondNeedto roll5+.Rolling the dice...Diceis6Congratulations! You wonthis game!

Upgrading from Versions < 1.5.0

The 1.5.0 release removes several helper APIs and unifies state-machine usage. The table below lists each breakingchange and its direct replacement.

Removed APIUse InsteadNotes
StateMachineHelperInject the state machine directly via interface into the state and callExecuteHelper no longer required.
StateMachineFactoryInject the state machine directly via interface into the state and callExecuteHelper no longer required.
IExecutableStateMachineIStateMachineSingle interface for all operations.
RegisterAbstractState /BindAbstractState and variantsRegisterState<TBase, TImpl> /BindState<TBase, TImpl>Same functionality without theAbstract prefix.
  1. Register and inject state machines by theIStateMachine (or your own) interface.
  2. Replace factory/utility calls (StateMachineHelper,StateMachineFactory) with state machine interface injection.
  3. Update container bindings to the two-parameterRegisterState /BindState overloads.
  4. Remove references toIExecutableStateMachine, useIStateMachine everywhere.

Integrations

UniState supports integrations with the most popular DI containers. If these frameworks are installed via UPM,everything will work out of the box, and no additional actions are required.

VContainer

GitHub:VContainer

VContainer Preparation

If the VContainer is installed via UPM, you can skip this step and proceed to theVContainer Usagesection.If the package is not installed via UPM, you need to manually add theUNISTATE_VCONTAINER_SUPPORT define symbol inScripting Define Symbols (Player Settings -> Player -> Scripting Define Symbols).

VContainer Usage

No extra setup is required - simply resolve the state machine from the DI container and invoke its Execute method.

publicclassGameEntryPoint:IStartable{privatereadonlyIStateMachine_stateMachine;publicGameEntryPoint(IStateMachinestateMachine){_stateMachine=stateMachine;}publicvoidStart(){_stateMachine.Execute<StartGameState>(CancellationToken.None).Forget();}}

VContainer Registering

All state machines, states and their dependencies should be registered in DI container.For convenient registering of states and state machines, special extension methods are available.

Here's an example code:

privatevoidRegisterStates(IContainerBuilderbuilder){// Use these registering in general usebuilder.RegisterStateMachine<IStateMachine,BarStateMachine>();builder.RegisterState<BarState>();builder.RegisterState<IBarState,BarState>();// Singleton version of registering, not recommended in general usebuilder.RegisterStateMachine<IStateMachine,BarStateMachine>(Lifetime.Singleton);builder.RegisterState<BarState>(Lifetime.Singleton);builder.RegisterState<IBarState,BarState>(Lifetime.Singleton);}

You can always skip the extensions and register directly if you need custom behavior.

Zenject (Extenject)

GitHub:Extenject orZenject

Zenject Preparation

If the Zenject / Extenject is installed via UPM, you can skip this step and proceed totheZenject Usage section.If the package is not installed via UPM, you need to manually add theUNISTATE_ZENJECT_SUPPORT define symbol inScripting Define Symbols (Player Settings -> Player -> Scripting Define Symbols).

Zenject Usage

No extra setup is required - simply resolve the state machine from the DI container and invoke its Execute method.

publicclassGameEntryPoint:IStartable{privatereadonlyIStateMachine_stateMachine;publicGameEntryPoint(IStateMachinestateMachine){_stateMachine=stateMachine;}publicvoidStart(){_stateMachine.Execute<StartGameState>(CancellationToken.None).Forget();}}

Zenject Registering

All state machines, states and their dependencies should be registered in DI container.For convenient registering of states and state machines, special extension methods are available.

Here's an example code:

privatevoidBindStates(DiContainercontainer){// Use these bindings in general usecontainer.BindStateMachine<IStateMachine,BarStateMachine>();container.BindState<BarState>();container.BindState<IBarState,BarState>();// Singleton version of bindings, not recommended in general usecontainer.BindStateMachineAsSingle<IStateMachine,BarStateMachine>();container.BindStateAsSingle<BarState>();container.BindStateAsSingle<IBarState,BarState>();}

License

This library is under the MIT License. Full text ishere.

Contributors3

  •  
  •  
  •  

Languages


[8]ページ先頭

©2009-2025 Movatter.jp