Files
Hasebe/Assets/Code/OfficeGame/BattleMode/Actions/BattleActionMove.cs
T
2026-10-30 02:58:51 +03:00

170 lines
6.7 KiB
C#

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;
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 const float StoppingDistance = 0.15f;
public override bool Tick(
float tickDelta,
EntityProperties entity,
BattleActionDecisionResult battleActionDecisionResult,
BattleActionData battleActionData)
{
var movementActionData = (MovementBattleActionData) battleActionData;
var entityMovementController = entity.MovementController;
if (movementActionData.Corners == null || movementActionData.CurrentCornerIndex >= movementActionData.Corners.Length)
{
StopMovementAndFinish(entityMovementController);
return true;
}
Vector3 currentPos = entity.KinematicCharacterMotor.TransientPosition;
while (movementActionData.CurrentCornerIndex < movementActionData.Corners.Length)
{
Vector3 toCorner = movementActionData.Corners[movementActionData.CurrentCornerIndex] - currentPos;
toCorner.y = 0f;
if (toCorner.sqrMagnitude <= StoppingDistance * StoppingDistance)
movementActionData.CurrentCornerIndex++;
else
break;
}
if (movementActionData.CurrentCornerIndex >= movementActionData.Corners.Length)
{
StopMovementAndFinish(entity.MovementController);
KinematicCharacterSystem.Simulate(
tickDelta,
KinematicCharacterSystem.CharacterMotors,
KinematicCharacterSystem.PhysicsMovers
);
return true;
}
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 = entityMovementController.MaxStableMoveSpeed * tickDelta;
float inputMagnitude =
(movementActionData.CurrentCornerIndex == movementActionData.Corners.Length - 1
&& distance < maxStep
&& maxStep > 0f)
? distance / maxStep
: 1f;
// TODO Для последнего кадра должен быть особый Look совпадающий с маркером
var aiInputs = new AICharacterInputs
{
MoveVector = moveNormal * inputMagnitude,
Look = moveNormal.sqrMagnitude > 0.0001f
? Quaternion.LookRotation(moveNormal, entityMovementController.Motor.CharacterUp)
: entityMovementController.Motor.TransientRotation
};
entityMovementController.SetInputs(ref aiInputs);
KinematicCharacterSystem.Simulate(
tickDelta,
KinematicCharacterSystem.CharacterMotors,
KinematicCharacterSystem.PhysicsMovers
);
Vector3 postMovePos = entityMovementController.Motor.TransientPosition;
Vector3 toFinal = movementActionData.Corners[^1] - postMovePos;
toFinal.y = 0f;
if (movementActionData.CurrentCornerIndex >= movementActionData.Corners.Length - 1
&& toFinal.sqrMagnitude <= StoppingDistance * StoppingDistance)
{
StopMovementAndFinish(entityMovementController);
return true;
}
return false;
}
private void StopMovementAndFinish(MovementController movementController)
{
Debug.Log("MovementFinished");
var emptyInputs = new AICharacterInputs
{
MoveVector = Vector3.zero,
Look = movementController.Motor.TransientRotation
};
movementController.SetInputs(ref emptyInputs);
movementController.Motor.BaseVelocity = Vector3.zero;
}
public override BattleActionType ActionType => BattleActionType.Move;
public override DeciderType DeciderType => DeciderType.Position;
public override BattleActionData MountAndGetFrames(EntityProperties entity, BattleActionDecisionResult result)
{
MovementBattleActionData movementBattleActionData = new();
Vector3 startPos = entity.KinematicCharacterMotor.TransientPosition;
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, MovementController movementController)
{
if (corners == null || corners.Length < 2)
return 0;
float totalDistanceXZ = 0f;
for (int i = 1; i < corners.Length; i++)
{
Vector3 segment = corners[i] - corners[i - 1];
segment.y = 0f;
totalDistanceXZ += segment.magnitude;
}
float effectiveDistance = Mathf.Max(0f, totalDistanceXZ - StoppingDistance);
float stepPerFrame = movementController.MaxStableMoveSpeed * BattleTimeline.TickDelta;
if (stepPerFrame <= 0.0001f)
return 0;
return Mathf.CeilToInt(effectiveDistance / stepPerFrame);
}
}
}