Add project files.
This commit is contained in:
25
Examples/Nodify.StateMachine/Runner/Actions/CopyKeyAction.cs
Normal file
25
Examples/Nodify.StateMachine/Runner/Actions/CopyKeyAction.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Nodify.StateMachine
|
||||
{
|
||||
[BlackboardItem("Copy Key")]
|
||||
public class CopyKeyAction : IBlackboardAction
|
||||
{
|
||||
[BlackboardProperty("Source", BlackboardKeyType.Object)]
|
||||
public BlackboardProperty Source { get; set; }
|
||||
|
||||
[BlackboardProperty("Target", BlackboardKeyType.Object)]
|
||||
public BlackboardProperty Target { get; set; }
|
||||
|
||||
public Task Execute(Blackboard blackboard)
|
||||
{
|
||||
if (Source != Target && Source.IsKey && Target.IsKey)
|
||||
{
|
||||
var value = blackboard[Source];
|
||||
blackboard[Target] = value;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Nodify.StateMachine
|
||||
{
|
||||
[BlackboardItem("Set Value")]
|
||||
public class SetKeyValueAction : IBlackboardAction
|
||||
{
|
||||
[BlackboardProperty(BlackboardKeyType.Object)]
|
||||
public BlackboardProperty Key { get; set; }
|
||||
|
||||
[BlackboardProperty(BlackboardKeyType.Object, CanChangeType = true)]
|
||||
public BlackboardProperty Value { get; set; }
|
||||
|
||||
public Task Execute(Blackboard blackboard)
|
||||
{
|
||||
var value = blackboard.GetValue<int>(Value);
|
||||
blackboard[Key] = value;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Nodify.StateMachine
|
||||
{
|
||||
[BlackboardItem("Set State Delay")]
|
||||
public class SetStateDelayAction : IBlackboardAction
|
||||
{
|
||||
[BlackboardProperty("Delay", BlackboardKeyType.Integer)]
|
||||
public BlackboardProperty Delay { get; set; }
|
||||
|
||||
[BlackboardProperty("Success", BlackboardKeyType.Boolean, Usage = BlackboardKeyUsage.Output)]
|
||||
public BlackboardProperty Success { get; set; }
|
||||
|
||||
public Task Execute(Blackboard blackboard)
|
||||
{
|
||||
var delay = blackboard.GetValue<int>(Delay);
|
||||
|
||||
if (delay.HasValue)
|
||||
{
|
||||
blackboard[DebugBlackboardDecorator.StateDelayKey] = delay;
|
||||
}
|
||||
|
||||
blackboard[Success] = delay.HasValue;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
88
Examples/Nodify.StateMachine/Runner/Blackboard/Blackboard.cs
Normal file
88
Examples/Nodify.StateMachine/Runner/Blackboard/Blackboard.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Nodify.StateMachine
|
||||
{
|
||||
public class Blackboard
|
||||
{
|
||||
private readonly Dictionary<BlackboardKey, object?> _objects = new Dictionary<BlackboardKey, object?>();
|
||||
|
||||
public virtual IReadOnlyCollection<BlackboardKey> Keys
|
||||
=> _objects.Keys;
|
||||
|
||||
public virtual T? GetValue<T>(BlackboardKey key)
|
||||
where T : struct
|
||||
{
|
||||
if (_objects.TryGetValue(key, out var value) && value is T result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public virtual T? GetObject<T>(BlackboardKey key)
|
||||
where T : class
|
||||
{
|
||||
if (_objects.TryGetValue(key, out var value))
|
||||
{
|
||||
return value as T;
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public virtual object? GetObject(BlackboardKey key)
|
||||
{
|
||||
if (_objects.TryGetValue(key, out var value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public virtual void Set(BlackboardKey key, object? value)
|
||||
=> _objects[key] = value;
|
||||
|
||||
public virtual bool HasKey(BlackboardKey key)
|
||||
=> _objects.ContainsKey(key);
|
||||
|
||||
public virtual void Remove(BlackboardKey key)
|
||||
=> _objects.Remove(key);
|
||||
|
||||
public virtual void Clear()
|
||||
=> _objects.Clear();
|
||||
|
||||
public void CopyTo(Blackboard newBlackboard)
|
||||
{
|
||||
foreach (var kvp in _objects)
|
||||
{
|
||||
newBlackboard.Set(kvp.Key, kvp.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public object? this[BlackboardKey key]
|
||||
{
|
||||
get => GetObject(key);
|
||||
set => Set(key, value);
|
||||
}
|
||||
|
||||
public T? GetValue<T>(BlackboardProperty value) where T : struct
|
||||
=> value.IsValue ? value.GetValue<T>() : GetValue<T>(value.Key);
|
||||
|
||||
public T? GetObject<T>(BlackboardProperty value) where T : class
|
||||
=> value.IsValue ? value.GetObject<T>() : GetObject<T>(value.Key);
|
||||
|
||||
public object? GetObject(BlackboardProperty value)
|
||||
=> value.IsValue ? value.Value : GetObject(value.Key);
|
||||
}
|
||||
|
||||
public static class BlackboardExtensions
|
||||
{
|
||||
public static bool IsValid(this BlackboardKey key)
|
||||
=> key != BlackboardKey.Invalid;
|
||||
|
||||
public static bool IsValid(this BlackboardProperty action)
|
||||
=> action != BlackboardProperty.Invalid;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Nodify.StateMachine
|
||||
{
|
||||
public enum BooleanOperator
|
||||
{
|
||||
And,
|
||||
Or
|
||||
}
|
||||
|
||||
public class BlackboardConditionSet : IBlackboardCondition
|
||||
{
|
||||
public BlackboardConditionSet(IEnumerable<IBlackboardCondition> conditions, BooleanOperator op)
|
||||
{
|
||||
Conditions = new List<IBlackboardCondition>(conditions);
|
||||
Operator = op;
|
||||
}
|
||||
|
||||
public IReadOnlyList<IBlackboardCondition> Conditions { get; }
|
||||
public BooleanOperator Operator { get; set; }
|
||||
|
||||
public async Task<bool> Evaluate(Blackboard blackboard)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
if (Operator == BooleanOperator.And)
|
||||
{
|
||||
for (int i = 0; i < Conditions.Count; i++)
|
||||
{
|
||||
result &= await Conditions[i].Evaluate(blackboard);
|
||||
}
|
||||
}
|
||||
else if (Operator == BooleanOperator.Or)
|
||||
{
|
||||
for (int i = 0; i < Conditions.Count; i++)
|
||||
{
|
||||
result |= await Conditions[i].Evaluate(blackboard);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace Nodify.StateMachine
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
|
||||
public sealed class BlackboardItemAttribute : Attribute
|
||||
{
|
||||
public BlackboardItemAttribute(string displayName)
|
||||
{
|
||||
DisplayName = displayName;
|
||||
}
|
||||
|
||||
public string DisplayName { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Nodify.StateMachine
|
||||
{
|
||||
public enum BlackboardKeyType
|
||||
{
|
||||
Boolean,
|
||||
Integer,
|
||||
Double,
|
||||
String,
|
||||
Object
|
||||
}
|
||||
|
||||
[DebuggerDisplay("{Name}: {Type}")]
|
||||
public readonly struct BlackboardKey : IEquatable<BlackboardKey>
|
||||
{
|
||||
public static BlackboardKey Invalid { get; } = new BlackboardKey();
|
||||
|
||||
public BlackboardKey(string name, BlackboardKeyType type)
|
||||
{
|
||||
Name = name ?? throw new ArgumentException(nameof(name));
|
||||
Type = type;
|
||||
}
|
||||
|
||||
public BlackboardKey(string name) : this(name, BlackboardKeyType.Object)
|
||||
{
|
||||
}
|
||||
|
||||
public readonly string Name;
|
||||
public readonly BlackboardKeyType Type;
|
||||
|
||||
public static implicit operator BlackboardKey(string name)
|
||||
=> new BlackboardKey(name);
|
||||
|
||||
public static implicit operator string(BlackboardKey key)
|
||||
=> key.Name;
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
=> obj is BlackboardKey bk && bk.Equals(this);
|
||||
|
||||
public override int GetHashCode()
|
||||
=> Name?.GetHashCode() ?? -1;
|
||||
|
||||
public bool Equals(BlackboardKey other)
|
||||
=> other.Name == Name;
|
||||
|
||||
public static bool operator ==(BlackboardKey left, BlackboardKey right)
|
||||
=> left.Equals(right);
|
||||
|
||||
public static bool operator !=(BlackboardKey left, BlackboardKey right)
|
||||
=> !(left == right);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Nodify.StateMachine
|
||||
{
|
||||
[DebuggerDisplay("{IsKey ? Key : Value}")]
|
||||
public struct BlackboardProperty : IEquatable<BlackboardProperty>
|
||||
{
|
||||
public static BlackboardProperty Invalid { get; } = new BlackboardProperty();
|
||||
|
||||
public BlackboardProperty(BlackboardKey key)
|
||||
{
|
||||
Key = key;
|
||||
Value = default;
|
||||
}
|
||||
|
||||
public BlackboardProperty(object? value)
|
||||
{
|
||||
Key = BlackboardKey.Invalid;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public BlackboardKey Key { get; }
|
||||
public object? Value { get; }
|
||||
|
||||
public bool IsKey => Key.IsValid();
|
||||
public bool IsValue => !IsKey;
|
||||
|
||||
public static implicit operator BlackboardKey(BlackboardProperty action)
|
||||
=> action.Key;
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
=> obj is BlackboardProperty action && action.Equals(this);
|
||||
|
||||
public override int GetHashCode()
|
||||
=> IsKey ? Key.GetHashCode() : Value?.GetHashCode() ?? -1;
|
||||
|
||||
public bool Equals(BlackboardProperty other)
|
||||
=> IsKey == other.IsKey && IsValue == other.IsValue && Key == other.Key && Value == other.Value;
|
||||
|
||||
public static bool operator ==(BlackboardProperty left, BlackboardProperty right)
|
||||
=> left.Equals(right);
|
||||
|
||||
public static bool operator !=(BlackboardProperty left, BlackboardProperty right)
|
||||
=> !(left == right);
|
||||
|
||||
public T? GetValue<T>() where T : struct
|
||||
=> Value is T result ? result : default;
|
||||
|
||||
public T? GetObject<T>() where T : class
|
||||
=> Value as T;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
|
||||
namespace Nodify.StateMachine
|
||||
{
|
||||
public enum BlackboardKeyUsage
|
||||
{
|
||||
Input,
|
||||
Output
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Properties decorated with this attribute must always be of type <see cref="BlackboardProperty"/>.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
|
||||
public sealed class BlackboardPropertyAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Properties decorated with this attribute must always be of type <see cref="BlackboardProperty"/>.
|
||||
/// </summary>
|
||||
/// <param name="name">The display name of the key.</param>
|
||||
/// <param name="type">The data type of the value that the key refers to.</param>
|
||||
public BlackboardPropertyAttribute(string? name, BlackboardKeyType type = BlackboardKeyType.Object)
|
||||
{
|
||||
Name = name;
|
||||
Type = type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Properties decorated with this attribute must always be of type <see cref="BlackboardProperty"/>.
|
||||
/// </summary>
|
||||
/// <param name="type">The data type of the value that the key refers to.</param>
|
||||
public BlackboardPropertyAttribute(BlackboardKeyType type = BlackboardKeyType.Object) : this(null, type)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public string? Name { get; }
|
||||
public BlackboardKeyType Type { get; }
|
||||
public BlackboardKeyUsage Usage { get; set; }
|
||||
public bool CanChangeType { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Nodify.StateMachine
|
||||
{
|
||||
public interface IBlackboardAction
|
||||
{
|
||||
Task Execute(Blackboard blackboard);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Nodify.StateMachine
|
||||
{
|
||||
public interface IBlackboardCondition
|
||||
{
|
||||
Task<bool> Evaluate(Blackboard blackboard);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Nodify.StateMachine
|
||||
{
|
||||
[BlackboardItem("Are Equal")]
|
||||
public class AreEqualCondition : IBlackboardCondition
|
||||
{
|
||||
[BlackboardProperty(BlackboardKeyType.Object, CanChangeType = true)]
|
||||
public BlackboardProperty Left { get; set; }
|
||||
|
||||
[BlackboardProperty(BlackboardKeyType.Object, CanChangeType = true)]
|
||||
public BlackboardProperty Right { get; set; }
|
||||
|
||||
public Task<bool> Evaluate(Blackboard blackboard)
|
||||
{
|
||||
var left = blackboard.GetObject(Left);
|
||||
var right = blackboard.GetObject(Right);
|
||||
|
||||
// TODO: Equality
|
||||
return Task.FromResult(Equals(left, right));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Nodify.StateMachine
|
||||
{
|
||||
[BlackboardItem("Has Key")]
|
||||
public class HasKeyCondition : IBlackboardCondition
|
||||
{
|
||||
[BlackboardProperty("Key Name", BlackboardKeyType.String)]
|
||||
public BlackboardProperty Key { get; set; }
|
||||
|
||||
public Task<bool> Evaluate(Blackboard blackboard)
|
||||
{
|
||||
var keyName = blackboard.GetObject<string>(Key);
|
||||
|
||||
if (keyName != null)
|
||||
{
|
||||
return Task.FromResult(blackboard.HasKey(keyName));
|
||||
}
|
||||
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Nodify.StateMachine
|
||||
{
|
||||
[BlackboardItem("Has Value")]
|
||||
public class HasValueCondition : IBlackboardCondition
|
||||
{
|
||||
[BlackboardProperty(BlackboardKeyType.Object)]
|
||||
public BlackboardKey Key { get; set; }
|
||||
|
||||
public Task<bool> Evaluate(Blackboard blackboard)
|
||||
=> Task.FromResult(blackboard.GetObject(Key) != null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Nodify.StateMachine
|
||||
{
|
||||
public class DebugBlackboardDecorator : Blackboard
|
||||
{
|
||||
public static BlackboardKey StateDelayKey { get; } = "__state.delay";
|
||||
public static BlackboardKey TransitionDelayKey { get; } = "__transition.delay";
|
||||
|
||||
private Blackboard? _blackboard;
|
||||
|
||||
public event Action<BlackboardKey, object?>? ValueChanged;
|
||||
|
||||
public DebugBlackboardDecorator(Blackboard? blackboard = default)
|
||||
=> Attach(blackboard);
|
||||
|
||||
public override IReadOnlyCollection<BlackboardKey> Keys => _blackboard?.Keys ?? Array.Empty<BlackboardKey>();
|
||||
|
||||
public override void Remove(BlackboardKey key)
|
||||
=> _blackboard?.Remove(key);
|
||||
|
||||
public override void Clear()
|
||||
=> _blackboard?.Clear();
|
||||
|
||||
public override T? GetObject<T>(BlackboardKey key) where T : class
|
||||
=> _blackboard?.GetObject<T>(key);
|
||||
|
||||
public override T? GetValue<T>(BlackboardKey key)
|
||||
=> _blackboard?.GetValue<T>(key);
|
||||
|
||||
public override void Set(BlackboardKey key, object? value)
|
||||
{
|
||||
_blackboard?.Set(key, value);
|
||||
ValueChanged?.Invoke(key, value);
|
||||
}
|
||||
|
||||
public override bool HasKey(BlackboardKey key)
|
||||
=> _blackboard?.HasKey(key) ?? false;
|
||||
|
||||
public override object? GetObject(BlackboardKey key)
|
||||
=> _blackboard?.GetObject(key);
|
||||
|
||||
public virtual void Attach(Blackboard? blackboard)
|
||||
{
|
||||
_blackboard = blackboard;
|
||||
|
||||
Set(StateDelayKey, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Nodify.StateMachine
|
||||
{
|
||||
public class DebugStateDecorator : State
|
||||
{
|
||||
private readonly State _state;
|
||||
|
||||
public DebugStateDecorator(State state) : base(state.Id, state.Transitions)
|
||||
{
|
||||
_state = state;
|
||||
}
|
||||
|
||||
public override async Task Activate(Blackboard blackboard)
|
||||
{
|
||||
int? delay = blackboard.GetValue<int>(DebugBlackboardDecorator.StateDelayKey);
|
||||
|
||||
await Task.Delay(Math.Max(10, delay ?? 10));
|
||||
|
||||
await _state.Activate(blackboard);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Nodify.StateMachine
|
||||
{
|
||||
public class DebugTransitionDecorator : Transition
|
||||
{
|
||||
private readonly Transition _transition;
|
||||
|
||||
public DebugTransitionDecorator(Transition transition) : base(transition.From, transition.To)
|
||||
{
|
||||
_transition = transition;
|
||||
}
|
||||
|
||||
public override async Task<bool> CanActivate(Blackboard blackboard)
|
||||
{
|
||||
int? delay = blackboard.GetValue<int>(DebugBlackboardDecorator.TransitionDelayKey);
|
||||
|
||||
if (delay > 0)
|
||||
{
|
||||
await Task.Delay(delay.Value);
|
||||
}
|
||||
|
||||
return await _transition.CanActivate(blackboard);
|
||||
}
|
||||
}
|
||||
}
|
||||
24
Examples/Nodify.StateMachine/Runner/State.cs
Normal file
24
Examples/Nodify.StateMachine/Runner/State.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Nodify.StateMachine
|
||||
{
|
||||
public class State
|
||||
{
|
||||
public Guid Id { get; }
|
||||
public IBlackboardAction? Action { get; }
|
||||
|
||||
public State(Guid id, IEnumerable<Transition> transitions, IBlackboardAction? action = default)
|
||||
{
|
||||
Id = id;
|
||||
Action = action;
|
||||
Transitions = new List<Transition>(transitions);
|
||||
}
|
||||
|
||||
public IReadOnlyList<Transition> Transitions { get; }
|
||||
|
||||
public virtual Task Activate(Blackboard blackboard)
|
||||
=> Action?.Execute(blackboard) ?? Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
111
Examples/Nodify.StateMachine/Runner/StateMachine.cs
Normal file
111
Examples/Nodify.StateMachine/Runner/StateMachine.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Nodify.StateMachine
|
||||
{
|
||||
public enum MachineState
|
||||
{
|
||||
Stopped,
|
||||
Running,
|
||||
Paused,
|
||||
}
|
||||
|
||||
public delegate void StateTransitionEventHandler(Guid from, Guid to);
|
||||
public delegate void StateChangedEventHandler(MachineState newStatus);
|
||||
|
||||
public class StateMachine
|
||||
{
|
||||
private readonly Dictionary<Guid, State> _states;
|
||||
|
||||
public State Root { get; }
|
||||
public MachineState? State { get; private set; }
|
||||
public Blackboard Blackboard { get; } = new Blackboard();
|
||||
|
||||
// param = aborted
|
||||
public event StateChangedEventHandler? StateChanged;
|
||||
public event StateTransitionEventHandler? StateTransition;
|
||||
|
||||
public StateMachine(Guid root, IEnumerable<State> states, Blackboard? blackboard = default)
|
||||
{
|
||||
_states = states.ToDictionary(x => x.Id, x => x);
|
||||
|
||||
if (!_states.ContainsKey(root))
|
||||
{
|
||||
throw new ArgumentException(nameof(root));
|
||||
}
|
||||
|
||||
Root = _states[root];
|
||||
|
||||
if (blackboard != null)
|
||||
{
|
||||
Blackboard = blackboard;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Start()
|
||||
{
|
||||
if (ChangeState(MachineState.Running))
|
||||
{
|
||||
// Skip root state
|
||||
State? previous = Root;
|
||||
State? current = await GetNext(Root);
|
||||
|
||||
while (State != MachineState.Stopped && current != null)
|
||||
{
|
||||
if (State == MachineState.Paused)
|
||||
{
|
||||
await Task.Delay(10);
|
||||
}
|
||||
else
|
||||
{
|
||||
StateTransition?.Invoke(previous.Id, current.Id);
|
||||
previous = current;
|
||||
|
||||
await current.Activate(Blackboard);
|
||||
current = await GetNext(current);
|
||||
}
|
||||
}
|
||||
|
||||
ChangeState(MachineState.Stopped);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<State?> GetNext(State current)
|
||||
{
|
||||
var transitions = current.Transitions;
|
||||
for (int i = 0; i < transitions.Count; i++)
|
||||
{
|
||||
var transition = transitions[i];
|
||||
if (_states.TryGetValue(transition.To, out var result) && await transition.CanActivate(Blackboard))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
=> ChangeState(MachineState.Stopped);
|
||||
|
||||
public void Pause()
|
||||
=> ChangeState(MachineState.Paused);
|
||||
|
||||
public void Unpause()
|
||||
=> ChangeState(MachineState.Running);
|
||||
|
||||
private bool ChangeState(MachineState newState)
|
||||
{
|
||||
if (newState == MachineState.Running || (State != null && State != newState))
|
||||
{
|
||||
State = newState;
|
||||
StateChanged?.Invoke(newState);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
22
Examples/Nodify.StateMachine/Runner/Transition.cs
Normal file
22
Examples/Nodify.StateMachine/Runner/Transition.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Nodify.StateMachine
|
||||
{
|
||||
public class Transition
|
||||
{
|
||||
public Transition(Guid from, Guid to, IBlackboardCondition? condition = default)
|
||||
{
|
||||
From = from;
|
||||
To = to;
|
||||
Condition = condition;
|
||||
}
|
||||
|
||||
public Guid From { get; }
|
||||
public Guid To { get; }
|
||||
public IBlackboardCondition? Condition { get; }
|
||||
|
||||
public virtual Task<bool> CanActivate(Blackboard blackboard)
|
||||
=> Condition?.Evaluate(blackboard) ?? Task.FromResult(true);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user