Files
Hasebe/Assets/Code/OfficeGame/BattleMode/Actions/BattleActionMove.cs
T
2026-10-23 00:57:36 +03:00

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;
}
}
}