feat: add state machine

This commit is contained in:
Bragin Stepan
2026-03-02 19:00:06 +05:00
parent 99c88c071f
commit 7737ee3158
50 changed files with 828 additions and 128 deletions

1
.gitignore vendored
View File

@@ -27,6 +27,7 @@
# Visual Studio cache directory
.vs/
.agent/
# Gradle cache directory
.gradle/

5
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,5 @@
{
"recommendations": [
"visualstudiotoolsforunity.vstuc"
]
}

10
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,10 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Attach to Unity",
"type": "vstuc",
"request": "attach"
}
]
}

View File

@@ -15,6 +15,7 @@ using Assets._Project.Develop.Runtime.Utilities.DataManagement;
using Assets._Project.Develop.Runtime.Utilities.DataManagement.DataProviders;
using Assets._Project.Develop.Runtime.Utilities.LoadingScreen;
using Assets._Project.Develop.Runtime.Utilities.SceneManagement;
using Assets._Project.Develop.Runtime.Utilities.Timer;
using Object = UnityEngine.Object;
namespace Assets._Project.Develop.Runtime.Infrastructure.EntryPoint
@@ -24,6 +25,7 @@ namespace Assets._Project.Develop.Runtime.Infrastructure.EntryPoint
public static void Process(DIContainer container)
{
container.RegisterAsSingle<ICoroutinesPerformer>(CreateCoroutinesPerformer);
container.RegisterAsSingle(CreateTimerServiceFactory);
container.RegisterAsSingle(CreateConfigsProviderService);
container.RegisterAsSingle(CreateResourcesAssetsLoader);
container.RegisterAsSingle(CreateSceneLoaderService);
@@ -64,6 +66,8 @@ namespace Assets._Project.Develop.Runtime.Infrastructure.EntryPoint
private static InputFactory CreateInputFactory(DIContainer c) => new();
private static TimerServiceFactory CreateTimerServiceFactory(DIContainer c) => new (c);
private static PlayerInput CreatePlayerInput(DIContainer c) => c.Resolve<InputFactory>().CreatePlayerInput();
private static UIInput CreateUIInput(DIContainer c) => c.Resolve<InputFactory>().CreateUIInput();

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d79e4112f0c64a90b05cae78e957a829
timeCreated: 1772454294

View File

@@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using _Project.Develop.Runtime.Entities;
namespace _Project.Develop.Runtime.Logic.Gameplay.Features.AI
{
public class AIBrainsContext : IDisposable
{
private readonly List<EntityToBrain> _entityToBrains = new ();
public void SetFor(Entity entity, IBrain brain)
{
foreach (EntityToBrain item in _entityToBrains)
{
if (item.Entity == entity)
{
item.Brain.Disable();
item.Brain.Dispose();
item.Brain = brain;
item.Brain.Enable();
return;
}
}
_entityToBrains.Add(new EntityToBrain(brain, entity));
brain.Enable();
}
public void Update(float deltaTime)
{
for (int i = 0; i < _entityToBrains.Count; i++)
{
if (_entityToBrains[i].Entity.IsInit == false)
{
int lastIndex = _entityToBrains.Count - 1;
_entityToBrains[i].Brain.Dispose();
_entityToBrains[i] = _entityToBrains[lastIndex];
_entityToBrains.RemoveAt(lastIndex);
i--;
continue;
}
_entityToBrains[i].Brain.Update(deltaTime);
}
}
public void Dispose()
{
foreach (EntityToBrain entityToBrain in _entityToBrains)
entityToBrain.Brain.Dispose();
_entityToBrains.Clear();
}
private class EntityToBrain
{
public Entity Entity;
public IBrain Brain;
public EntityToBrain(IBrain brain, Entity entity)
{
Brain = brain;
Entity = entity;
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 647b53823c00413081b7c9221d5bb5c1
timeCreated: 1772458604

View File

@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using Assets._Project.Develop.Runtime.Utilities.StateMachineCore;
namespace _Project.Develop.Runtime.Logic.Gameplay.Features.AI
{
public class AIStateMachine : StateMachine<IUpdatableState>
{
public AIStateMachine(List<IDisposable> disposables) : base(disposables)
{
}
public AIStateMachine() : base (new List<IDisposable>())
{
}
protected override void UpdateLogic(float deltaTime)
{
base.UpdateLogic(deltaTime);
CurrentState?.Update(deltaTime);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1e225964fa5a4120a1e4bf1cc8d47641
timeCreated: 1772455227

View File

@@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using _Project.Develop.Runtime.Entities;
using _Project.Develop.Runtime.Logic.Gameplay.Features.AI.States;
using _Project.Develop.Runtime.Utilities.Conditions;
using Assets._Project.Develop.Runtime.Infrastructure.DI;
using Assets._Project.Develop.Runtime.Utilities.Timer;
namespace _Project.Develop.Runtime.Logic.Gameplay.Features.AI
{
public class BrainsFactory
{
private readonly DIContainer _container;
private readonly AIBrainsContext _aiBrainsContext;
private readonly TimerServiceFactory _timerServiceFactory;
public BrainsFactory(DIContainer container)
{
_container = container;
_aiBrainsContext = _container.Resolve<AIBrainsContext>();
_timerServiceFactory = _container.Resolve<TimerServiceFactory>();
}
public StateMachineBrain CreateGhostBrain(Entity entity)
{
AIStateMachine stateMachine = CreateRandomMovementStateMachine(entity);
StateMachineBrain brain = new (stateMachine);
_aiBrainsContext.SetFor(entity, brain);
return brain;
}
private AIStateMachine CreateRandomMovementStateMachine(Entity entity)
{
List<IDisposable> disposables = new ();
RandomMovementState randomMovementState = new (entity, 0.5f);
EmptyState emptyState = new ();
TimerService movementTimer = _timerServiceFactory.Create(2f);
disposables.Add(randomMovementState.Entered.Subscribe(movementTimer.Restart));
disposables.Add(movementTimer);
TimerService idleTimer = _timerServiceFactory.Create(3f);
disposables.Add(emptyState.Entered.Subscribe(idleTimer.Restart));
disposables.Add(idleTimer);
FuncCondition movementTimerEndedCondition = new (() => movementTimer.IsOver);
FuncCondition idleTimerEndedCondition = new (() => idleTimer.IsOver);
AIStateMachine stateMachine = new (disposables);
stateMachine.AddState(randomMovementState);
stateMachine.AddState(emptyState);
stateMachine.AddTransition(randomMovementState, emptyState, movementTimerEndedCondition);
stateMachine.AddTransition(emptyState, randomMovementState, idleTimerEndedCondition);
return stateMachine;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0b566da239ac47d5b8c828cc1ac54cbe
timeCreated: 1772455908

View File

@@ -0,0 +1,14 @@
using System;
namespace _Project.Develop.Runtime.Logic.Gameplay.Features.AI
{
public interface IBrain : IDisposable
{
void Enable();
void Disable();
void Update(float deltaTime);
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 77beb88ee43645a5b0f85bf7d1518e4a
timeCreated: 1772458164

View File

@@ -0,0 +1,41 @@
namespace _Project.Develop.Runtime.Logic.Gameplay.Features.AI
{
public class StateMachineBrain : IBrain
{
private readonly AIStateMachine _stateMachine;
private bool _isEnabled;
public StateMachineBrain(AIStateMachine stateMachine)
{
_stateMachine = stateMachine;
}
public void Enable()
{
_stateMachine.Enter();
_isEnabled = true;
}
public void Update(float deltaTime)
{
if (_isEnabled == false)
return;
_stateMachine.Update(deltaTime);
}
public void Disable()
{
_stateMachine.Exit();
_stateMachine.Dispose();
_isEnabled = false;
}
public void Dispose()
{
_stateMachine.Dispose();
_isEnabled = false;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 45f2a68dedcb4a61a4ae6dc8a50227e4
timeCreated: 1772458266

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a0534b2062654595bef4d1e59081a533
timeCreated: 1772454304

View File

@@ -0,0 +1,12 @@
using Assets._Project.Develop.Runtime.Utilities.StateMachineCore;
namespace _Project.Develop.Runtime.Logic.Gameplay.Features.AI.States
{
public class EmptyState : State, IUpdatableState
{
public void Update(float deltaTime)
{
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3f40181e821046968e6c69ecef7e2384
timeCreated: 1772454318

View File

@@ -0,0 +1,67 @@
using _Project.Develop.Runtime.Entities;
using _Project.Develop.Runtime.Utils.ReactiveManagement;
using Assets._Project.Develop.Runtime.Utilities.StateMachineCore;
using UnityEngine;
namespace _Project.Develop.Runtime.Logic.Gameplay.Features.AI.States
{
public class RandomMovementState : State, IUpdatableState
{
private ReactiveVariable<Vector3> _moveDirection;
private ReactiveVariable<Vector3> _rotateDirection;
private float _cooldownBetweenDirection;
private float _time;
public RandomMovementState(Entity entity, float cooldownBetweenDirection)
{
_moveDirection = entity.MoveDirection;
_rotateDirection = entity.RotateDirection;
_cooldownBetweenDirection = cooldownBetweenDirection;
}
public override void Enter()
{
base.Enter();
Vector3 randomDirection = new Vector3(Random.Range(-1f, 1f), 0, Random.Range(-1f, 1f)).normalized;
_moveDirection.Value = randomDirection;
_rotateDirection.Value = randomDirection;
_time = 0;
}
public void Update(float deltaTime)
{
_time += deltaTime;
if (_time >= _cooldownBetweenDirection)
{
Vector3 newDirection = GenerateNewDirection();
_moveDirection.Value = newDirection;
_rotateDirection.Value = newDirection;
_time = 0;
}
}
public override void Exit()
{
base.Exit();
_moveDirection.Value = Vector3.zero;
}
private Vector3 GenerateNewDirection()
{
Vector3 inverseDirection = -_moveDirection.Value.normalized;
Quaternion randomTurn = Quaternion.Euler(0, Random.Range(-30, 30), 0);
return randomTurn * inverseDirection;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: dd6446a9b2f646bc870251ae870904b3
timeCreated: 1772454385

View File

@@ -3,7 +3,7 @@ using _Project.Develop.Runtime.Entities;
using _Project.Develop.Runtime.Utilities.Conditions;
using _Project.Develop.Runtime.Utils.ReactiveManagement;
using _Project.Develop.Runtime.Utils.ReactiveManagement.Event;
using Unity.Mathematics;
using UnityEngine;
namespace _Project.Develop.Runtime.Logic.Gameplay.Features.Energy.Systems
{
@@ -45,12 +45,12 @@ namespace _Project.Develop.Runtime.Logic.Gameplay.Features.Energy.Systems
if (energyDifference <= 0)
return;
int regenAmount= (int)math.floor(_maxEnergy.Value * (percentage / 100f));
int regenAmount = (int)Mathf.Floor(_maxEnergy.Value * (percentage / 100f));
if (regenAmount < 1 && _maxEnergy.Value > 0)
regenAmount = 1;
int valueAdded = math.min(regenAmount, energyDifference);
int valueAdded = Mathf.Min(regenAmount, energyDifference);
_currentEnergy.Value += valueAdded;

View File

@@ -3,7 +3,6 @@ using _Project.Develop.Runtime.Entities;
using _Project.Develop.Runtime.Utilities.Conditions;
using _Project.Develop.Runtime.Utils.ReactiveManagement;
using _Project.Develop.Runtime.Utils.ReactiveManagement.Event;
using Unity.Mathematics;
using UnityEngine;
namespace _Project.Develop.Runtime.Logic.Gameplay.Features.Energy.Systems
@@ -46,7 +45,7 @@ namespace _Project.Develop.Runtime.Logic.Gameplay.Features.Energy.Systems
if (energyDifference <= 0)
return;
int valueAdded = math.min(value, energyDifference);
int valueAdded = Mathf.Min(value, energyDifference);
_currentEnergy.Value += valueAdded;

View File

@@ -5,6 +5,7 @@ using System;
using System.Collections;
using _Project.Develop.Runtime.Entities;
using _Project.Develop.Runtime.Logic.Gameplay.Features;
using _Project.Develop.Runtime.Logic.Gameplay.Features.AI;
using _Project.Develop.Runtime.Utils.InputManagement;
using UnityEngine;
@@ -16,7 +17,7 @@ namespace Assets._Project.Develop.Runtime.Gameplay.Infrastructure
private DIContainer _container;
private EntitiesLifeContext _entitiesLifeContext;
private GameplayInputArgs _gameplayArgs;
private AIBrainsContext _aiBrainsContext;
private IPlayerInput _playerInput;
@@ -28,12 +29,12 @@ namespace Assets._Project.Develop.Runtime.Gameplay.Infrastructure
throw new ArgumentException($"{nameof(sceneArgs)} is not match with {typeof(GameplayInputArgs)} type");
GameplayContextRegistrations.Process(_container);
_gameplayArgs = gameplayInputArgs;
}
public override IEnumerator Initialize()
{
_entitiesLifeContext = _container.Resolve<EntitiesLifeContext>();
_aiBrainsContext = _container.Resolve<AIBrainsContext>();
_testGameplay.Initialize(_container);
yield break;
@@ -46,6 +47,7 @@ namespace Assets._Project.Develop.Runtime.Gameplay.Infrastructure
private void Update()
{
_aiBrainsContext?.Update(Time.deltaTime);
_entitiesLifeContext?.Update(Time.deltaTime);
}
}

View File

@@ -1,4 +1,5 @@
using _Project.Develop.Runtime.Entities;
using _Project.Develop.Runtime.Logic.Gameplay.Features.AI;
using _Project.Develop.Runtime.UI;
using _Project.Develop.Runtime.UI.Core;
using _Project.Develop.Runtime.UI.Screens.Gameplay;
@@ -21,9 +22,13 @@ namespace Assets._Project.Develop.Runtime.Gameplay.Infrastructure
container.RegisterAsSingle(CreateEntitiesFactory);
container.RegisterAsSingle(CreateEntitiesLifeContext);
container.RegisterAsSingle(CreateCollidersRegistryService);
container.RegisterAsSingle(CreateAIBrainContext);
container.RegisterAsSingle(CreateBrainsFactory);
container.RegisterAsSingle(CreateMonoEntitiesFactory).NonLazy();
}
private static AIBrainsContext CreateAIBrainContext(DIContainer c) => new();
private static BrainsFactory CreateBrainsFactory(DIContainer c) => new(c);
private static EntitiesLifeContext CreateEntitiesLifeContext(DIContainer c) => new();
private static EntitiesFactory CreateEntitiesFactory(DIContainer c) => new(c);

View File

@@ -1,5 +1,6 @@
using System;
using _Project.Develop.Runtime.Entities;
using _Project.Develop.Runtime.Logic.Gameplay.Features.AI;
using _Project.Develop.Runtime.Utils.InputManagement;
using Assets._Project.Develop.Runtime.Infrastructure.DI;
using UnityEngine;
@@ -10,8 +11,11 @@ namespace _Project.Develop.Runtime.Logic.Gameplay.Features
{
private DIContainer _container;
private EntitiesFactory _entitiesFactory;
private BrainsFactory _brainsFactory;
private Entity _hero;
private Entity _ghost;
private Entity _entity;
private bool _isRunning;
public void Initialize(DIContainer container)
@@ -20,15 +24,15 @@ namespace _Project.Develop.Runtime.Logic.Gameplay.Features
_container.Resolve<IPlayerInput>().Enable();
_entitiesFactory = _container.Resolve<EntitiesFactory>();
_brainsFactory = _container.Resolve<BrainsFactory>();
}
public void Run()
{
_entity = _entitiesFactory.CreateTeleportWizard(Vector3.zero);
_hero = _entitiesFactory.CreateTeleportWizard(Vector3.zero);
_ghost = _entitiesFactory.CreateGhost(Vector3.zero + Vector3.forward * 5);
_entitiesFactory.CreateGhost(Vector3.zero + Vector3.forward * 5);
_entitiesFactory.CreateGhost(Vector3.zero + Vector3.right * 5);
_entitiesFactory.CreateGhost(Vector3.zero + Vector3.left * 5);
_brainsFactory.CreateGhostBrain(_ghost);
_isRunning = true;
}
@@ -41,11 +45,11 @@ namespace _Project.Develop.Runtime.Logic.Gameplay.Features
private void OnGUI()
{
if (_entity == null)
if (_hero == null)
return;
GUI.Label(new Rect(10, 20, 200, 50), $"Health: {_entity.CurrentHealth.Value}/{_entity.MaxHealth.Value}");
GUI.Label(new Rect(10, 40, 200, 50), $"Energy: {_entity.CurrentEnergy.Value}/{_entity.MaxEnergy.Value}");
GUI.Label(new Rect(10, 20, 200, 50), $"Health: {_hero.CurrentHealth.Value}/{_hero.MaxHealth.Value}");
GUI.Label(new Rect(10, 40, 200, 50), $"Energy: {_hero.CurrentEnergy.Value}/{_hero.MaxEnergy.Value}");
}
}
}

View File

@@ -41,9 +41,9 @@ namespace _Project.Develop.Runtime.Utils.ReactiveManagement.Event
public class ReactiveEvent : IReadOnlyEvent
{
private readonly List<Subscriber> _subscribers = new();
private readonly List<Subscriber> _toAdd = new();
private readonly List<Subscriber> _toRemove = new();
private readonly List<Subscriber> _subscribers = new List<Subscriber>();
private readonly List<Subscriber> _toAdd = new List<Subscriber>();
private readonly List<Subscriber> _toRemove = new List<Subscriber>();
public IDisposable Subscribe(Action action)
{

View File

@@ -5,9 +5,9 @@ namespace _Project.Develop.Runtime.Utils.ReactiveManagement
{
public class ReactiveVariable<T> : IReadOnlyVariable<T> where T : IEquatable<T>
{
private readonly List<Subscriber<T, T>> _subscribers = new ();
private readonly List<Subscriber<T, T>> _toAddList = new ();
private readonly List<Subscriber<T, T>> _toRemoveList = new ();
private readonly List<Subscriber<T, T>> _subscribers = new List<Subscriber<T, T>>();
private readonly List<Subscriber<T, T>> _toAddList = new List<Subscriber<T, T>>();
private readonly List<Subscriber<T, T>> _toRemoveList = new List<Subscriber<T, T>>();
public ReactiveVariable() => _value = default(T);
@@ -30,7 +30,7 @@ namespace _Project.Develop.Runtime.Utils.ReactiveManagement
public IDisposable Subscribe(Action<T, T> action)
{
Subscriber<T, T> subscriber = new (action, RemoveSubscriber);
Subscriber<T, T> subscriber = new Subscriber<T, T>(action, RemoveSubscriber);
_toAddList.Add(subscriber);
return subscriber;

View File

@@ -0,0 +1,14 @@

using _Project.Develop.Runtime.Utils.ReactiveManagement.Event;
namespace Assets._Project.Develop.Runtime.Utilities.StateMachineCore
{
public interface IState
{
IReadOnlyEvent Entered { get; }
IReadOnlyEvent Exited { get; }
void Enter();
void Exit();
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: aed062833c374a6c8f4f784616dad179
timeCreated: 1772453453

View File

@@ -0,0 +1,7 @@
namespace Assets._Project.Develop.Runtime.Utilities.StateMachineCore
{
public interface IUpdatableState : IState
{
void Update(float deltaTime);
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c1ea582554484972bd37276974ad4434
timeCreated: 1772453453

View File

@@ -0,0 +1,32 @@
using System.Collections.Generic;
namespace Assets._Project.Develop.Runtime.Utilities.StateMachineCore
{
public abstract class ParallelState<TState> : State where TState : class, IState
{
private List<TState> _states;
public ParallelState(params TState[] states)
{
_states = new List<TState>(states);
}
protected IReadOnlyList<TState> States => _states;
public override void Enter()
{
base.Enter();
foreach (TState state in _states)
state.Enter();
}
public override void Exit()
{
base.Exit();
foreach (TState state in _states)
state.Exit();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3084d8be941e4eb89f019da66b818702
timeCreated: 1772453453

View File

@@ -0,0 +1,18 @@
using _Project.Develop.Runtime.Utils.ReactiveManagement.Event;
namespace Assets._Project.Develop.Runtime.Utilities.StateMachineCore
{
public abstract class State : IState
{
private ReactiveEvent _entered = new();
private ReactiveEvent _exited = new();
public IReadOnlyEvent Entered => _entered;
public IReadOnlyEvent Exited => _exited;
public virtual void Enter() => _entered.Invoke();
public virtual void Exit() => _exited.Invoke();
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 975fa4387d1e4f37afb584f32071e4bc
timeCreated: 1772453453

View File

@@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
using System.Linq;
using _Project.Develop.Runtime.Utilities.Conditions;
namespace Assets._Project.Develop.Runtime.Utilities.StateMachineCore
{
public abstract class StateMachine<TState> : State, IDisposable, IUpdatableState where TState : class, IState
{
private List<StateNode<TState>> _states = new();
private StateNode<TState> _currentState;
private bool _isRunning;
private List<IDisposable> _disposables;
protected StateMachine(List<IDisposable> disposables)
{
_disposables = new List<IDisposable>(disposables);
}
protected TState CurrentState => _currentState.State;
public void AddState(TState state) => _states.Add(new StateNode<TState>(state));
public void AddTransition(TState fromState, TState toState, ICondition condition)
{
StateNode<TState> from = _states.First(stateNode => stateNode.State == fromState);
StateNode<TState> to = _states.First(stateNode => stateNode.State == toState);
from.AddTransition(new StateTransition<TState>(to, condition));
}
public void Update(float deltaTime)
{
if (_isRunning == false)
return;
foreach (StateTransition<TState> transition in _currentState.Transitions)
{
if (transition.Condition.Evaluate())
{
SwitchState(transition.ToState);
break;
}
}
UpdateLogic(deltaTime);
}
protected virtual void UpdateLogic(float deltaTime) { }
public void Dispose()
{
_isRunning = false;
foreach (StateNode<TState> stateNode in _states)
if (stateNode.State is IDisposable disposableState)
disposableState.Dispose();
_states.Clear();
foreach (IDisposable disposable in _disposables)
disposable.Dispose();
_disposables.Clear();
}
public override void Enter()
{
base.Enter();
if (_currentState == null)
SwitchState(_states[0]);
_isRunning = true;
}
public override void Exit()
{
base.Exit();
_currentState?.State.Exit();
_isRunning = false;
}
private void SwitchState(StateNode<TState> nextState)
{
_currentState?.State.Exit();
_currentState = nextState;
_currentState.State.Enter();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 421f6f14fa064baaa737bea3684d473f
timeCreated: 1772453453

View File

@@ -0,0 +1,20 @@
using System.Collections.Generic;
namespace Assets._Project.Develop.Runtime.Utilities.StateMachineCore
{
public class StateNode<TState> where TState : class, IState
{
private List<StateTransition<TState>> _transitions = new();
public StateNode(TState state)
{
State = state;
}
public TState State { get; }
public IReadOnlyList<StateTransition<TState>> Transitions => _transitions;
public void AddTransition(StateTransition<TState> transition) => _transitions.Add(transition);
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: dce697e6ac3b45b497e8e89c5ca258cc
timeCreated: 1772453453

View File

@@ -0,0 +1,16 @@
using _Project.Develop.Runtime.Utilities.Conditions;
namespace Assets._Project.Develop.Runtime.Utilities.StateMachineCore
{
public class StateTransition<TState> where TState : class, IState
{
public StateTransition(StateNode<TState> toState, ICondition condition)
{
ToState = toState;
Condition = condition;
}
public StateNode<TState> ToState { get; }
public ICondition Condition { get; }
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 07aef2e29c2643419ff17e32556da944
timeCreated: 1772453453

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 831af1f46fb24b5c89e15c287f7bdfad
timeCreated: 1772456814

View File

@@ -0,0 +1,67 @@
using System;
using System.Collections;
using _Project.Develop.Runtime.Utils.ReactiveManagement;
using _Project.Develop.Runtime.Utils.ReactiveManagement.Event;
using Assets._Project.Develop.Runtime.Utilities.CoroutinesManagement;
using UnityEngine;
namespace Assets._Project.Develop.Runtime.Utilities.Timer
{
public class TimerService : IDisposable
{
private float _cooldown;
private ReactiveEvent _cooldownEnded;
private ReactiveVariable<float> _currentTime;
private ICoroutinesPerformer _coroutinePerformer;
private Coroutine _cooldownProcess;
public TimerService(
float cooldown,
ICoroutinesPerformer coroutinePerformer)
{
_cooldown = cooldown;
_coroutinePerformer = coroutinePerformer;
_cooldownEnded = new ReactiveEvent();
_currentTime = new ReactiveVariable<float>();
}
public IReadOnlyEvent CooldownEnded => _cooldownEnded;
public IReadOnlyVariable<float> CurrentTime => _currentTime;
public bool IsOver => _currentTime.Value <= 0;
public void Dispose()
{
Stop();
}
public void Stop()
{
if (_cooldownProcess != null)
_coroutinePerformer.StopPerform(_cooldownProcess);
}
public void Restart()
{
Stop();
_cooldownProcess = _coroutinePerformer.StartPerform(CooldownProcess());
}
private IEnumerator CooldownProcess()
{
_currentTime.Value = _cooldown;
while (IsOver == false)
{
_currentTime.Value -= Time.deltaTime;
yield return null;
}
_cooldownEnded.Invoke();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e5636415f9a840ab8ff88d96719179ec
timeCreated: 1772456820

View File

@@ -0,0 +1,18 @@
using Assets._Project.Develop.Runtime.Infrastructure.DI;
using Assets._Project.Develop.Runtime.Utilities.CoroutinesManagement;
namespace Assets._Project.Develop.Runtime.Utilities.Timer
{
public class TimerServiceFactory
{
private readonly DIContainer _container;
public TimerServiceFactory(DIContainer container)
{
_container = container;
}
public TimerService Create(float cooldown)
=> new TimerService(cooldown, _container.Resolve<ICoroutinesPerformer>());
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6fbab9cab89b4ad094fc636facb8597a
timeCreated: 1772456820

View File

@@ -924,7 +924,7 @@ PlayerSettings:
embeddedLinuxEnableGamepadInput: 1
hmiLogStartupTiming: 0
hmiCpuConfiguration:
apiCompatibilityLevel: 6
apiCompatibilityLevel: 3
activeInputHandler: 2
windowsGamepadBackendHint: 0
cloudProjectId: 4a15d730-99ee-4a69-b00c-c0c75aa639bd

33
project-entity.slnx Normal file
View File

@@ -0,0 +1,33 @@
<Solution>
<Project Path="Unity.Timeline.csproj" />
<Project Path="Unity.Timeline.Editor.csproj" />
<Project Path="Assembly-CSharp.csproj" />
<Project Path="Unity.InputSystem.csproj" />
<Project Path="Unity.TextMeshPro.Editor.csproj" />
<Project Path="Unity.PlasticSCM.Editor.csproj" />
<Project Path="Unity.VisualScripting.Flow.csproj" />
<Project Path="Unity.VisualScripting.Core.csproj" />
<Project Path="Unity.Mathematics.csproj" />
<Project Path="Unity.VisualScripting.Core.Editor.csproj" />
<Project Path="Unity.VisualScripting.Flow.Editor.csproj" />
<Project Path="UnityEditor.TestRunner.csproj" />
<Project Path="Unity.VisualScripting.SettingsProvider.Editor.csproj" />
<Project Path="UnityEngine.TestRunner.csproj" />
<Project Path="Unity.Searcher.Editor.csproj" />
<Project Path="Unity.Burst.csproj" />
<Project Path="Unity.VisualStudio.Editor.csproj" />
<Project Path="Unity.Rider.Editor.csproj" />
<Project Path="Unity.CollabProxy.Editor.csproj" />
<Project Path="Unity.VisualScripting.State.Editor.csproj" />
<Project Path="Unity.Burst.CodeGen.csproj" />
<Project Path="Unity.VisualScripting.State.csproj" />
<Project Path="Unity.TextMeshPro.csproj" />
<Project Path="Assembly-CSharp-firstpass.csproj" />
<Project Path="Unity.VSCode.Editor.csproj" />
<Project Path="Unity.Burst.Editor.csproj" />
<Project Path="Unity.InputSystem.ForUI.csproj" />
<Project Path="Unity.Mathematics.Editor.csproj" />
<Project Path="Unity.InputSystem.TestFramework.csproj" />
<Project Path="Unity.VisualScripting.Shared.Editor.csproj" />
<Project Path="Assembly-CSharp-Editor.csproj" />
</Solution>