diff --git a/Assets/_Project/Develop/Runtime/Logic/Gameplay/Entities/EntitiesFactory.cs b/Assets/_Project/Develop/Runtime/Logic/Gameplay/Entities/EntitiesFactory.cs index 1edc7a8..a9ab03f 100644 --- a/Assets/_Project/Develop/Runtime/Logic/Gameplay/Entities/EntitiesFactory.cs +++ b/Assets/_Project/Develop/Runtime/Logic/Gameplay/Entities/EntitiesFactory.cs @@ -22,14 +22,12 @@ namespace _Project.Develop.Runtime.Entities private readonly EntitiesLifeContext _entitiesLifeContext; private readonly MonoEntitiesFactory _monoEntitiesFactory; private readonly CollidersRegistryService _collidersRegistryService; - private readonly IPlayerInput _playerInput; public EntitiesFactory(DIContainer container) { _collidersRegistryService = container.Resolve(); _entitiesLifeContext = container.Resolve(); _monoEntitiesFactory = container.Resolve(); - _playerInput = container.Resolve(); } public Entity CreateHero(Vector3 position) @@ -58,7 +56,7 @@ namespace _Project.Develop.Runtime.Entities .AddStartAttackRequest() .AddStartAttackEvent() .AddEndAttackEvent() - .AddAttackDelayTime(new ReactiveVariable(1)) + .AddAttackDelayTime(new ReactiveVariable(0.1f)) .AddAttackDelayEndEvent() .AddInstantAttackDamage(new ReactiveVariable(50)) .AddAttackCanceledEvent() diff --git a/Assets/_Project/Develop/Runtime/Logic/Gameplay/Features/AI/BrainsFactory.cs b/Assets/_Project/Develop/Runtime/Logic/Gameplay/Features/AI/BrainsFactory.cs index d154225..7b36391 100644 --- a/Assets/_Project/Develop/Runtime/Logic/Gameplay/Features/AI/BrainsFactory.cs +++ b/Assets/_Project/Develop/Runtime/Logic/Gameplay/Features/AI/BrainsFactory.cs @@ -65,13 +65,13 @@ namespace _Project.Develop.Runtime.Logic.Gameplay.Features.AI return brain; } - public StateMachineBrain CreateMainHeroBrain(Entity entity, ITargetSelector targetSelector) + public StateMachineBrain CreateArcheroHeroBrain(Entity entity, ITargetSelector targetSelector) { AIStateMachine combatState = CreateAutoAttackStateMachine(entity); PlayerInputMovementState movementState = new (entity, _playerInput); - + ReactiveVariable currentTarget = entity.CurrentTarget; - + ICompositeCondition fromMovementToCombatStateCondition = new CompositeCondition() .Add(new FuncCondition(() => currentTarget.Value != null)) .Add(new FuncCondition(() => _playerInput.Move.Value == Vector2.zero)); @@ -79,6 +79,39 @@ namespace _Project.Develop.Runtime.Logic.Gameplay.Features.AI ICompositeCondition fromCombatToMovementStateCondition = new CompositeCondition(LogicOperationsUtils.Or) .Add(new FuncCondition(() => currentTarget.Value == null)) .Add(new FuncCondition(() => _playerInput.Move.Value != Vector2.zero)); + + AIStateMachine behaviour = new (); + + behaviour.AddState(combatState); + behaviour.AddState(movementState); + + behaviour.AddTransition(combatState, movementState, fromCombatToMovementStateCondition); + behaviour.AddTransition(movementState, combatState, fromMovementToCombatStateCondition); + + FindTargetState findTargetState = new (_entitiesLifeContext, entity, targetSelector); + AIParallelState parallelState = new (findTargetState, behaviour); + + AIStateMachine rootStateMachine = new (); + + rootStateMachine.AddState(parallelState); + + StateMachineBrain brain = new (rootStateMachine); + + _aiBrainsContext.SetFor(entity, brain); + + return brain; + } + + public StateMachineBrain CreateMainHeroBrain(Entity entity) + { + AIStateMachine combatState = CreateSteeringAttackStateMachine(entity); + PlayerInputMovementState movementState = new (entity, _playerInput); + + ICompositeCondition fromMovementToCombatStateCondition = new CompositeCondition() + .Add(new FuncCondition(() => _playerInput.Move.Value == Vector2.zero)); + + ICompositeCondition fromCombatToMovementStateCondition = new CompositeCondition() + .Add(new FuncCondition(() => _playerInput.Move.Value != Vector2.zero)); AIStateMachine behaviour = new (); @@ -88,15 +121,8 @@ namespace _Project.Develop.Runtime.Logic.Gameplay.Features.AI behaviour.AddTransition(combatState, movementState, fromCombatToMovementStateCondition); behaviour.AddTransition(movementState, combatState, fromMovementToCombatStateCondition); - FindTargetState findTargetState = new (_entitiesLifeContext, entity, targetSelector); - AIParallelState parallelState = new (findTargetState, behaviour); + StateMachineBrain brain = new (behaviour); - AIStateMachine rootStateMachine = new (); - - rootStateMachine.AddState(parallelState); - - StateMachineBrain brain = new (rootStateMachine); - _aiBrainsContext.SetFor(entity, brain); return brain; @@ -130,6 +156,42 @@ namespace _Project.Develop.Runtime.Logic.Gameplay.Features.AI return stateMachine; } + + private AIStateMachine CreateSteeringAttackStateMachine(Entity entity) + { + AIStateMachine steeringState = CreateSteeringInputStateMachine(entity); + PlayerInputAttackTriggerState attackTriggerState = new (entity, _playerInput); + + AIParallelState parallelState = new (steeringState, attackTriggerState); + + AIStateMachine stateMachine = new (); + + stateMachine.AddState(parallelState); + + return stateMachine; + } + + private AIStateMachine CreateSteeringInputStateMachine(Entity entity) + { + EmptyState emptyState = new (); + PlayerInputRotationState rotationState = new (entity, _playerInput); + + ICondition fromIdleToRotateStateCondition = new CompositeCondition() + .Add(new FuncCondition(() => _playerInput.Look.Value != Vector2.zero)); + + ICondition fromRotateToIdleStateCondition = new CompositeCondition() + .Add(new FuncCondition(() => _playerInput.Look.Value == Vector2.zero)); + + AIStateMachine stateMachine = new (); + + stateMachine.AddState(emptyState); + stateMachine.AddState(rotationState); + + stateMachine.AddTransition(rotationState, emptyState, fromRotateToIdleStateCondition); + stateMachine.AddTransition(emptyState, rotationState, fromIdleToRotateStateCondition); + + return stateMachine; + } private AIStateMachine CreateAutoAttackStateMachine(Entity entity) { diff --git a/Assets/_Project/Develop/Runtime/Logic/Gameplay/Features/AI/States/PlayerInputAttackTriggerState.cs b/Assets/_Project/Develop/Runtime/Logic/Gameplay/Features/AI/States/PlayerInputAttackTriggerState.cs new file mode 100644 index 0000000..b82dd12 --- /dev/null +++ b/Assets/_Project/Develop/Runtime/Logic/Gameplay/Features/AI/States/PlayerInputAttackTriggerState.cs @@ -0,0 +1,35 @@ +using _Project.Develop.Runtime.Entities; +using _Project.Develop.Runtime.Utils.InputManagement; +using _Project.Develop.Runtime.Utils.ReactiveManagement.Event; +using Assets._Project.Develop.Runtime.Utilities.StateMachineCore; + +namespace _Project.Develop.Runtime.Logic.Gameplay.Features.AI.States +{ + public class PlayerInputAttackTriggerState : State, IUpdatableState + { + private readonly IPlayerInput _playerInput; + private ReactiveEvent _request; + + public PlayerInputAttackTriggerState(Entity entity, IPlayerInput playerInput) + { + _playerInput = playerInput; + + _request = entity.StartAttackRequest; + _playerInput.Attack.Enter += OnAttack; + } + + public void Update(float deltaTime) + { } + + public override void Exit() + { + base.Exit(); + _playerInput.Attack.Enter -= OnAttack; + } + + private void OnAttack(float value) + { + _request.Invoke(); + } + } +} \ No newline at end of file diff --git a/Assets/_Project/Develop/Runtime/Logic/Gameplay/Features/AI/States/PlayerInputAttackTriggerState.cs.meta b/Assets/_Project/Develop/Runtime/Logic/Gameplay/Features/AI/States/PlayerInputAttackTriggerState.cs.meta new file mode 100644 index 0000000..189237a --- /dev/null +++ b/Assets/_Project/Develop/Runtime/Logic/Gameplay/Features/AI/States/PlayerInputAttackTriggerState.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6e82caaf47984b6982642cf87f3bf1fc +timeCreated: 1772810576 \ No newline at end of file diff --git a/Assets/_Project/Develop/Runtime/Logic/Gameplay/Features/AI/States/PlayerInputMovementState.cs b/Assets/_Project/Develop/Runtime/Logic/Gameplay/Features/AI/States/PlayerInputMovementState.cs index 97d7215..ebb8119 100644 --- a/Assets/_Project/Develop/Runtime/Logic/Gameplay/Features/AI/States/PlayerInputMovementState.cs +++ b/Assets/_Project/Develop/Runtime/Logic/Gameplay/Features/AI/States/PlayerInputMovementState.cs @@ -10,21 +10,18 @@ namespace _Project.Develop.Runtime.Logic.Gameplay.Features.AI.States { private readonly IPlayerInput _playerInput; - private ReactiveVariable _rotateDirection; private ReactiveVariable _moveDirection; public PlayerInputMovementState(Entity entity, IPlayerInput playerInput) { _playerInput = playerInput; - - _rotateDirection = entity.RotateDirection; + _moveDirection = entity.MoveDirection; } public void Update(float deltaTime) { _moveDirection.Value = new Vector3(_playerInput.Move.Value.x, 0, _playerInput.Move.Value.y); - _rotateDirection.Value = new Vector3(_playerInput.Move.Value.x, 0, _playerInput.Move.Value.y); } public override void Exit() diff --git a/Assets/_Project/Develop/Runtime/Logic/Gameplay/Features/AI/States/PlayerInputRotationState.cs b/Assets/_Project/Develop/Runtime/Logic/Gameplay/Features/AI/States/PlayerInputRotationState.cs new file mode 100644 index 0000000..18f74d4 --- /dev/null +++ b/Assets/_Project/Develop/Runtime/Logic/Gameplay/Features/AI/States/PlayerInputRotationState.cs @@ -0,0 +1,52 @@ +using _Project.Develop.Runtime.Entities; +using _Project.Develop.Runtime.Utils.InputManagement; +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 PlayerInputRotationState : State, IUpdatableState + { + private readonly IPlayerInput _playerInput; + private readonly Transform _transform; + + private ReactiveVariable _rotateDirection; + + private const float Sensitivity = 0.5f; + private const float DeadZone = 0.1f; + + public PlayerInputRotationState(Entity entity, IPlayerInput playerInput) + { + _playerInput = playerInput; + _transform = entity.Transform; + + _rotateDirection = entity.RotateDirection; + } + + public void Update(float deltaTime) + { + float lookX = _playerInput.Look.Value.x; + + if (Mathf.Abs(lookX) > DeadZone) + { + Vector3 currentDirection = _rotateDirection.Value; + + if (currentDirection == Vector3.zero) + currentDirection = _transform.forward; + + currentDirection.y = 0; + + if (currentDirection.sqrMagnitude < DeadZone) + currentDirection = Vector3.forward; + + currentDirection.Normalize(); + + float angle = lookX * Sensitivity; + Quaternion rotation = Quaternion.Euler(0, angle, 0); + + _rotateDirection.Value = rotation * currentDirection; + } + } + } +} diff --git a/Assets/_Project/Develop/Runtime/Logic/Gameplay/Features/AI/States/PlayerInputRotationState.cs.meta b/Assets/_Project/Develop/Runtime/Logic/Gameplay/Features/AI/States/PlayerInputRotationState.cs.meta new file mode 100644 index 0000000..5ed0e9d --- /dev/null +++ b/Assets/_Project/Develop/Runtime/Logic/Gameplay/Features/AI/States/PlayerInputRotationState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f85bcb531011e7049b6d03f311ff377f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/_Project/Develop/Runtime/Logic/Gameplay/Infrastructure/TestGameplay.cs b/Assets/_Project/Develop/Runtime/Logic/Gameplay/Infrastructure/TestGameplay.cs index 985a2d3..9b21852 100644 --- a/Assets/_Project/Develop/Runtime/Logic/Gameplay/Infrastructure/TestGameplay.cs +++ b/Assets/_Project/Develop/Runtime/Logic/Gameplay/Infrastructure/TestGameplay.cs @@ -28,13 +28,10 @@ namespace _Project.Develop.Runtime.Logic.Gameplay.Features _brainsFactory = _container.Resolve(); _hero = _entitiesFactory.CreateHero(Vector3.zero); - _brainsFactory.CreateMainHeroBrain(_hero, new NearestDamageableTargetSelector(_hero)); - - // _enemy = _entitiesFactory.CreateTeleportWizard(Vector3.zero + Vector3.forward * 5); - // _brainsFactory.CreateWizardBrain(_enemy); + _brainsFactory.CreateMainHeroBrain(_hero); - _enemy = _entitiesFactory.CreateTeleportWizard(Vector3.zero + Vector3.forward * 5); - _brainsFactory.CreateDangerWizardBrain(_enemy, new LowestHealthTargetSelector(_enemy)); + // _enemy = _entitiesFactory.CreateTeleportWizard(Vector3.zero + Vector3.forward * 5); + // _brainsFactory.CreateDangerWizardBrain(_enemy, new LowestHealthTargetSelector(_enemy)); } public void Run()