187 lines
6.6 KiB
C#
187 lines
6.6 KiB
C#
using System.Threading;
|
|
using Code.Game.Gameplay.Player;
|
|
using Code.Infrastructure.Camera;
|
|
using Code.Infrastructure.Input;
|
|
using Cysharp.Threading.Tasks;
|
|
using KinematicCharacterController;
|
|
using Reflex.Attributes;
|
|
using UnityEngine;
|
|
using UnityEngine.AI;
|
|
using UnityEngine.InputSystem;
|
|
|
|
namespace Code.OfficeGame.BattleMode.Actions
|
|
{
|
|
public class BattleActionMove : BattleAction
|
|
{
|
|
private CameraStateMachine _cameraStateMachine;
|
|
private IInputService _inputService;
|
|
private PlayerMovementController _playerMovementController;
|
|
|
|
private readonly NavMeshPath _path = new();
|
|
private Vector3[] _corners;
|
|
private int _currentCornerIndex;
|
|
|
|
private Vector3 _targetPosition;
|
|
private LayerMask _navMeshMask;
|
|
|
|
private const float StoppingDistance = 0.15f;
|
|
|
|
public BattleActionMove(
|
|
CameraStateMachine cameraStateMachine,
|
|
IInputService inputService,
|
|
PlayerMovementController playerMovementController)
|
|
{
|
|
_cameraStateMachine = cameraStateMachine;
|
|
_inputService = inputService;
|
|
_playerMovementController = playerMovementController;
|
|
_navMeshMask = LayerMask.GetMask("Environment");
|
|
}
|
|
|
|
public override void Tick(float tickDelta)
|
|
{
|
|
if (Finished) return;
|
|
|
|
if (_corners == null || _currentCornerIndex >= _corners.Length)
|
|
{
|
|
StopMovementAndFinish();
|
|
return;
|
|
}
|
|
|
|
Vector3 currentPos = _playerMovementController.Motor.TransientPosition;
|
|
|
|
while (_currentCornerIndex < _corners.Length)
|
|
{
|
|
Vector3 toCorner = _corners[_currentCornerIndex] - currentPos;
|
|
toCorner.y = 0f;
|
|
|
|
if (toCorner.sqrMagnitude <= StoppingDistance * StoppingDistance)
|
|
{
|
|
_currentCornerIndex++;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (_currentCornerIndex >= _corners.Length)
|
|
{
|
|
StopMovementAndFinish();
|
|
KinematicCharacterSystem.Simulate(
|
|
tickDelta,
|
|
KinematicCharacterSystem.CharacterMotors,
|
|
KinematicCharacterSystem.PhysicsMovers
|
|
);
|
|
return;
|
|
}
|
|
|
|
Vector3 targetCorner = _corners[_currentCornerIndex];
|
|
Vector3 moveDirection = targetCorner - currentPos;
|
|
moveDirection.y = 0f;
|
|
|
|
float distance = moveDirection.magnitude;
|
|
Vector3 moveNormal = distance > 0.0001f ? moveDirection / distance : Vector3.zero;
|
|
|
|
float maxStep = _playerMovementController.MaxStableMoveSpeed * tickDelta;
|
|
float inputMagnitude = (_currentCornerIndex == _corners.Length - 1 && distance < maxStep && maxStep > 0f)
|
|
? distance / maxStep
|
|
: 1f;
|
|
|
|
var aiInputs = new AICharacterInputs
|
|
{
|
|
MoveVector = moveNormal * inputMagnitude,
|
|
Look = moveNormal.sqrMagnitude > 0.0001f
|
|
? Quaternion.LookRotation(moveNormal, _playerMovementController.Motor.CharacterUp)
|
|
: _playerMovementController.Motor.TransientRotation
|
|
};
|
|
|
|
_playerMovementController.SetInputs(ref aiInputs);
|
|
|
|
KinematicCharacterSystem.Simulate(
|
|
tickDelta,
|
|
KinematicCharacterSystem.CharacterMotors,
|
|
KinematicCharacterSystem.PhysicsMovers
|
|
);
|
|
|
|
Vector3 postMovePos = _playerMovementController.Motor.TransientPosition;
|
|
Vector3 toFinal = _corners[^1] - postMovePos;
|
|
toFinal.y = 0f;
|
|
|
|
if (_currentCornerIndex >= _corners.Length - 1 && toFinal.sqrMagnitude <= StoppingDistance * StoppingDistance)
|
|
{
|
|
StopMovementAndFinish();
|
|
}
|
|
}
|
|
|
|
private void StopMovementAndFinish()
|
|
{
|
|
var emptyInputs = new AICharacterInputs
|
|
{
|
|
MoveVector = Vector3.zero,
|
|
Look = _playerMovementController.Motor.TransientRotation
|
|
};
|
|
_playerMovementController.SetInputs(ref emptyInputs);
|
|
_playerMovementController.Motor.BaseVelocity = Vector3.zero;
|
|
|
|
Finished = true;
|
|
}
|
|
|
|
private CancellationTokenSource _waitForEnterTokenSource;
|
|
private bool _positionChosen;
|
|
|
|
public override async UniTask<bool> DecideAction()
|
|
{
|
|
_waitForEnterTokenSource = new CancellationTokenSource();
|
|
_positionChosen = false;
|
|
_targetPosition = _playerMovementController.transform.position;
|
|
|
|
_inputService.BindActionPerformed((int)ActionMappings.BattleMode, (int)BattleModeActions.Select,
|
|
SelectPosition);
|
|
_inputService.BindActionPerformed((int)ActionMappings.BattleMode, (int)BattleModeActions.Confirm,
|
|
ConfirmPosition);
|
|
|
|
await UniTask.WaitUntilCanceled(_waitForEnterTokenSource.Token).SuppressCancellationThrow();
|
|
|
|
_inputService.UnbindActionPerformed((int)ActionMappings.BattleMode, (int)BattleModeActions.Select,
|
|
SelectPosition);
|
|
_inputService.UnbindActionPerformed((int)ActionMappings.BattleMode, (int)BattleModeActions.Confirm,
|
|
ConfirmPosition);
|
|
|
|
_waitForEnterTokenSource.Dispose();
|
|
_waitForEnterTokenSource = null;
|
|
|
|
Finished = !_positionChosen;
|
|
return _positionChosen;
|
|
}
|
|
|
|
private void ConfirmPosition(InputAction.CallbackContext input)
|
|
{
|
|
Vector3 startPos = _playerMovementController.Motor.TransientPosition;
|
|
|
|
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;
|
|
_positionChosen = true;
|
|
}
|
|
|
|
_waitForEnterTokenSource?.Cancel();
|
|
}
|
|
|
|
private void SelectPosition(InputAction.CallbackContext input)
|
|
{
|
|
var position = _cameraStateMachine.Camera.transform.position;
|
|
var direction = _cameraStateMachine.Camera.transform.forward;
|
|
|
|
var didHit = Physics.Raycast(position, direction, out RaycastHit hit, Mathf.Infinity, _navMeshMask);
|
|
|
|
if (!didHit) return;
|
|
|
|
Debug.Log($"Hit {hit.point}");
|
|
|
|
_targetPosition = hit.point;
|
|
}
|
|
}
|
|
} |