Better action system
This commit is contained in:
1 parent
2bd71f8dc5
commit
71bb4c3b5b
110 files changed
+73523
-86288
No files matched your search
@@ -65,14 +65,8 @@ namespace Code.Game.TestingLevel
|
||||
.AsUniTask();
|
||||
|
||||
var factoryConfig = new SingleConfig<PlayerFactorySettings>();
|
||||
|
||||
var playerDecisionMarkerConfig = new SingleConfig<PlayerDecisionMarkerConfig>();
|
||||
var attackActionsConfigs = new MultipleConfig<BattleActionParameters>();
|
||||
|
||||
await UniTask.WhenAll(
|
||||
factoryConfig.LoadConfig(),
|
||||
attackActionsConfigs.LoadConfigs(),
|
||||
playerDecisionMarkerConfig.LoadConfig());
|
||||
await factoryConfig.LoadConfig();
|
||||
|
||||
var factory = new PlayerFactory(factoryConfig);
|
||||
var player = await factory.CreatePlayer();
|
||||
@@ -80,36 +74,19 @@ namespace Code.Game.TestingLevel
|
||||
var playerMovementController = player.GetComponent<MovementController>();
|
||||
var playerControlsProvider = player.GetComponent<PlayerControlsProvider>();
|
||||
var playerAnimatorProvider = player.GetComponent<EntityAnimator>();
|
||||
var playerTimelineActor = player.GetComponent<PlayerControlledTimelineActor>();
|
||||
var playerProperties = player.GetComponent<EntityProperties>();
|
||||
|
||||
playerMovementController.Freeze = true;
|
||||
|
||||
BattleActionDecisionPanel battleActionDecisionPanel =
|
||||
(BattleActionDecisionPanel)await Context.UIFactory
|
||||
.LoadPanel(UIPanelType.BattleActionDecision, false);
|
||||
|
||||
BattleTimelinePanel battleTimelinePanel =
|
||||
(BattleTimelinePanel)await Context.UIFactory
|
||||
.LoadPanel(UIPanelType.BattleTimelinePanel, false);
|
||||
|
||||
MarkerManager markerManager = new(playerDecisionMarkerConfig);
|
||||
|
||||
void InstallExtra(Scene scene, ContainerBuilder builder)
|
||||
{
|
||||
builder.RegisterValue(attackActionsConfigs);
|
||||
builder.RegisterValue(playerDecisionMarkerConfig);
|
||||
builder.RegisterValue(markerManager);
|
||||
|
||||
builder.RegisterValue(factoryConfig);
|
||||
builder.RegisterValue(factory, new[] { typeof(IPlayerFactory) });
|
||||
builder.RegisterValue(playerKinematicCharacterMotor);
|
||||
builder.RegisterValue(playerMovementController);
|
||||
builder.RegisterValue(playerControlsProvider);
|
||||
builder.RegisterValue(playerAnimatorProvider);
|
||||
builder.RegisterValue(playerTimelineActor);
|
||||
|
||||
builder.RegisterValue(battleActionDecisionPanel);
|
||||
builder.RegisterValue(battleTimelinePanel);
|
||||
builder.RegisterValue(playerProperties);
|
||||
}
|
||||
|
||||
await UniTask.WhenAll(
|
||||
@@ -119,6 +96,7 @@ namespace Code.Game.TestingLevel
|
||||
LoadSceneMode.Single,
|
||||
true,
|
||||
InstallExtra),
|
||||
//////
|
||||
Context.SceneLoader
|
||||
.LoadSceneAsync(
|
||||
SceneId.FirstTestingSegment,
|
||||
@@ -128,7 +106,8 @@ namespace Code.Game.TestingLevel
|
||||
.LoadSceneAsync(
|
||||
SceneId.SecondTestingSegment,
|
||||
LoadSceneMode.Additive,
|
||||
false),
|
||||
false),
|
||||
//////
|
||||
waitForContainer
|
||||
);
|
||||
|
||||
@@ -138,14 +117,7 @@ namespace Code.Game.TestingLevel
|
||||
.SetPositionAndRotation(
|
||||
container.Resolve<TestingLevelData>().playerSpawnPoint.position,
|
||||
container.Resolve<TestingLevelData>().playerSpawnPoint.rotation);
|
||||
|
||||
var entities =
|
||||
Object.FindObjectsByType<EntityTimelineActor>()
|
||||
.Select(x => x.gameObject)
|
||||
.ToList();
|
||||
|
||||
GameObjectInjector.InjectRecursiveMany(entities, container);
|
||||
|
||||
|
||||
playerMovementController.Freeze = false;
|
||||
}
|
||||
|
||||
|
||||
+147
-151
@@ -1,151 +1,147 @@
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Code.Infrastructure.Input;
|
||||
using Code.Infrastructure.Utils;
|
||||
using Code.OfficeGame.BattleMode.Actions.HoloCopy;
|
||||
using Code.OfficeGame.BattleMode.HitBoxes;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Reflex.Attributes;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace Code.OfficeGame.BattleMode.Actions.Attacks
|
||||
{
|
||||
public class BattleActionPlayerAttackMMA : BattleAction
|
||||
{
|
||||
[SerializeField] private HoloCop holoCop;
|
||||
[SerializeField] private Animator animator;
|
||||
private EntityHitBoxes _entityHitBoxes;
|
||||
private AttackActionConfig _attackData;
|
||||
public int EntryStateHash => Animator.StringToHash("WalkBlend");
|
||||
|
||||
private int _currentFrame;
|
||||
private IInputService _inputService;
|
||||
private CancellationTokenSource _waitForEnterTokenSource;
|
||||
private BattleActionDecisionResultCode _decisionResultCode;
|
||||
|
||||
[Inject]
|
||||
public void Construct(MultipleConfig<BattleActionParameters> attackData, IInputService inputService)
|
||||
{
|
||||
_inputService = inputService;
|
||||
|
||||
_entityHitBoxes = GetComponent<EntityHitBoxes>();
|
||||
|
||||
_attackData = (AttackActionConfig) attackData.Configs.SingleOrDefault(x=>x.ActionType == ActionType);
|
||||
}
|
||||
|
||||
public override async UniTask<BattleActionDecisionResult> DecideAction()
|
||||
{
|
||||
_currentFrame = 0;
|
||||
Finished = false;
|
||||
|
||||
_waitForEnterTokenSource = new CancellationTokenSource();
|
||||
_decisionResultCode = BattleActionDecisionResultCode.CancelDecision;
|
||||
|
||||
_inputService.BindActionPerformed((int)ActionMappings.BattleMode, (int)BattleModeActions.Confirm,
|
||||
ConfirmAttack);
|
||||
_inputService.BindActionPerformed((int)ActionMappings.BattleMode, (int)BattleModeActions.Cancel,
|
||||
CancelAttack);
|
||||
|
||||
holoCop.Simulate(TickHoloCopy, _waitForEnterTokenSource.Token).Forget();
|
||||
|
||||
await UniTask.WaitUntilCanceled(_waitForEnterTokenSource.Token).SuppressCancellationThrow();
|
||||
|
||||
_inputService.UnbindActionPerformed((int)ActionMappings.BattleMode, (int)BattleModeActions.Confirm,
|
||||
ConfirmAttack);
|
||||
_inputService.UnbindActionPerformed((int)ActionMappings.BattleMode, (int)BattleModeActions.Cancel,
|
||||
CancelAttack);
|
||||
|
||||
_currentFrame = 0;
|
||||
|
||||
return new() { ResultCode = _decisionResultCode, FrameDuration = _attackData.TotalFrames};
|
||||
}
|
||||
|
||||
private void CancelAttack(InputAction.CallbackContext obj)
|
||||
{
|
||||
_decisionResultCode = BattleActionDecisionResultCode.CancelDecision;
|
||||
_waitForEnterTokenSource?.Cancel();
|
||||
}
|
||||
|
||||
private void ConfirmAttack(InputAction.CallbackContext obj)
|
||||
{
|
||||
_decisionResultCode = BattleActionDecisionResultCode.Decided;
|
||||
_waitForEnterTokenSource?.Cancel();
|
||||
}
|
||||
|
||||
public override BattleActionType ActionType => BattleActionType.PlayerAttackMMA;
|
||||
|
||||
public override void Tick(float tickDelta)
|
||||
{
|
||||
if (Finished) return;
|
||||
|
||||
if (_currentFrame == 0)
|
||||
{
|
||||
// TODO Root Motion не должен быть использован в нормальных анимациях
|
||||
animator.applyRootMotion = true;
|
||||
animator.Play(_attackData.StateHash, 0, 0f);
|
||||
}
|
||||
|
||||
_currentFrame++;
|
||||
|
||||
if (_currentFrame >= _attackData.HitboxStartFrame && _currentFrame <= _attackData.HitboxEndFrame)
|
||||
{
|
||||
_entityHitBoxes.EnableHitbox(_attackData.AttackingBodyPart);
|
||||
_entityHitBoxes.CheckHitsForCurrentFrame(OnEnemyHit);
|
||||
}
|
||||
else
|
||||
{
|
||||
_entityHitBoxes.DisableAllHitboxes();
|
||||
}
|
||||
|
||||
if (_currentFrame >= _attackData.TotalFrames)
|
||||
{
|
||||
CompleteOrInterruptAction();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEnemyHit(Collider enemyCollider)
|
||||
{
|
||||
Debug.Log($"Попадание в {enemyCollider.name} на {_currentFrame}-м кадре! Урон: {_attackData.Damage}");
|
||||
var entityActor = enemyCollider.GetComponentInParent<EntityTimelineActor>();
|
||||
entityActor.ForceDecideAction(BattleActionType.Stugger);
|
||||
}
|
||||
|
||||
public void CompleteOrInterruptAction()
|
||||
{
|
||||
animator.Play(EntryStateHash, 0, 0f); // Создать None State и кинуть эту штуку туда
|
||||
animator.Update(0f);
|
||||
_entityHitBoxes.DisableAllHitboxes();
|
||||
|
||||
// TODO С нормальными анимациями не нужен будет рут моушен√
|
||||
animator.applyRootMotion = false;
|
||||
animator.transform.localPosition = Vector3.zero;
|
||||
animator.transform.localRotation = Quaternion.identity;
|
||||
|
||||
Finished = true;
|
||||
}
|
||||
|
||||
// HoloCopy
|
||||
|
||||
private bool TickHoloCopy(HoloCop holoCop, float tickDelta)
|
||||
{
|
||||
if (_currentFrame == 0)
|
||||
{
|
||||
holoCop.Animator.applyRootMotion = true;
|
||||
holoCop.Animator.Play(_attackData.StateHash, 0, 0f);
|
||||
}
|
||||
|
||||
_currentFrame++;
|
||||
|
||||
if(_currentFrame >= _attackData.TotalFrames)
|
||||
{
|
||||
_currentFrame = 0;
|
||||
holoCop.Animator.applyRootMotion = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// using System.Linq;
|
||||
// using System.Threading;
|
||||
// using Code.Infrastructure.Input;
|
||||
// using Code.Infrastructure.Utils;
|
||||
// using Code.OfficeGame.BattleMode.Actions.Deciders;
|
||||
// using Code.OfficeGame.BattleMode.Actions.HoloCopy;
|
||||
// using Code.OfficeGame.BattleMode.HitBoxes;
|
||||
// using Cysharp.Threading.Tasks;
|
||||
// using Reflex.Attributes;
|
||||
// using UnityEngine;
|
||||
// using UnityEngine.InputSystem;
|
||||
//
|
||||
// namespace Code.OfficeGame.BattleMode.Actions.Attacks
|
||||
// {
|
||||
// public class BattleActionPlayerAttackMMA : BattleAction
|
||||
// {
|
||||
// [SerializeField] private HoloCop holoCop;
|
||||
// [SerializeField] private Animator animator;
|
||||
// private EntityHitBoxes _entityHitBoxes;
|
||||
// private AttackActionConfig _attackData;
|
||||
// public int EntryStateHash => Animator.StringToHash("WalkBlend");
|
||||
//
|
||||
// private int _currentFrame;
|
||||
// private IInputService _inputService;
|
||||
// private CancellationTokenSource _waitForEnterTokenSource;
|
||||
// private BattleActionDecisionResultCode _decisionResultCode;
|
||||
//
|
||||
// [Inject]
|
||||
// public void Construct(MultipleConfig<BattleActionParameters> attackData, IInputService inputService)
|
||||
// {
|
||||
// _inputService = inputService;
|
||||
//
|
||||
// _entityHitBoxes = GetComponent<EntityHitBoxes>();
|
||||
//
|
||||
// _attackData = (AttackActionConfig) attackData.Configs.SingleOrDefault(x=>x.ActionType == ActionType);
|
||||
// }
|
||||
//
|
||||
// public override async UniTask<BattleActionDecisionResult> DecideAction()
|
||||
// {
|
||||
// _currentFrame = 0;
|
||||
// Finished = false;
|
||||
//
|
||||
// _waitForEnterTokenSource = new CancellationTokenSource();
|
||||
// _decisionResultCode = BattleActionDecisionResultCode.CancelDecision;
|
||||
//
|
||||
// _inputService.BindActionPerformed((int)ActionMappings.BattleMode, (int)BattleModeActions.Confirm,
|
||||
// ConfirmAttack);
|
||||
// _inputService.BindActionPerformed((int)ActionMappings.BattleMode, (int)BattleModeActions.Cancel,
|
||||
// CancelAttack);
|
||||
//
|
||||
// holoCop.Simulate(TickHoloCopy, _waitForEnterTokenSource.Token).Forget();
|
||||
//
|
||||
// await UniTask.WaitUntilCanceled(_waitForEnterTokenSource.Token).SuppressCancellationThrow();
|
||||
//
|
||||
// _inputService.UnbindActionPerformed((int)ActionMappings.BattleMode, (int)BattleModeActions.Confirm,
|
||||
// ConfirmAttack);
|
||||
// _inputService.UnbindActionPerformed((int)ActionMappings.BattleMode, (int)BattleModeActions.Cancel,
|
||||
// CancelAttack);
|
||||
//
|
||||
// _currentFrame = 0;
|
||||
//
|
||||
// return new() { ResultCode = _decisionResultCode, FrameDuration = _attackData.TotalFrames};
|
||||
// }
|
||||
//
|
||||
// private void CancelAttack(InputAction.CallbackContext obj)
|
||||
// {
|
||||
// _decisionResultCode = BattleActionDecisionResultCode.CancelDecision;
|
||||
// _waitForEnterTokenSource?.Cancel();
|
||||
// }
|
||||
//
|
||||
// private void ConfirmAttack(InputAction.CallbackContext obj)
|
||||
// {
|
||||
// _decisionResultCode = BattleActionDecisionResultCode.Decided;
|
||||
// _waitForEnterTokenSource?.Cancel();
|
||||
// }
|
||||
//
|
||||
// public override BattleActionType ActionType => BattleActionType.PlayerAttackMMA;
|
||||
//
|
||||
// public override bool Tick(float tickDelta, EntityProperties entity)
|
||||
// {
|
||||
// if (Finished) return;
|
||||
//
|
||||
// if (_currentFrame == 0)
|
||||
// {
|
||||
// // TODO Root Motion не должен быть использован в нормальных анимациях
|
||||
// animator.applyRootMotion = true;
|
||||
// animator.Play(_attackData.StateHash, 0, 0f);
|
||||
// }
|
||||
//
|
||||
// _currentFrame++;
|
||||
//
|
||||
// if (_currentFrame >= _attackData.HitboxStartFrame && _currentFrame <= _attackData.HitboxEndFrame)
|
||||
// {
|
||||
// _entityHitBoxes.CheckHitsForCurrentFrame(OnEnemyHit, HitBoxPartType.LArm);
|
||||
// }
|
||||
//
|
||||
// if (_currentFrame >= _attackData.TotalFrames)
|
||||
// {
|
||||
// CompleteOrInterruptAction();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public override DeciderType DeciderType => DeciderType.Position;
|
||||
//
|
||||
// private void OnEnemyHit(Collider enemyCollider)
|
||||
// {
|
||||
// Debug.Log($"Попадание в {enemyCollider.name} на {_currentFrame}-м кадре! Урон: {_attackData.Damage}");
|
||||
// var entityActor = enemyCollider.GetComponentInParent<EntityTimelineActor>();
|
||||
// entityActor.ForceDecideAction(BattleActionType.Stugger);
|
||||
// }
|
||||
//
|
||||
// public void CompleteOrInterruptAction()
|
||||
// {
|
||||
// animator.Play(EntryStateHash, 0, 0f); // Создать None State и кинуть эту штуку туда
|
||||
// animator.Update(0f);
|
||||
//
|
||||
// // TODO С нормальными анимациями не нужен будет рут моушен√
|
||||
// animator.applyRootMotion = false;
|
||||
// animator.transform.localPosition = Vector3.zero;
|
||||
// animator.transform.localRotation = Quaternion.identity;
|
||||
//
|
||||
// Finished = true;
|
||||
// }
|
||||
//
|
||||
// // HoloCopy
|
||||
//
|
||||
// private bool TickHoloCopy(HoloCop holoCop, float tickDelta)
|
||||
// {
|
||||
// if (_currentFrame == 0)
|
||||
// {
|
||||
// holoCop.Animator.applyRootMotion = true;
|
||||
// holoCop.Animator.Play(_attackData.StateHash, 0, 0f);
|
||||
// }
|
||||
//
|
||||
// _currentFrame++;
|
||||
//
|
||||
// if(_currentFrame >= _attackData.TotalFrames)
|
||||
// {
|
||||
// _currentFrame = 0;
|
||||
// holoCop.Animator.applyRootMotion = false;
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
@@ -1,13 +1,13 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Code.OfficeGame.BattleMode.Actions.Deciders;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Code.OfficeGame.BattleMode.Actions
|
||||
{
|
||||
public abstract class BattleAction : MonoBehaviour
|
||||
public abstract class BattleAction
|
||||
{
|
||||
public bool Finished { get; protected set; }
|
||||
public abstract void Tick(float tickDelta);
|
||||
public abstract UniTask<BattleActionDecisionResult> DecideAction();
|
||||
public abstract bool Tick(float tickDelta, EntityProperties entity, BattleActionDecisionResult battleActionDecisionResult, BattleActionData battleActionData);
|
||||
public abstract BattleActionType ActionType { get; }
|
||||
public abstract DeciderType DeciderType { get; }
|
||||
public abstract BattleActionData MountAndGetFrames(EntityProperties entity, BattleActionDecisionResult result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Code.OfficeGame.BattleMode.Actions
|
||||
{
|
||||
public abstract class BattleActionData
|
||||
{
|
||||
public int TotalFrames;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dec8cd0d83354b05a5766a6fd7f3db95
|
||||
timeCreated: 1793314110
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace Code.OfficeGame.BattleMode.Actions
|
||||
{
|
||||
public struct BattleActionDecisionResult
|
||||
{
|
||||
public BattleActionDecisionResultCode ResultCode;
|
||||
public int FrameDuration;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ using System.Threading;
|
||||
using Code.Game.Gameplay.Player;
|
||||
using Code.Infrastructure.Camera;
|
||||
using Code.Infrastructure.Input;
|
||||
using Code.OfficeGame.BattleMode.Actions.Deciders;
|
||||
using Code.OfficeGame.BattleMode.Actions.Player;
|
||||
using Code.OfficeGame.BattleMode.Marker;
|
||||
using Code.OfficeGame.BattleMode.Timeline;
|
||||
@@ -16,79 +17,61 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
{
|
||||
public class BattleActionMove : BattleAction
|
||||
{
|
||||
private CameraStateMachine _cameraStateMachine;
|
||||
private IInputService _inputService;
|
||||
private MovementController _movementController;
|
||||
|
||||
private NavMeshPath _path;
|
||||
private Vector3[] _corners;
|
||||
private int _currentCornerIndex;
|
||||
|
||||
private Vector3 _targetPosition;
|
||||
private LayerMask _navMeshMask;
|
||||
|
||||
private const float StoppingDistance = 0.15f;
|
||||
|
||||
[Inject]
|
||||
public void Construct(
|
||||
CameraStateMachine cameraStateMachine,
|
||||
IInputService inputService,
|
||||
MarkerManager markerManager)
|
||||
public override bool Tick(
|
||||
float tickDelta,
|
||||
EntityProperties entity,
|
||||
BattleActionDecisionResult battleActionDecisionResult,
|
||||
BattleActionData battleActionData)
|
||||
{
|
||||
_markerManager = markerManager;
|
||||
_cameraStateMachine = cameraStateMachine;
|
||||
_inputService = inputService;
|
||||
_movementController = GetComponent<MovementController>();
|
||||
_navMeshMask = LayerMask.GetMask("Environment");
|
||||
}
|
||||
var movementActionData = (MovementBattleActionData) battleActionData;
|
||||
|
||||
var entityMovementController = entity.MovementController;
|
||||
|
||||
public override void Tick(float tickDelta)
|
||||
{
|
||||
if (Finished) return;
|
||||
|
||||
if (_corners == null || _currentCornerIndex >= _corners.Length)
|
||||
if (movementActionData.Corners == null || movementActionData.CurrentCornerIndex >= movementActionData.Corners.Length)
|
||||
{
|
||||
StopMovementAndFinish();
|
||||
return;
|
||||
StopMovementAndFinish(entityMovementController);
|
||||
return true;
|
||||
}
|
||||
|
||||
Vector3 currentPos = _movementController.Motor.TransientPosition;
|
||||
Vector3 currentPos = entity.KinematicCharacterMotor.TransientPosition;
|
||||
|
||||
while (_currentCornerIndex < _corners.Length)
|
||||
while (movementActionData.CurrentCornerIndex < movementActionData.Corners.Length)
|
||||
{
|
||||
Vector3 toCorner = _corners[_currentCornerIndex] - currentPos;
|
||||
Vector3 toCorner = movementActionData.Corners[movementActionData.CurrentCornerIndex] - currentPos;
|
||||
toCorner.y = 0f;
|
||||
|
||||
if (toCorner.sqrMagnitude <= StoppingDistance * StoppingDistance)
|
||||
{
|
||||
_currentCornerIndex++;
|
||||
}
|
||||
movementActionData.CurrentCornerIndex++;
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_currentCornerIndex >= _corners.Length)
|
||||
if (movementActionData.CurrentCornerIndex >= movementActionData.Corners.Length)
|
||||
{
|
||||
StopMovementAndFinish();
|
||||
StopMovementAndFinish(entity.MovementController);
|
||||
KinematicCharacterSystem.Simulate(
|
||||
tickDelta,
|
||||
KinematicCharacterSystem.CharacterMotors,
|
||||
KinematicCharacterSystem.PhysicsMovers
|
||||
);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
Vector3 targetCorner = _corners[_currentCornerIndex];
|
||||
Vector3 targetCorner = movementActionData.Corners[movementActionData.CurrentCornerIndex];
|
||||
Vector3 moveDirection = targetCorner - currentPos;
|
||||
moveDirection.y = 0f;
|
||||
|
||||
float distance = moveDirection.magnitude;
|
||||
Vector3 moveNormal = distance > 0.0001f ? moveDirection / distance : Vector3.zero;
|
||||
|
||||
float maxStep = _movementController.MaxStableMoveSpeed * tickDelta;
|
||||
float inputMagnitude = (_currentCornerIndex == _corners.Length - 1 && distance < maxStep && maxStep > 0f)
|
||||
|
||||
float maxStep = entityMovementController.MaxStableMoveSpeed * tickDelta;
|
||||
float inputMagnitude =
|
||||
(movementActionData.CurrentCornerIndex == movementActionData.Corners.Length - 1
|
||||
&& distance < maxStep
|
||||
&& maxStep > 0f)
|
||||
? distance / maxStep
|
||||
: 1f;
|
||||
|
||||
@@ -97,11 +80,11 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
{
|
||||
MoveVector = moveNormal * inputMagnitude,
|
||||
Look = moveNormal.sqrMagnitude > 0.0001f
|
||||
? Quaternion.LookRotation(moveNormal, _movementController.Motor.CharacterUp)
|
||||
: _movementController.Motor.TransientRotation
|
||||
? Quaternion.LookRotation(moveNormal, entityMovementController.Motor.CharacterUp)
|
||||
: entityMovementController.Motor.TransientRotation
|
||||
};
|
||||
|
||||
_movementController.SetInputs(ref aiInputs);
|
||||
entityMovementController.SetInputs(ref aiInputs);
|
||||
|
||||
KinematicCharacterSystem.Simulate(
|
||||
tickDelta,
|
||||
@@ -109,110 +92,58 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
KinematicCharacterSystem.PhysicsMovers
|
||||
);
|
||||
|
||||
Vector3 postMovePos = _movementController.Motor.TransientPosition;
|
||||
Vector3 toFinal = _corners[^1] - postMovePos;
|
||||
Vector3 postMovePos = entityMovementController.Motor.TransientPosition;
|
||||
Vector3 toFinal = movementActionData.Corners[^1] - postMovePos;
|
||||
toFinal.y = 0f;
|
||||
|
||||
if (_currentCornerIndex >= _corners.Length - 1 && toFinal.sqrMagnitude <= StoppingDistance * StoppingDistance)
|
||||
if (movementActionData.CurrentCornerIndex >= movementActionData.Corners.Length - 1
|
||||
&& toFinal.sqrMagnitude <= StoppingDistance * StoppingDistance)
|
||||
{
|
||||
StopMovementAndFinish();
|
||||
StopMovementAndFinish(entityMovementController);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void StopMovementAndFinish()
|
||||
private void StopMovementAndFinish(MovementController movementController)
|
||||
{
|
||||
Debug.Log("MovementFinished");
|
||||
var emptyInputs = new AICharacterInputs
|
||||
{
|
||||
MoveVector = Vector3.zero,
|
||||
Look = _movementController.Motor.TransientRotation
|
||||
Look = movementController.Motor.TransientRotation
|
||||
};
|
||||
_movementController.SetInputs(ref emptyInputs);
|
||||
_movementController.Motor.BaseVelocity = Vector3.zero;
|
||||
|
||||
Finished = true;
|
||||
}
|
||||
|
||||
private CancellationTokenSource _waitForEnterTokenSource;
|
||||
private BattleActionDecisionResultCode _decisionResultCode;
|
||||
|
||||
public override async UniTask<BattleActionDecisionResult> DecideAction()
|
||||
{
|
||||
_waitForEnterTokenSource = new CancellationTokenSource();
|
||||
_decisionResultCode = BattleActionDecisionResultCode.CancelDecision;
|
||||
_targetPosition = _movementController.transform.position;
|
||||
|
||||
_inputService.BindActionPerformed((int)ActionMappings.BattleMode, (int)BattleModeActions.Select,
|
||||
SelectPosition);
|
||||
_inputService.BindActionPerformed((int)ActionMappings.BattleMode, (int)BattleModeActions.Confirm,
|
||||
ConfirmPosition);
|
||||
_inputService.BindActionPerformed((int)ActionMappings.BattleMode, (int)BattleModeActions.Cancel,
|
||||
CancelPosition);
|
||||
_inputService.BindActionPerformed((int)ActionMappings.BattleMode, (int)BattleModeActions.Watchment,
|
||||
MarkPosition);
|
||||
|
||||
_playerLockedDecisionMarker = _markerManager.GetMarkerInstance();
|
||||
_playerDecisionMarker = _markerManager.GetMarkerInstance();
|
||||
_playerDecisionMarker.Toggle(true);
|
||||
|
||||
await UniTask.WaitUntilCanceled(_waitForEnterTokenSource.Token).SuppressCancellationThrow();
|
||||
|
||||
_playerDecisionMarker.Toggle(false);
|
||||
_markerManager.ReleaseMarkerInstance(_playerDecisionMarker);
|
||||
_markerManager.ReleaseMarkerInstance(_playerLockedDecisionMarker);
|
||||
|
||||
_inputService.UnbindActionPerformed((int)ActionMappings.BattleMode, (int)BattleModeActions.Select,
|
||||
SelectPosition);
|
||||
_inputService.UnbindActionPerformed((int)ActionMappings.BattleMode, (int)BattleModeActions.Confirm,
|
||||
ConfirmPosition);
|
||||
_inputService.UnbindActionPerformed((int)ActionMappings.BattleMode, (int)BattleModeActions.Cancel,
|
||||
CancelPosition);
|
||||
_inputService.UnbindActionPerformed((int)ActionMappings.BattleMode, (int)BattleModeActions.Watchment,
|
||||
MarkPosition);
|
||||
|
||||
_waitForEnterTokenSource.Dispose();
|
||||
_waitForEnterTokenSource = null;
|
||||
|
||||
Finished = _decisionResultCode != BattleActionDecisionResultCode.Decided;
|
||||
|
||||
return new() { ResultCode = _decisionResultCode, FrameDuration = _totalFrames};
|
||||
movementController.SetInputs(ref emptyInputs);
|
||||
movementController.Motor.BaseVelocity = Vector3.zero;
|
||||
}
|
||||
|
||||
public override BattleActionType ActionType => BattleActionType.Move;
|
||||
public override DeciderType DeciderType => DeciderType.Position;
|
||||
|
||||
private int _totalFrames;
|
||||
private MarkerManager _markerManager;
|
||||
|
||||
private void CancelPosition(InputAction.CallbackContext obj)
|
||||
public override BattleActionData MountAndGetFrames(EntityProperties entity, BattleActionDecisionResult result)
|
||||
{
|
||||
_decisionResultCode = BattleActionDecisionResultCode.CancelDecision;
|
||||
_waitForEnterTokenSource?.Cancel();
|
||||
}
|
||||
|
||||
private void ConfirmPosition(InputAction.CallbackContext input)
|
||||
{
|
||||
Vector3 startPos = _movementController.Motor.TransientPosition;
|
||||
|
||||
_path = new();
|
||||
MovementBattleActionData movementBattleActionData = new();
|
||||
|
||||
if ((_targetPosition - startPos).sqrMagnitude > StoppingDistance * StoppingDistance
|
||||
&& NavMesh.CalculatePath(startPos, _targetPosition, NavMesh.AllAreas, _path)
|
||||
&& _path.corners.Length > 0)
|
||||
{
|
||||
_corners = _path.corners;
|
||||
_currentCornerIndex = _corners.Length > 1 ? 1 : 0;
|
||||
_decisionResultCode = BattleActionDecisionResultCode.Decided;
|
||||
_totalFrames = CalculatePathFrames(_corners);
|
||||
}
|
||||
else
|
||||
{
|
||||
_decisionResultCode = BattleActionDecisionResultCode.Deciding;
|
||||
_totalFrames = 0;
|
||||
}
|
||||
Vector3 startPos = entity.KinematicCharacterMotor.TransientPosition;
|
||||
|
||||
_waitForEnterTokenSource?.Cancel();
|
||||
NavMeshPath path = new();
|
||||
|
||||
var positionDecisionResult = (PositionBattleActionDeciderResult) result;
|
||||
|
||||
if ((positionDecisionResult.Position - startPos).sqrMagnitude > StoppingDistance * StoppingDistance
|
||||
&& NavMesh.CalculatePath(startPos, positionDecisionResult.Position, NavMesh.AllAreas, path)
|
||||
&& path.corners.Length > 0)
|
||||
{
|
||||
movementBattleActionData.Corners = path.corners;
|
||||
movementBattleActionData.CurrentCornerIndex = movementBattleActionData.Corners.Length > 1 ? 1 : 0;
|
||||
movementBattleActionData.TotalFrames = CalculatePathFrames(movementBattleActionData.Corners, entity.MovementController);
|
||||
}
|
||||
else movementBattleActionData.TotalFrames = 0;
|
||||
|
||||
return movementBattleActionData;
|
||||
}
|
||||
private int CalculatePathFrames(Vector3[] corners)
|
||||
private int CalculatePathFrames(Vector3[] corners, MovementController movementController)
|
||||
{
|
||||
if (corners == null || corners.Length < 2)
|
||||
return 0;
|
||||
@@ -228,42 +159,12 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
|
||||
float effectiveDistance = Mathf.Max(0f, totalDistanceXZ - StoppingDistance);
|
||||
|
||||
float stepPerFrame = _movementController.MaxStableMoveSpeed * BattleTimeline.TickDelta;
|
||||
float stepPerFrame = movementController.MaxStableMoveSpeed * BattleTimeline.TickDelta;
|
||||
|
||||
if (stepPerFrame <= 0.0001f)
|
||||
return 0;
|
||||
|
||||
// Округляем вверх, так как последний неполный шаг тоже занимает 1 целый кадр
|
||||
return Mathf.CeilToInt(effectiveDistance / stepPerFrame);
|
||||
}
|
||||
|
||||
private RaycastHit[] _hitBuffer = new RaycastHit[1];
|
||||
private Vector3 _lastViewedPosition;
|
||||
private PlayerDecisionMarker _playerDecisionMarker;
|
||||
private PlayerDecisionMarker _playerLockedDecisionMarker;
|
||||
|
||||
private void MarkPosition(InputAction.CallbackContext input)
|
||||
{
|
||||
var position = _cameraStateMachine.Camera.transform.position;
|
||||
var direction = _cameraStateMachine.Camera.transform.forward;
|
||||
|
||||
var hitCount = Physics.RaycastNonAlloc(position, direction, _hitBuffer, Mathf.Infinity, _navMeshMask);
|
||||
|
||||
if (hitCount == 0) return;
|
||||
|
||||
var hit = _hitBuffer[0];
|
||||
|
||||
_playerDecisionMarker.MoveMarker(hit.point);
|
||||
|
||||
_lastViewedPosition = hit.point;
|
||||
}
|
||||
|
||||
private void SelectPosition(InputAction.CallbackContext input)
|
||||
{
|
||||
_targetPosition = _lastViewedPosition;
|
||||
|
||||
_playerLockedDecisionMarker.MoveMarker(_targetPosition);
|
||||
_playerLockedDecisionMarker.Toggle(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,64 +1,64 @@
|
||||
using System.Linq;
|
||||
using Code.Infrastructure.Utils;
|
||||
using Code.OfficeGame.BattleMode.HitBoxes;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Reflex.Attributes;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Code.OfficeGame.BattleMode.Actions
|
||||
{
|
||||
public class BattleActionStugger : BattleAction
|
||||
{
|
||||
private Animator _animator;
|
||||
private EntityHitBoxes _entityHitBoxes;
|
||||
private int EntryStateHash => Animator.StringToHash("WalkBlend");
|
||||
private int StuggerStateHash => Animator.StringToHash("Stugger");
|
||||
|
||||
private BattleActionParameters _actionParameters;
|
||||
|
||||
private int _currentFrame;
|
||||
|
||||
[Inject]
|
||||
public void Construct(MultipleConfig<BattleActionParameters> actionData)
|
||||
{
|
||||
_animator = GetComponentInChildren<Animator>();
|
||||
_entityHitBoxes = GetComponent<EntityHitBoxes>();
|
||||
_actionParameters = actionData.Configs.SingleOrDefault(x => x.ActionType == ActionType);
|
||||
}
|
||||
public override async UniTask<BattleActionDecisionResult> DecideAction()
|
||||
{
|
||||
await UniTask.Yield();
|
||||
|
||||
_currentFrame = 0;
|
||||
Finished = false;
|
||||
|
||||
return new() { ResultCode = BattleActionDecisionResultCode.Decided, FrameDuration = _actionParameters.TotalFrames};
|
||||
|
||||
}
|
||||
|
||||
public override BattleActionType ActionType => BattleActionType.Stugger;
|
||||
|
||||
public override void Tick(float tickDelta)
|
||||
{
|
||||
if (Finished) return;
|
||||
|
||||
if (_currentFrame == 0)
|
||||
{
|
||||
_animator.Play(StuggerStateHash, 0, 0f);
|
||||
}
|
||||
|
||||
_currentFrame++;
|
||||
|
||||
if (_currentFrame >= _actionParameters.TotalFrames)
|
||||
{
|
||||
CompleteOrInterruptAction();
|
||||
}
|
||||
}
|
||||
|
||||
public void CompleteOrInterruptAction()
|
||||
{
|
||||
_animator.Play(EntryStateHash, 0, 0f); // Создать None State и кинуть эту штуку туда
|
||||
Finished = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// using System.Linq;
|
||||
// using Code.Infrastructure.Utils;
|
||||
// using Code.OfficeGame.BattleMode.HitBoxes;
|
||||
// using Cysharp.Threading.Tasks;
|
||||
// using Reflex.Attributes;
|
||||
// using UnityEngine;
|
||||
//
|
||||
// namespace Code.OfficeGame.BattleMode.Actions
|
||||
// {
|
||||
// public class BattleActionStugger : BattleAction
|
||||
// {
|
||||
// private Animator _animator;
|
||||
// private EntityHitBoxes _entityHitBoxes;
|
||||
// private int EntryStateHash => Animator.StringToHash("WalkBlend");
|
||||
// private int StuggerStateHash => Animator.StringToHash("Stugger");
|
||||
//
|
||||
// private BattleActionParameters _actionParameters;
|
||||
//
|
||||
// private int _currentFrame;
|
||||
//
|
||||
// [Inject]
|
||||
// public void Construct(MultipleConfig<BattleActionParameters> actionData)
|
||||
// {
|
||||
// _animator = GetComponentInChildren<Animator>();
|
||||
// _entityHitBoxes = GetComponent<EntityHitBoxes>();
|
||||
// _actionParameters = actionData.Configs.SingleOrDefault(x => x.ActionType == ActionType);
|
||||
// }
|
||||
// public override async UniTask<BattleActionDecisionResult> DecideAction()
|
||||
// {
|
||||
// await UniTask.Yield();
|
||||
//
|
||||
// _currentFrame = 0;
|
||||
// Finished = false;
|
||||
//
|
||||
// return new() { ResultCode = BattleActionDecisionResultCode.Decided, FrameDuration = _actionParameters.TotalFrames};
|
||||
//
|
||||
// }
|
||||
//
|
||||
// public override BattleActionType ActionType => BattleActionType.Stugger;
|
||||
//
|
||||
// public override bool Tick(float tickDelta, EntityProperties entity)
|
||||
// {
|
||||
// if (Finished) return;
|
||||
//
|
||||
// if (_currentFrame == 0)
|
||||
// {
|
||||
// _animator.Play(StuggerStateHash, 0, 0f);
|
||||
// }
|
||||
//
|
||||
// _currentFrame++;
|
||||
//
|
||||
// if (_currentFrame >= _actionParameters.TotalFrames)
|
||||
// {
|
||||
// CompleteOrInterruptAction();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public void CompleteOrInterruptAction()
|
||||
// {
|
||||
// _animator.Play(EntryStateHash, 0, 0f); // Создать None State и кинуть эту штуку туда
|
||||
// Finished = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
@@ -1,31 +1,32 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Code.OfficeGame.BattleMode.Actions.Deciders;
|
||||
|
||||
namespace Code.OfficeGame.BattleMode.Actions
|
||||
{
|
||||
public class BattleActionWait : BattleAction
|
||||
{
|
||||
private int _waitFrames;
|
||||
public override void Tick(float tickDelta)
|
||||
public override bool Tick(float tickDelta,
|
||||
EntityProperties entity,
|
||||
BattleActionDecisionResult battleActionDecisionResult,
|
||||
BattleActionData battleActionData)
|
||||
{
|
||||
_waitFrames--;
|
||||
if (_waitFrames <= 0) Finished = true;
|
||||
}
|
||||
|
||||
public override async UniTask<BattleActionDecisionResult> DecideAction()
|
||||
{
|
||||
Finished = false;
|
||||
var waitActionData = (WaitBattleActionData) battleActionData;
|
||||
|
||||
await UniTask.Yield();
|
||||
|
||||
_waitFrames = 120;
|
||||
waitActionData.WaitFrames--;
|
||||
if (waitActionData.WaitFrames <= 0) return true;
|
||||
|
||||
return new()
|
||||
{
|
||||
ResultCode = BattleActionDecisionResultCode.Decided,
|
||||
FrameDuration = _waitFrames
|
||||
};
|
||||
return false;
|
||||
}
|
||||
|
||||
public override BattleActionType ActionType => BattleActionType.Wait;
|
||||
public override DeciderType DeciderType => DeciderType.AsIs;
|
||||
public override BattleActionData MountAndGetFrames(EntityProperties entity, BattleActionDecisionResult result)
|
||||
{
|
||||
WaitBattleActionData actionData = new()
|
||||
{
|
||||
WaitFrames = 120
|
||||
};
|
||||
|
||||
return actionData;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3a5f1323a1c04e9ead8fa528427c62e0
|
||||
timeCreated: 1793278477
|
||||
@@ -0,0 +1,24 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
namespace Code.OfficeGame.BattleMode.Actions.Deciders
|
||||
{
|
||||
public class AsIsBattleActionDecider : BattleActionDecider
|
||||
{
|
||||
public override DeciderType DeciderType => DeciderType.AsIs;
|
||||
public override async UniTask<BattleActionDecisionResult> GetDecision()
|
||||
{
|
||||
await UniTask.Yield();
|
||||
|
||||
return new AsIsBattleActionDeciderResult()
|
||||
{
|
||||
ResultCode = BattleActionDecisionResultCode.Decided
|
||||
};
|
||||
}
|
||||
|
||||
public override void UnmountDecider()
|
||||
{ }
|
||||
|
||||
public override void MountDecider()
|
||||
{ }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2034bb40c0ea462baeac67d89aef0429
|
||||
timeCreated: 1793316764
|
||||
@@ -0,0 +1,15 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
namespace Code.OfficeGame.BattleMode.Actions.Deciders
|
||||
{
|
||||
public abstract class BattleActionDecider
|
||||
{
|
||||
public abstract DeciderType DeciderType { get; }
|
||||
protected bool IsMounted;
|
||||
public abstract UniTask<BattleActionDecisionResult> GetDecision();
|
||||
|
||||
public abstract void UnmountDecider();
|
||||
|
||||
public abstract void MountDecider();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8b6762673a7f4e40979aa0bd6532f836
|
||||
timeCreated: 1793278644
|
||||
@@ -0,0 +1,22 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Code.OfficeGame.BattleMode.Actions
|
||||
{
|
||||
public abstract class BattleActionDecisionResult
|
||||
{
|
||||
public BattleActionType ActionType;
|
||||
public BattleActionDecisionResultCode ResultCode;
|
||||
}
|
||||
public class PositionBattleActionDeciderResult : BattleActionDecisionResult
|
||||
{
|
||||
public Vector3 Position;
|
||||
public Quaternion Rotation;
|
||||
}
|
||||
public class FrameBattleActionDeciderResult : BattleActionDecisionResult
|
||||
{
|
||||
public int FrameCount;
|
||||
}
|
||||
public class AsIsBattleActionDeciderResult : BattleActionDecisionResult
|
||||
{
|
||||
}
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,142 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Code.Infrastructure.Camera;
|
||||
using Code.Infrastructure.Input;
|
||||
using Code.OfficeGame.BattleMode.Actions.Player;
|
||||
using Code.OfficeGame.BattleMode.Marker;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
namespace Code.OfficeGame.BattleMode.Actions.Deciders
|
||||
{
|
||||
public class BattleActionPositionDecider : BattleActionDecider
|
||||
{
|
||||
private CameraStateMachine _cameraStateMachine;
|
||||
private IInputService _inputService;
|
||||
|
||||
private Vector3 _targetPosition;
|
||||
private LayerMask _navMeshMask;
|
||||
|
||||
private const float StoppingDistance = 0.15f;
|
||||
|
||||
public BattleActionPositionDecider(
|
||||
CameraStateMachine cameraStateMachine,
|
||||
IInputService inputService,
|
||||
MarkerManager markerManager)
|
||||
{
|
||||
_markerManager = markerManager;
|
||||
_cameraStateMachine = cameraStateMachine;
|
||||
_inputService = inputService;
|
||||
_navMeshMask = LayerMask.GetMask("Environment");
|
||||
}
|
||||
|
||||
private CancellationTokenSource _waitForEnterTokenSource;
|
||||
private BattleActionDecisionResultCode _decisionResultCode;
|
||||
|
||||
public override DeciderType DeciderType => DeciderType.Position;
|
||||
|
||||
public override async UniTask<BattleActionDecisionResult> GetDecision()
|
||||
{
|
||||
if (!IsMounted) throw new Exception("Decider is not mounted"); // А почему бы и нет?
|
||||
|
||||
_waitForEnterTokenSource = new CancellationTokenSource();
|
||||
_decisionResultCode = BattleActionDecisionResultCode.CancelDecision;
|
||||
|
||||
await UniTask.WaitUntilCanceled(_waitForEnterTokenSource.Token).SuppressCancellationThrow();
|
||||
|
||||
_waitForEnterTokenSource.Dispose();
|
||||
_waitForEnterTokenSource = null;
|
||||
|
||||
return new PositionBattleActionDeciderResult()
|
||||
{
|
||||
ResultCode = _decisionResultCode,
|
||||
Position = _targetPosition,
|
||||
};
|
||||
}
|
||||
|
||||
public override void UnmountDecider()
|
||||
{
|
||||
if (!IsMounted) return;
|
||||
|
||||
_playerDecisionMarker.Toggle(false);
|
||||
_markerManager.ReleaseMarkerInstance(_playerDecisionMarker);
|
||||
_markerManager.ReleaseMarkerInstance(_playerLockedDecisionMarker);
|
||||
|
||||
_inputService.UnbindActionPerformed((int)ActionMappings.BattleMode, (int)BattleModeActions.Select,
|
||||
SelectPosition);
|
||||
_inputService.UnbindActionPerformed((int)ActionMappings.BattleMode, (int)BattleModeActions.Confirm,
|
||||
ConfirmPosition);
|
||||
_inputService.UnbindActionPerformed((int)ActionMappings.BattleMode, (int)BattleModeActions.Cancel,
|
||||
CancelPosition);
|
||||
_inputService.UnbindActionPerformed((int)ActionMappings.BattleMode, (int)BattleModeActions.Watchment,
|
||||
MarkPosition);
|
||||
|
||||
IsMounted = false;
|
||||
}
|
||||
|
||||
public override void MountDecider()
|
||||
{
|
||||
if (IsMounted) return;
|
||||
|
||||
_inputService.BindActionPerformed((int)ActionMappings.BattleMode, (int)BattleModeActions.Select,
|
||||
SelectPosition);
|
||||
_inputService.BindActionPerformed((int)ActionMappings.BattleMode, (int)BattleModeActions.Confirm,
|
||||
ConfirmPosition);
|
||||
_inputService.BindActionPerformed((int)ActionMappings.BattleMode, (int)BattleModeActions.Cancel,
|
||||
CancelPosition);
|
||||
_inputService.BindActionPerformed((int)ActionMappings.BattleMode, (int)BattleModeActions.Watchment,
|
||||
MarkPosition);
|
||||
|
||||
_playerLockedDecisionMarker = _markerManager.GetMarkerInstance();
|
||||
_playerDecisionMarker = _markerManager.GetMarkerInstance();
|
||||
_playerDecisionMarker.Toggle(true);
|
||||
|
||||
IsMounted = true;
|
||||
}
|
||||
|
||||
private MarkerManager _markerManager;
|
||||
|
||||
private void CancelPosition(InputAction.CallbackContext obj)
|
||||
{
|
||||
_decisionResultCode = BattleActionDecisionResultCode.CancelDecision;
|
||||
_waitForEnterTokenSource?.Cancel();
|
||||
}
|
||||
|
||||
private void ConfirmPosition(InputAction.CallbackContext input)
|
||||
{
|
||||
_decisionResultCode = BattleActionDecisionResultCode.Decided;
|
||||
|
||||
_waitForEnterTokenSource?.Cancel();
|
||||
}
|
||||
|
||||
private RaycastHit[] _hitBuffer = new RaycastHit[1];
|
||||
private Vector3 _lastViewedPosition;
|
||||
private PlayerDecisionMarker _playerDecisionMarker;
|
||||
private PlayerDecisionMarker _playerLockedDecisionMarker;
|
||||
|
||||
private void MarkPosition(InputAction.CallbackContext input)
|
||||
{
|
||||
var position = _cameraStateMachine.Camera.transform.position;
|
||||
var direction = _cameraStateMachine.Camera.transform.forward;
|
||||
|
||||
var hitCount = Physics.RaycastNonAlloc(position, direction, _hitBuffer, Mathf.Infinity, _navMeshMask);
|
||||
|
||||
if (hitCount == 0) return;
|
||||
|
||||
var hit = _hitBuffer[0];
|
||||
|
||||
_playerDecisionMarker.MoveMarker(hit.point);
|
||||
|
||||
_lastViewedPosition = hit.point;
|
||||
}
|
||||
|
||||
private void SelectPosition(InputAction.CallbackContext input)
|
||||
{
|
||||
_targetPosition = _lastViewedPosition;
|
||||
|
||||
_playerLockedDecisionMarker.MoveMarker(_targetPosition);
|
||||
_playerLockedDecisionMarker.Toggle(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 220462a568ce470482b264d105953329
|
||||
timeCreated: 1793278626
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Code.OfficeGame.BattleMode.Actions.Deciders
|
||||
{
|
||||
public enum DeciderType
|
||||
{
|
||||
AsIs,
|
||||
Position
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 534386b84312450f997b33343b402f74
|
||||
timeCreated: 1793278491
|
||||
@@ -0,0 +1,15 @@
|
||||
using Code.Game.Gameplay.Player;
|
||||
using KinematicCharacterController;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Code.OfficeGame.BattleMode.Actions
|
||||
{
|
||||
public class EntityProperties : MonoBehaviour
|
||||
{
|
||||
public Animator animator;
|
||||
public BattleActionType[] battleActionTypes;
|
||||
|
||||
public MovementController MovementController;
|
||||
public KinematicCharacterMotor KinematicCharacterMotor;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8e4a996f851e47ca8fc906e7e89fdca5
|
||||
timeCreated: 1793291498
|
||||
@@ -1,51 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Code.OfficeGame.BattleMode.Timeline;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using R3;
|
||||
using Unity.VisualScripting;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Code.OfficeGame.BattleMode.Actions
|
||||
{
|
||||
public abstract class EntityTimelineActor : MonoBehaviour
|
||||
{
|
||||
public BattleAction CurrentAction { get; protected set;}
|
||||
public bool Finished => CurrentAction == null || CurrentAction.Finished;
|
||||
|
||||
private readonly Subject<TimelineBattleAction> _onActionSet = new();
|
||||
public Observable<TimelineBattleAction> OnActionSet => _onActionSet;
|
||||
|
||||
protected void SetCurrentAction(BattleAction action, int durationInFrames)
|
||||
{
|
||||
CurrentAction = action;
|
||||
|
||||
_onActionSet.OnNext(new TimelineBattleAction
|
||||
{
|
||||
EntityId = gameObject.GetEntityId(),
|
||||
Frame = durationInFrames
|
||||
});
|
||||
}
|
||||
private void OnDestroy()
|
||||
{
|
||||
_onActionSet.Dispose();
|
||||
}
|
||||
|
||||
public abstract UniTask DecideAction(CancellationToken ctx);
|
||||
public abstract UniTaskVoid ForceDecideAction(BattleActionType actionType);
|
||||
|
||||
[DoNotSerialize] public Animator animator;
|
||||
|
||||
protected readonly Dictionary<BattleActionType, BattleAction> BattleActions = new();
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
animator = GetComponentInChildren<Animator>();
|
||||
|
||||
foreach (var action in GetComponents<BattleAction>())
|
||||
{
|
||||
BattleActions.Add(action.ActionType, action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 17efe885cebe451d9cc5db4706f13ffe
|
||||
timeCreated: 1792477417
|
||||
@@ -0,0 +1,14 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI;
|
||||
|
||||
namespace Code.OfficeGame.BattleMode.Actions
|
||||
{
|
||||
public class MovementBattleActionData : BattleActionData
|
||||
{
|
||||
public Vector3 Position;
|
||||
public Quaternion Rotation;
|
||||
public NavMeshPath Path;
|
||||
public Vector3[] Corners;
|
||||
public int CurrentCornerIndex;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 39d3651a2dc5444ca0801b647b60d47e
|
||||
timeCreated: 1793314117
|
||||
@@ -1,62 +0,0 @@
|
||||
using System.Threading;
|
||||
using Code.Game.OfficeGame.BattleMode;
|
||||
using Code.Infrastructure.Camera;
|
||||
using Code.OfficeGame.BattleMode.UI;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Reflex.Attributes;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Code.OfficeGame.BattleMode.Actions.Player
|
||||
{
|
||||
public class PlayerControlledTimelineActor : EntityTimelineActor
|
||||
{
|
||||
[Inject] private BattleActionDecisionPanel _battleActionDecisionPanel;
|
||||
[Inject] private CameraStateMachine _cameraStateMachine;
|
||||
|
||||
public override async UniTask DecideAction(CancellationToken ctx)
|
||||
{
|
||||
_battleActionDecisionPanel.SetEntityInfo(BattleActions);
|
||||
|
||||
BattleActionType decidedAction = BattleActionType.None;
|
||||
|
||||
BattleActionDecisionResult decision = new();
|
||||
|
||||
while (decidedAction == BattleActionType.None || ctx.IsCancellationRequested)
|
||||
{
|
||||
Cursor.lockState = CursorLockMode.Confined;
|
||||
|
||||
((CameraBattleModeState)_cameraStateMachine.CurrentBaseState).battleDeciding = true;
|
||||
|
||||
await _battleActionDecisionPanel.Open(ctx);
|
||||
|
||||
decidedAction = await _battleActionDecisionPanel.GetDecision(ctx);
|
||||
|
||||
while (decidedAction != BattleActionType.None
|
||||
&& !ctx.IsCancellationRequested)
|
||||
{
|
||||
Cursor.lockState = CursorLockMode.Locked;
|
||||
|
||||
((CameraBattleModeState)_cameraStateMachine.CurrentBaseState).battleDeciding = false;
|
||||
|
||||
await _battleActionDecisionPanel.Close(ctx);
|
||||
|
||||
decision = await BattleActions[decidedAction].DecideAction();
|
||||
|
||||
if (decision.ResultCode == BattleActionDecisionResultCode.CancelDecision)
|
||||
decidedAction = BattleActionType.None;
|
||||
|
||||
if (decision.ResultCode != BattleActionDecisionResultCode.Deciding)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SetCurrentAction(BattleActions[decidedAction], decision.FrameDuration);
|
||||
}
|
||||
|
||||
public override async UniTaskVoid ForceDecideAction(BattleActionType actionType)
|
||||
{
|
||||
var decision = await CurrentAction.DecideAction();
|
||||
SetCurrentAction(BattleActions[actionType], decision.FrameDuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f9f637cef8714ef1b5ec779d21630ebd
|
||||
timeCreated: 1792693336
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Code.OfficeGame.BattleMode.Actions
|
||||
{
|
||||
public class WaitBattleActionData : BattleActionData
|
||||
{
|
||||
public int WaitFrames;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1650fc3ffbfd48b0b2bd9a1b31988d8f
|
||||
timeCreated: 1793314128
|
||||
@@ -7,17 +7,26 @@ using Code.Infrastructure.Camera;
|
||||
using Code.Infrastructure.Generators;
|
||||
using Code.Infrastructure.Input;
|
||||
using Code.Infrastructure.SceneLoader;
|
||||
using Code.Infrastructure.UI;
|
||||
using Code.Infrastructure.Utils;
|
||||
using Code.OfficeGame.BattleMode.Actions;
|
||||
using Code.OfficeGame.BattleMode.Actions.Deciders;
|
||||
using Code.OfficeGame.BattleMode.Actions.Player;
|
||||
using Code.OfficeGame.BattleMode.Marker;
|
||||
using Code.OfficeGame.BattleMode.Timeline;
|
||||
using Code.OfficeGame.BattleMode.UI;
|
||||
using Code.OfficeGame.Player;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using KinematicCharacterController;
|
||||
using ObservableCollections;
|
||||
using R3;
|
||||
using Reflex.Core;
|
||||
using Reflex.Enums;
|
||||
using Reflex.Injectors;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.SceneManagement;
|
||||
using Resolution = Reflex.Enums.Resolution;
|
||||
|
||||
namespace Code.Game.OfficeGame.GameStates
|
||||
{
|
||||
@@ -32,7 +41,7 @@ namespace Code.Game.OfficeGame.GameStates
|
||||
|
||||
private BattleTimeline _battleTimeline;
|
||||
private CancellationTokenSource _battleTokenSource = new CancellationTokenSource();
|
||||
|
||||
|
||||
public override async UniTask EnterState()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
@@ -44,23 +53,88 @@ namespace Code.Game.OfficeGame.GameStates
|
||||
|
||||
Context.InputService.EnableMap(ActionMappings.BattleMode);
|
||||
|
||||
var container = Context.SceneContainerManager.ScenesContainers[SceneId.Testing];
|
||||
|
||||
// T
|
||||
var entities =
|
||||
Object.FindObjectsByType<EntityTimelineActor>().ToList();
|
||||
|
||||
////// T
|
||||
var entities =
|
||||
Object.FindObjectsByType<EntityProperties>()
|
||||
.ToList();
|
||||
|
||||
Container container;
|
||||
|
||||
if (Context.StateMachine.CurrentState == AppStates.Testing)
|
||||
{
|
||||
var gameplayContainer = Context.SceneContainerManager.ScenesContainers[SceneId.Testing];
|
||||
|
||||
var waitForContainer =
|
||||
Context.SceneContainerManager.ScenesContainers
|
||||
.ObserveDictionaryAdd()
|
||||
.Where(x => x.Key == SceneId.Battle)
|
||||
.Take(1)
|
||||
.WaitAsync()
|
||||
.AsUniTask();
|
||||
|
||||
var playerDecisionMarkerConfig = new SingleConfig<PlayerDecisionMarkerConfig>();
|
||||
var attackActionsConfigs = new MultipleConfig<BattleActionParameters>();
|
||||
|
||||
await UniTask.WhenAll(
|
||||
attackActionsConfigs.LoadConfigs(),
|
||||
playerDecisionMarkerConfig.LoadConfig());
|
||||
|
||||
BattleActionDecisionPanel battleActionDecisionPanel =
|
||||
(BattleActionDecisionPanel)await Context.UIFactory
|
||||
.LoadPanel(UIPanelType.BattleActionDecision, false);
|
||||
|
||||
BattleTimelinePanel battleTimelinePanel =
|
||||
(BattleTimelinePanel)await Context.UIFactory
|
||||
.LoadPanel(UIPanelType.BattleTimelinePanel, false);
|
||||
|
||||
void InstallExtra(Scene scene, ContainerBuilder builder)
|
||||
{
|
||||
builder.SetParent(gameplayContainer);
|
||||
|
||||
builder.RegisterValue(attackActionsConfigs);
|
||||
builder.RegisterValue(playerDecisionMarkerConfig);
|
||||
|
||||
builder.RegisterType(typeof(MarkerManager), Lifetime.Singleton, Resolution.Eager);
|
||||
|
||||
builder.RegisterValue(battleActionDecisionPanel);
|
||||
builder.RegisterValue(battleTimelinePanel);
|
||||
|
||||
builder.RegisterType(typeof(BattleActionMove), new[]{ typeof(BattleAction) }, Lifetime.Singleton, Resolution.Eager);
|
||||
builder.RegisterType(typeof(BattleActionWait), new[]{ typeof(BattleAction) }, Lifetime.Singleton, Resolution.Eager);
|
||||
|
||||
builder.RegisterType(typeof(BattleActionPositionDecider), new[]{ typeof(BattleActionDecider) } , Lifetime.Singleton, Resolution.Eager);
|
||||
builder.RegisterType(typeof(AsIsBattleActionDecider), new[]{ typeof(BattleActionDecider) } , Lifetime.Singleton, Resolution.Eager);
|
||||
|
||||
builder.RegisterType(typeof(BattleTimeline), Lifetime.Singleton, Resolution.Eager);
|
||||
}
|
||||
|
||||
await UniTask.WhenAll(
|
||||
Context.SceneLoader
|
||||
.LoadSceneAsync(
|
||||
SceneId.Battle,
|
||||
LoadSceneMode.Additive,
|
||||
true,
|
||||
InstallExtra),
|
||||
waitForContainer
|
||||
);
|
||||
|
||||
container = Context.SceneContainerManager.ScenesContainers[SceneId.Battle];
|
||||
|
||||
///// T
|
||||
GameObjectInjector
|
||||
.InjectRecursiveMany(
|
||||
entities.Select(x => x.gameObject).ToList(), container);
|
||||
/////
|
||||
_battleTokenSource = new();
|
||||
|
||||
_battleTimeline = ConstructorInjector.Construct(typeof(BattleTimeline), container) as BattleTimeline;
|
||||
|
||||
|
||||
_battleTimeline = container.Resolve<BattleTimeline>();
|
||||
|
||||
Physics.simulationMode = SimulationMode.Script;
|
||||
KinematicCharacterSystem.EnsureCreation();
|
||||
KinematicCharacterSystem.Settings.AutoSimulation = false;
|
||||
KinematicCharacterSystem.Settings.Interpolate = false;
|
||||
|
||||
|
||||
TogglePlayerBattleMode(container, false);
|
||||
|
||||
await Context.Camera.SwitchState(CameraMode.BattleMode);
|
||||
@@ -70,9 +144,13 @@ namespace Code.Game.OfficeGame.GameStates
|
||||
entity.animator.enabled = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
container = Context.SceneContainerManager.ScenesContainers[SceneId.Battle];
|
||||
}
|
||||
|
||||
_battleTimeline?.RunBattleLoopAsync(entities, _battleTokenSource.Token).Forget();
|
||||
|
||||
_battleTimeline?.RunBattleLoopAsync(entities,_battleTokenSource.Token).Forget();
|
||||
|
||||
await container.Resolve<BattleTimelinePanel>().Open();
|
||||
}
|
||||
|
||||
@@ -81,7 +159,7 @@ namespace Code.Game.OfficeGame.GameStates
|
||||
container.Resolve<EntityAnimator>().Animator.enabled = enabled;
|
||||
container.Resolve<PlayerControlsProvider>().enabled = enabled;
|
||||
}
|
||||
|
||||
|
||||
private void ToggleBattleMode(InputAction.CallbackContext obj)
|
||||
{
|
||||
Context.StateMachine.SwitchState(AppStates.Testing).Forget();
|
||||
@@ -90,36 +168,38 @@ namespace Code.Game.OfficeGame.GameStates
|
||||
public override async UniTask ExitState()
|
||||
{
|
||||
if (Context.StateMachine.NextState == AppStates.Testing)
|
||||
{
|
||||
{
|
||||
_battleTokenSource.Cancel();
|
||||
_battleTokenSource.Dispose();
|
||||
|
||||
|
||||
Physics.simulationMode = SimulationMode.FixedUpdate;
|
||||
KinematicCharacterSystem.Settings.AutoSimulation = true;
|
||||
KinematicCharacterSystem.Settings.Interpolate = true;
|
||||
|
||||
var container = Context.SceneContainerManager.ScenesContainers[SceneId.Testing];
|
||||
|
||||
var container = Context.SceneContainerManager.ScenesContainers[SceneId.Battle];
|
||||
|
||||
TogglePlayerBattleMode(container, true);
|
||||
await container.Resolve<BattleTimelinePanel>().Close();
|
||||
|
||||
|
||||
// T
|
||||
var entities =
|
||||
Object.FindObjectsByType<EntityTimelineActor>().ToList();
|
||||
var entities =
|
||||
Object.FindObjectsByType<EntityProperties>().ToList();
|
||||
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
entity.animator.enabled = false;
|
||||
}
|
||||
|
||||
await Context.SceneLoader.UnloadSceneAsync(SceneId.Battle);
|
||||
}
|
||||
|
||||
|
||||
#if UNITY_EDITOR
|
||||
Context.InputService.DisableMap(ActionMappings.Debug);
|
||||
|
||||
|
||||
Context.InputService.UnbindActionPerformed((int)ActionMappings.Debug, (int)DebugActions.ToggleBattleMode,
|
||||
ToggleBattleMode);
|
||||
#endif
|
||||
|
||||
|
||||
Context.InputService.DisableMap(ActionMappings.BattleMode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using Code.Infrastructure.SceneLoader;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using R3;
|
||||
using Reflex.Core;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Code.OfficeGame.BattleMode
|
||||
{
|
||||
public class BattleStateSceneInstaller: MonoBehaviour, IInstaller, IDisposable
|
||||
{
|
||||
private IDisposable _onContainerBuildDisposable;
|
||||
|
||||
public void InstallBindings(ContainerBuilder containerBuilder)
|
||||
{
|
||||
_onContainerBuildDisposable = Observable
|
||||
.FromEvent<Container>(
|
||||
h => containerBuilder.OnContainerBuilt += h,
|
||||
h => containerBuilder.OnContainerBuilt -= h)
|
||||
.Subscribe(x => OnContainerBuilt(x).Forget());
|
||||
}
|
||||
|
||||
private ISceneContainerManager _sceneContainerManager;
|
||||
private async UniTaskVoid OnContainerBuilt(Container container)
|
||||
{
|
||||
await UniTask.Yield();
|
||||
|
||||
_onContainerBuildDisposable.Dispose();
|
||||
|
||||
_sceneContainerManager = container.Resolve<ISceneContainerManager>();
|
||||
_sceneContainerManager.ScenesContainers.Add(SceneId.Battle, container);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_sceneContainerManager.ScenesContainers.Remove(SceneId.Battle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 76d99016e7074ce2bf981a533ff1d1c2
|
||||
timeCreated: 1793316405
|
||||
@@ -7,63 +7,24 @@ namespace Code.OfficeGame.BattleMode.HitBoxes
|
||||
// TODO Добавить меотды отключения,чтобы не получать урон во время стаггера
|
||||
public class EntityHitBoxes : MonoBehaviour
|
||||
{
|
||||
[Serializable]
|
||||
public struct HitBoxPartCollider
|
||||
{
|
||||
public HitBoxPartType PartType;
|
||||
public BoxCollider Collider;
|
||||
}
|
||||
|
||||
[SerializeField] private HitBoxPartCollider[] bodyParts;
|
||||
[SerializeField] private Dictionary<HitBoxPartType, BoxCollider> bodyParts;
|
||||
private LayerMask _hitBoxLayerMask;
|
||||
|
||||
private readonly Dictionary<HitBoxPartType, BoxCollider> _colliderMap = new();
|
||||
private readonly Collider[] _hitResults = new Collider[8];
|
||||
|
||||
// Список уже поражённых целей за один взмах, чтобы один удар не наносил урон 8 раз за 8 активных кадров
|
||||
private readonly HashSet<GameObject> _alreadyHitTargets = new();
|
||||
|
||||
private HitBoxPartType? _activePart;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
foreach (var part in bodyParts)
|
||||
foreach (var part in bodyParts.Values)
|
||||
{
|
||||
part.Collider.enabled = false;
|
||||
_colliderMap[part.PartType] = part.Collider;
|
||||
part.enabled = false;
|
||||
}
|
||||
_hitBoxLayerMask = LayerMask.GetMask("HitBox");
|
||||
}
|
||||
|
||||
public void EnableHitbox(HitBoxPartType part)
|
||||
public void CheckHitsForCurrentFrame(Action<Collider> onNewTargetHit, HitBoxPartType hitBoxPartType)
|
||||
{
|
||||
if (_activePart == part) return;
|
||||
|
||||
_alreadyHitTargets.Clear();
|
||||
_activePart = part;
|
||||
|
||||
if (_colliderMap.TryGetValue(part, out var col))
|
||||
{
|
||||
col.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void DisableAllHitboxes()
|
||||
{
|
||||
if (_activePart.HasValue && _colliderMap.TryGetValue(_activePart.Value, out var col))
|
||||
{
|
||||
col.enabled = false;
|
||||
}
|
||||
|
||||
_activePart = null;
|
||||
_alreadyHitTargets.Clear();
|
||||
}
|
||||
|
||||
public void CheckHitsForCurrentFrame(Action<Collider> onNewTargetHit)
|
||||
{
|
||||
if (!_activePart.HasValue) return;
|
||||
|
||||
BoxCollider box = _colliderMap[_activePart.Value];
|
||||
BoxCollider box = bodyParts[hitBoxPartType];
|
||||
Transform boxTransform = box.transform;
|
||||
|
||||
// Вычисляем точный мировой центр и размер коллайдера на кости в текущем кадре анимации
|
||||
@@ -98,7 +59,17 @@ namespace Code.OfficeGame.BattleMode.HitBoxes
|
||||
{
|
||||
Head,
|
||||
Chest,
|
||||
Arm,
|
||||
Leg
|
||||
LArm,
|
||||
LElbow,
|
||||
LWrist,
|
||||
LLeg,
|
||||
LKnee,
|
||||
LAnkle,
|
||||
RArm,
|
||||
RElbow,
|
||||
RWrist,
|
||||
RLeg,
|
||||
RKnee,
|
||||
RAnkle
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Code.Game.OfficeGame.BattleMode;
|
||||
using Code.Infrastructure.Camera;
|
||||
using Code.OfficeGame.BattleMode.Actions;
|
||||
using Code.OfficeGame.BattleMode.Actions.Deciders;
|
||||
using Code.OfficeGame.BattleMode.UI;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using R3;
|
||||
@@ -17,14 +22,31 @@ namespace Code.OfficeGame.BattleMode.Timeline
|
||||
public ReadOnlyReactiveProperty<int> CurrentBattleFrame => _currentBattleFrame;
|
||||
|
||||
private readonly BattleTimelinePanel _timelinePanel;
|
||||
private List<EntityTimelineActor> _entities;
|
||||
private List<EntityProperties> _entities;
|
||||
private DisposableBag _battleSubscriptions;
|
||||
|
||||
private Dictionary<BattleActionType, BattleAction> _battleActions;
|
||||
private Dictionary<DeciderType, BattleActionDecider> _battleActionDeciders;
|
||||
|
||||
public BattleTimeline(BattleTimelinePanel timelinePanel)
|
||||
private Dictionary<EntityId, BattleActionDecisionResult> _actionDecisionResults = new();
|
||||
private Dictionary<EntityId, BattleActionData> _battleActionDatas = new();
|
||||
private BattleActionDecisionPanel _battleActionDecisionPanel;
|
||||
private CameraStateMachine _cameraStateMachine;
|
||||
|
||||
public BattleTimeline(
|
||||
BattleTimelinePanel timelinePanel,
|
||||
IEnumerable<BattleAction> battleActions,
|
||||
IEnumerable<BattleActionDecider> battleActionDeciders,
|
||||
BattleActionDecisionPanel battleActionDecisionPanel,
|
||||
CameraStateMachine cameraStateMachine)
|
||||
{
|
||||
_cameraStateMachine = cameraStateMachine;
|
||||
_battleActionDecisionPanel = battleActionDecisionPanel;
|
||||
_timelinePanel = timelinePanel;
|
||||
_battleActions = battleActions.ToDictionary(x=>x.ActionType);
|
||||
_battleActionDeciders = battleActionDeciders.ToDictionary(x => x.DeciderType);
|
||||
}
|
||||
public async UniTask RunBattleLoopAsync(List<EntityTimelineActor> entities, CancellationToken ctx)
|
||||
public async UniTask RunBattleLoopAsync(List<EntityProperties> entities, CancellationToken ctx)
|
||||
{
|
||||
_entities = entities;
|
||||
_currentBattleFrame.Value = 0;
|
||||
@@ -37,20 +59,20 @@ namespace Code.OfficeGame.BattleMode.Timeline
|
||||
.Subscribe(_timelinePanel.OnFrameTick)
|
||||
.AddTo(ref _battleSubscriptions);
|
||||
|
||||
foreach (var entity in entities)
|
||||
entity.OnActionSet
|
||||
.Subscribe(action =>
|
||||
{
|
||||
action.Frame += _currentBattleFrame.Value;
|
||||
_timelinePanel.SetAction(action);
|
||||
})
|
||||
.AddTo(ref _battleSubscriptions);
|
||||
// foreach (var entity in entities)
|
||||
// entity.OnActionSet
|
||||
// .Subscribe(action =>
|
||||
// {
|
||||
// action.Frame += _currentBattleFrame.Value;
|
||||
// _timelinePanel.SetAction(action);
|
||||
// })
|
||||
// .AddTo(ref _battleSubscriptions);
|
||||
|
||||
await DecideActions(_entities, ctx);
|
||||
|
||||
while (!IsBattleOver() && !ctx.IsCancellationRequested)
|
||||
while (!ctx.IsCancellationRequested)
|
||||
{
|
||||
List<EntityTimelineActor> entitiesNeedOrders = await PlayTimelineUntilPause(ctx);
|
||||
List<EntityProperties> entitiesNeedOrders = await PlayTimelineUntilPause(ctx);
|
||||
|
||||
await DecideActions(entitiesNeedOrders, ctx);
|
||||
}
|
||||
@@ -61,19 +83,32 @@ namespace Code.OfficeGame.BattleMode.Timeline
|
||||
}
|
||||
}
|
||||
|
||||
private async UniTask DecideActions(List<EntityTimelineActor> entityActionMakers, CancellationToken ct)
|
||||
private async UniTask DecideActions(List<EntityProperties> entities, CancellationToken ctx)
|
||||
{
|
||||
foreach (var entity in entityActionMakers)
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
Debug.Log($"Deciding action for {entity.name}");
|
||||
await entity.DecideAction(ct);
|
||||
|
||||
var entityId = entity.GetEntityId();
|
||||
|
||||
_actionDecisionResults.Remove(entityId);
|
||||
|
||||
_actionDecisionResults[entityId] = await DecideAction(ctx, entity);
|
||||
|
||||
_battleActionDatas.Remove(entityId);
|
||||
|
||||
var decidedAction = _actionDecisionResults[entityId];
|
||||
|
||||
_battleActionDatas[entityId] =
|
||||
_battleActions[decidedAction.ActionType]
|
||||
.MountAndGetFrames(entity, decidedAction);
|
||||
}
|
||||
}
|
||||
|
||||
private async UniTask<List<EntityTimelineActor>> PlayTimelineUntilPause(CancellationToken ctx)
|
||||
private async UniTask<List<EntityProperties>> PlayTimelineUntilPause(CancellationToken ctx)
|
||||
{
|
||||
float accumulator = 0f;
|
||||
var readyEntities = new List<EntityTimelineActor>();
|
||||
var readyEntities = new List<EntityProperties>();
|
||||
|
||||
while (readyEntities.Count == 0 && !ctx.IsCancellationRequested)
|
||||
{
|
||||
@@ -93,16 +128,24 @@ namespace Code.OfficeGame.BattleMode.Timeline
|
||||
return readyEntities;
|
||||
}
|
||||
|
||||
private void SimulateSingleFrame(List<EntityTimelineActor> outReadyEntities)
|
||||
private void SimulateSingleFrame(List<EntityProperties> outReadyEntities)
|
||||
{
|
||||
_currentBattleFrame.Value++;
|
||||
|
||||
foreach (var entity in _entities)
|
||||
{
|
||||
entity.CurrentAction.Tick(TickDelta);
|
||||
var entityId = entity.GetEntityId();
|
||||
|
||||
var finished = _battleActions[_actionDecisionResults[entityId].ActionType]
|
||||
.Tick(
|
||||
TickDelta,
|
||||
entity,
|
||||
_actionDecisionResults[entityId],
|
||||
_battleActionDatas[entityId]);
|
||||
|
||||
entity.animator.Update(TickDelta);
|
||||
|
||||
if (entity.Finished)
|
||||
if (finished)
|
||||
{
|
||||
outReadyEntities.Add(entity);
|
||||
}
|
||||
@@ -112,6 +155,55 @@ namespace Code.OfficeGame.BattleMode.Timeline
|
||||
Physics.SyncTransforms();
|
||||
}
|
||||
|
||||
private bool IsBattleOver() => false;
|
||||
private async UniTask<BattleActionDecisionResult> DecideAction(CancellationToken ctx, EntityProperties entity)
|
||||
{
|
||||
_battleActionDecisionPanel.SetEntityInfo(entity.battleActionTypes);
|
||||
|
||||
BattleActionType decidedAction = BattleActionType.None;
|
||||
|
||||
BattleActionDecisionResult decision = null;
|
||||
|
||||
while (decidedAction == BattleActionType.None || ctx.IsCancellationRequested)
|
||||
{
|
||||
Cursor.lockState = CursorLockMode.Confined;
|
||||
|
||||
((CameraBattleModeState)_cameraStateMachine.CurrentBaseState).battleDeciding = true;
|
||||
|
||||
await _battleActionDecisionPanel.Open(ctx);
|
||||
|
||||
decidedAction = await _battleActionDecisionPanel.GetDecision(ctx);
|
||||
|
||||
if (decidedAction == BattleActionType.None) continue;
|
||||
|
||||
var deciderType = _battleActions[decidedAction].DeciderType;
|
||||
|
||||
_battleActionDeciders[deciderType].MountDecider();
|
||||
|
||||
while (!ctx.IsCancellationRequested)
|
||||
{
|
||||
Cursor.lockState = CursorLockMode.Locked;
|
||||
|
||||
((CameraBattleModeState)_cameraStateMachine.CurrentBaseState).battleDeciding = false;
|
||||
|
||||
await _battleActionDecisionPanel.Close(ctx);
|
||||
|
||||
decision = await _battleActionDeciders[deciderType].GetDecision();
|
||||
|
||||
if (decision.ResultCode == BattleActionDecisionResultCode.CancelDecision)
|
||||
decidedAction = BattleActionType.None;
|
||||
|
||||
if (decision.ResultCode != BattleActionDecisionResultCode.Deciding)
|
||||
break;
|
||||
}
|
||||
|
||||
_battleActionDeciders[deciderType].UnmountDecider();
|
||||
}
|
||||
|
||||
if (decision == null) throw new ArgumentNullException(nameof(decision));
|
||||
|
||||
decision.ActionType = decidedAction;
|
||||
|
||||
return decision;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,13 +20,13 @@ namespace Code.OfficeGame.BattleMode.UI
|
||||
private MotionHandle _motionHandle;
|
||||
private UniTaskCompletionSource<BattleActionType> _decisionCompletionSource;
|
||||
|
||||
public void SetEntityInfo(Dictionary<BattleActionType, BattleAction> battleActions)
|
||||
public void SetEntityInfo(BattleActionType[] battleActions)
|
||||
{
|
||||
foreach (var button in _actionButtons) Destroy(button.gameObject);
|
||||
|
||||
_actionButtons.Clear();
|
||||
|
||||
foreach (var action in battleActions.Keys)
|
||||
foreach (var action in battleActions)
|
||||
{
|
||||
var button = Instantiate(actionButtonPrefab, transform);
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace Code.Infrastructure.SceneLoader
|
||||
public enum SceneId
|
||||
{
|
||||
Testing,
|
||||
Battle,
|
||||
FirstTestingSegment,
|
||||
SecondTestingSegment
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user