Skip to content
This repository was archived by the owner on Dec 10, 2022. It is now read-only.

Commit

Permalink
Initial fsm
Browse files Browse the repository at this point in the history
  • Loading branch information
vanifatovvlad committed Apr 24, 2022
1 parent 160b4dd commit e57ad06
Show file tree
Hide file tree
Showing 19 changed files with 427 additions and 0 deletions.
File renamed without changes.
7 changes: 7 additions & 0 deletions LICENSE.md.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

102 changes: 102 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Morpeh.StateMachine [![Github license](https://img.shields.io/github/license/codewriter-packages/Morpeh.StateMachine.svg?style=flat-square)](#) [![Unity 2020.1](https://img.shields.io/badge/Unity-2020.1+-2296F3.svg?style=flat-square)](#) ![GitHub package.json version](https://img.shields.io/github/package-json/v/codewriter-packages/Morpeh.StateMachine?style=flat-square)
_State Machine for Morpeh ECS_

## How to use?

```csharp
using System;
using Morpeh;

// === Components ===
[Serializable]
public struct UnitComponent : IComponent { }

public static class UnitFsmEvents {
[Serializable]
public struct TargetFound : IComponent { public Entity target; }

[Serializable]
public struct TargetLost : IComponent { }
}

public static class UnitFsmStates {
[Serializable]
public struct Idle : IComponent { }

[Serializable]
public struct FollowTarget : IComponent { public Entity target; }
}

// === StateMachine System ===
public class UniBehaviourSystem : UpdateSystem {
private Fsm fsm;

public override void OnAwake() {
this.fsm = new Fsm(World, Filter.With<UnitComponent>());

this.fsm.AtState<UnitFsmStates.Idle>()
.Transition((ref UnitFsmEvents.TargetFound evt) => new UnitFsmStates.FollowTarget {
target = evt.target,
});

this.fsm.AtState<UnitFsmStates.FollowTarget>()
.OnEnter(entity => Debug.Log("Enter FollowTarget state"))
.OnExit(entity => Debug.Log("Exit FollowTarget state"))
.Transition((ref UnitFsmEvents.TargetLost evt) => new UnitFsmStates.Idle());
}

public override void OnUpdate(float deltaTime) {
this.fsm.Execute();
}
}

// === State Logic Systems ===
public class UnitIdleSystem : UpdateSystem {
private Filter unitsFilter;

public override void OnAwake() {
this.unitsFilter = Filter.With<UnitComponent>().With<UnitFsmStates.Idle>();
}

public override void OnUpdate(float deltaTime) {
foreach (var entity in this.unitsFilter) {
if (TryGetTarget(out var target)) {
entity.AddComponent<UnitFsmEvents.TargetFound>();
}
}
}

private bool TryGetTarget(out Entity target) { /* ... */ }
}

public class UnitFollowTargetSystem : UpdateSystem {
private Filter unitsFilter;

public override void OnAwake() {
this.unitsFilter = Filter.With<UnitComponent>().With<UnitFsmStates.FollowTarget>();
}

public override void OnUpdate(float deltaTime) {
foreach (var entity in this.unitsFilter) {
ref var followTargetState = ref entity.GetComponent<UnitFsmStates.FollowTarget>();

if (followTargetState.target.IsNullOrDisposed()) {
entity.AddComponent<UnitFsmEvents.TargetLost>();
}
}
}
}
```

## How to Install
Minimal Unity Version is 2020.1.

Library distributed as git package ([How to install package from git URL](https://docs.unity3d.com/Manual/upm-ui-giturl.html))
<br>Git URL: `https://github.com/codewriter-packages/Morpeh.StateMachine.git`

## License

Morpeh.StateMachine is [MIT licensed](./LICENSE.md).
7 changes: 7 additions & 0 deletions README.md.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions Runtime.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 49 additions & 0 deletions Runtime/Fsm.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System.Collections.Generic;
using JetBrains.Annotations;

namespace Morpeh
{
public sealed class Fsm
{
internal readonly World World;
internal readonly Filter Filter;
internal readonly List<FsmRunnerBase> StateRunners = new List<FsmRunnerBase>();
internal readonly List<FsmRunnerBase> TransitionRunners = new List<FsmRunnerBase>();

public Fsm(World world, Filter filter)
{
World = world;
Filter = filter;
}

[PublicAPI]
public FsmStateBuilder<TState> AtState<TState>()
where TState : struct, IComponent
{
var stateRunner = new FsmStateRunner<TState>(this);

StateRunners.Add(stateRunner);

return new FsmStateBuilder<TState>(this, stateRunner);
}

[PublicAPI]
public void Execute()
{
foreach (var runner in StateRunners)
{
runner.Run();
}

foreach (var runner in TransitionRunners)
{
runner.Run();
}
}
}

internal abstract class FsmRunnerBase
{
public abstract void Run();
}
}
11 changes: 11 additions & 0 deletions Runtime/Fsm.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 35 additions & 0 deletions Runtime/FsmCustomTransitionRunner.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
namespace Morpeh
{
internal sealed class FsmCustomTransitionRunner<TState, TEvent> : FsmRunnerBase
where TState : struct, IComponent
where TEvent : struct, IComponent
{
private readonly FsmTransitionCustom<TEvent> _transition;
private readonly ComponentsCache<TEvent> _eventCache;
private readonly ComponentsCache<TState> _stateCache;
private readonly Filter _transitionFilter;

public FsmCustomTransitionRunner(Fsm fsm, FsmTransitionCustom<TEvent> transition)
{
_transition = transition;
_eventCache = fsm.World.GetCache<TEvent>();
_stateCache = fsm.World.GetCache<TState>();
_transitionFilter = fsm.Filter.With<TState>().With<TEvent>();
}

public override void Run()
{
foreach (var entity in _transitionFilter)
{
ref var evt = ref _eventCache.GetComponent(entity);

_transition.Invoke(ref evt, entity);

_stateCache.RemoveComponent(entity);
_eventCache.RemoveComponent(entity);
}
}
}

public delegate void FsmTransitionCustom<TEvent>(ref TEvent evt, Entity entity);
}
11 changes: 11 additions & 0 deletions Runtime/FsmCustomTransitionRunner.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 40 additions & 0 deletions Runtime/FsmSimpleTransitionRunner.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
namespace Morpeh
{
internal sealed class FsmSimpleTransitionRunner<TState, TEvent, TNextState> : FsmRunnerBase
where TState : struct, IComponent
where TEvent : struct, IComponent
where TNextState : struct, IComponent
{
private readonly FsmTransition<TEvent, TNextState> _transition;
private readonly ComponentsCache<TState> _stateCache;
private readonly ComponentsCache<TEvent> _eventCache;
private readonly ComponentsCache<TNextState> _nextStateCache;
private readonly Filter _transitionFilter;

public FsmSimpleTransitionRunner(Fsm fsm, FsmTransition<TEvent, TNextState> transition)
{
_transition = transition;
_stateCache = fsm.World.GetCache<TState>();
_eventCache = fsm.World.GetCache<TEvent>();
_nextStateCache = fsm.World.GetCache<TNextState>();
_transitionFilter = fsm.Filter.With<TState>().With<TEvent>();
}

public override void Run()
{
foreach (var entity in _transitionFilter)
{
ref var evt = ref _eventCache.GetComponent(entity);

var nextState = _transition.Invoke(ref evt);
_nextStateCache.SetComponent(entity, nextState);

_stateCache.RemoveComponent(entity);
_eventCache.RemoveComponent(entity);
}
}
}

// ReSharper disable once TypeParameterCanBeVariant
public delegate TState FsmTransition<TEvent, TState>(ref TEvent evt);
}
11 changes: 11 additions & 0 deletions Runtime/FsmSimpleTransitionRunner.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 49 additions & 0 deletions Runtime/FsmStateBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System;
using JetBrains.Annotations;

namespace Morpeh
{
public readonly struct FsmStateBuilder<TState>
where TState : struct, IComponent
{
internal readonly Fsm Fsm;
internal readonly FsmStateRunner<TState> StateRunner;

internal FsmStateBuilder(Fsm fsm, FsmStateRunner<TState> stateRunner)
{
Fsm = fsm;
StateRunner = stateRunner;
}

[PublicAPI]
public FsmStateBuilder<TState> OnEnter(Action<Entity> callback)
{
StateRunner.StateEnterCallbacks.Add(callback);
return this;
}

[PublicAPI]
public FsmStateBuilder<TState> OnExit(Action<Entity> callback)
{
StateRunner.StateExitCallbacks.Add(callback);
return this;
}

[PublicAPI]
public FsmStateBuilder<TState> Transition<TEvent, TNextState>(FsmTransition<TEvent, TNextState> t)
where TEvent : struct, IComponent
where TNextState : struct, IComponent
{
Fsm.TransitionRunners.Add(new FsmSimpleTransitionRunner<TState, TEvent, TNextState>(Fsm, t));
return this;
}

[PublicAPI]
public FsmStateBuilder<TState> Transition<TEvent>(FsmTransitionCustom<TEvent> t)
where TEvent : struct, IComponent
{
Fsm.TransitionRunners.Add(new FsmCustomTransitionRunner<TState, TEvent>(Fsm, t));
return this;
}
}
}
3 changes: 3 additions & 0 deletions Runtime/FsmStateBuilder.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit e57ad06

Please sign in to comment.