Compare commits

...

3 Commits

Author SHA1 Message Date
Bragin Stepan
1356b8bfb2 fix: attack trigger entered 2026-03-06 21:17:51 +05:00
Bragin Stepan
51fd4cdc8d feat: add tank tower rotation [ [] ]=[] ------- 2026-03-06 21:03:45 +05:00
Bragin Stepan
ffeb1655c8 fix 2026-03-06 21:03:33 +05:00
13 changed files with 193 additions and 25 deletions

4
.gitignore vendored
View File

@@ -28,6 +28,7 @@
# Visual Studio cache directory
.vs/
.agent/
.agents/
# Gradle cache directory
.gradle/
@@ -65,6 +66,9 @@ sysinfo.txt
*.unitypackage.meta
*.app
# AI
Antigravity.Ide.Editor.csproj
# Crashlytics generated file
crashlytics-build.properties

View File

@@ -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<CollidersRegistryService>();
_entitiesLifeContext = container.Resolve<EntitiesLifeContext>();
_monoEntitiesFactory = container.Resolve<MonoEntitiesFactory>();
_playerInput = container.Resolve<IPlayerInput>();
}
public Entity CreateHero(Vector3 position)
@@ -58,7 +56,7 @@ namespace _Project.Develop.Runtime.Entities
.AddStartAttackRequest()
.AddStartAttackEvent()
.AddEndAttackEvent()
.AddAttackDelayTime(new ReactiveVariable<float>(1))
.AddAttackDelayTime(new ReactiveVariable<float>(0.1f))
.AddAttackDelayEndEvent()
.AddInstantAttackDamage(new ReactiveVariable<float>(50))
.AddAttackCanceledEvent()

View File

@@ -2,7 +2,6 @@
using System.Collections.Generic;
using _Project.Develop.Runtime.Entities;
using _Project.Develop.Runtime.Logic.Gameplay.Features.AI.States;
using _Project.Develop.Runtime.Logic.Gameplay.Features.Selectors;
using _Project.Develop.Runtime.Utilities.Conditions;
using _Project.Develop.Runtime.Utils.InputManagement;
using _Project.Develop.Runtime.Utils.ReactiveManagement;
@@ -56,6 +55,7 @@ namespace _Project.Develop.Runtime.Logic.Gameplay.Features.AI
AIParallelState parallelState = new (findTargetState, teleportStateMachine);
AIStateMachine rootStateMachine = new ();
rootStateMachine.AddState(parallelState);
StateMachineBrain brain = new (rootStateMachine);
@@ -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<Entity> 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,43 @@ namespace _Project.Develop.Runtime.Logic.Gameplay.Features.AI
return stateMachine;
}
// На сколько хорошая идея дробить на маленькие стейтмашины?
private AIStateMachine CreateSteeringAttackStateMachine(Entity entity)
{
AIStateMachine steeringState = CreateSteeringInputStateMachine(entity); // просто это уже 3 по вложенности
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)
{

View File

@@ -0,0 +1,39 @@
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;
}
public override void Enter()
{
base.Enter();
_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();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6e82caaf47984b6982642cf87f3bf1fc
timeCreated: 1772810576

View File

@@ -10,21 +10,18 @@ namespace _Project.Develop.Runtime.Logic.Gameplay.Features.AI.States
{
private readonly IPlayerInput _playerInput;
private ReactiveVariable<Vector3> _rotateDirection;
private ReactiveVariable<Vector3> _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()

View File

@@ -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<Vector3> _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;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f85bcb531011e7049b6d03f311ff377f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -46,7 +46,8 @@ namespace _Project.Develop.Runtime.Logic.Gameplay.Features.Teleport.Systems
private void OnEndTeleport()
{
if (_radius <= 0 || _damage <= 0) return;
if (_radius <= 0 || _damage <= 0)
return;
int count = Physics.OverlapSphereNonAlloc(
_toPoint.position,

View File

@@ -44,6 +44,7 @@ namespace _Project.Develop.Runtime.Logic.Gameplay.Features.Teleport.Systems
private Vector3 GetRandomPointInRadius(Vector3 center, float radius)
{
Vector2 randomPoint = Random.insideUnitCircle * radius;
return center + new Vector3(randomPoint.x, 0f, randomPoint.y);
}
}

View File

@@ -28,10 +28,7 @@ namespace _Project.Develop.Runtime.Logic.Gameplay.Features
_brainsFactory = _container.Resolve<BrainsFactory>();
_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));

View File

@@ -28,7 +28,7 @@ namespace _Project.Develop.Runtime.Utils.InputManagement
ICompositeCondition playerToUiStateCondition = new CompositeCondition(LogicOperationsUtils.Or)
.Add(new FuncCondition(() => _uiInput.IsEnabled));
// .Add(new FuncCondition(() => _playerHorseInput.IsEnabled));
// .Add(new FuncCondition(() => _playerHorseInput.IsEnabled)); просто как пример что включен только 1 вариант
ICompositeCondition uiToPlayerStateCondition = new CompositeCondition(LogicOperationsUtils.Or)
.Add(new FuncCondition(() => _playerInput.IsEnabled));

View File

@@ -73,6 +73,8 @@ namespace Assets._Project.Develop.Runtime.Utilities.StateMachineCore
if (_currentState == null)
SwitchState(_states[0]);
else
_currentState.State.Enter();
_isRunning = true;
}