HitboxDisplay lots of broken stuff

This commit is contained in:
ThomasCharester committed 2026-11-09 02:51:36 +03:00
1 parent 9203f269db
commit f40a10912c
78 files changed
+12768 -309066

No files matched your search

@@ -11,27 +11,151 @@ namespace Code.OfficeGame.BattleMode.Actions
{
private const float StoppingDistance = 0.15f;
private const float RotateDistance = 1f;
private const float NavMeshSampleRadius = 1.5f;
private const float TurnSpeedDegPerSec = 720f; // Скорость вращения на месте (градусов/сек)
private int MoveStateHash => Animator.StringToHash("WalkBlend");
public override BattleActionType ActionType => BattleActionType.Move;
public override DeciderType DeciderType => DeciderType.Position;
public override BattleActionData MountAndGetFrames(EntityProperties entity, BattleActionDecisionResult result)
{
MovementBattleActionData data = new();
var positionDecisionResult = (PositionBattleActionDeciderResult)result;
Vector3 startPos = entity.KinematicCharacterMotor.TransientPosition;
Vector3 targetPos = positionDecisionResult.Position;
// 1. Проверяем дистанцию в плоскости XZ
Vector3 toTargetXZ = targetPos - startPos;
toTargetXZ.y = 0f;
float distanceXZ = toTargetXZ.magnitude;
// ЕСЛИ ВПЛОТНУЮ: отключаем перемещение, оставляем только поворот
if (distanceXZ <= StoppingDistance)
{
data.IsRotateOnly = true;
data.Corners = null;
float angle = Quaternion.Angle(entity.KinematicCharacterMotor.TransientRotation, positionDecisionResult.Rotation);
// Если персонаж уже смотрит в нужную сторону — 0 кадров
if (angle < 1f)
{
data.TotalFrames = 0;
}
else
{
float stepPerFrame = TurnSpeedDegPerSec * BattleTimeline.TickDelta;
data.TotalFrames = Mathf.Max(2, Mathf.CeilToInt(angle / stepPerFrame));
}
return data;
}
// 2. ДВИЖЕНИЕ: проецируем точки строго на полигоны NavMesh
bool hasStart = NavMesh.SamplePosition(startPos, out NavMeshHit startHit, NavMeshSampleRadius, NavMesh.AllAreas);
bool hasTarget = NavMesh.SamplePosition(targetPos, out NavMeshHit targetHit, NavMeshSampleRadius, NavMesh.AllAreas);
if (!hasStart || !hasTarget)
{
data.TotalFrames = 0;
return data;
}
Vector3 validStart = startHit.position;
Vector3 validTarget = targetHit.position;
// 3. Прямая видимость: если препятствий нет, идём по идеальной прямой без квантования полигонов NavMesh
if (!NavMesh.Raycast(validStart, validTarget, out _, NavMesh.AllAreas))
{
data.Corners = new[] { startPos, validTarget };
data.CurrentCornerIndex = 1;
data.TotalFrames = CalculatePathFrames(data.Corners, entity.MovementController);
return data;
}
// 4. Обход препятствий: строим путь и отсекаем частичные обрубки (PathPartial)
NavMeshPath path = new();
if (NavMesh.CalculatePath(validStart, validTarget, NavMesh.AllAreas, path)
&& path.status == NavMeshPathStatus.PathComplete
&& path.corners.Length > 1)
{
// Привязываем нулевую и финальную точки к фактическим координатам
path.corners[0] = startPos;
path.corners[^1] = validTarget;
data.Corners = path.corners;
data.CurrentCornerIndex = 1;
data.TotalFrames = CalculatePathFrames(data.Corners, entity.MovementController);
}
else
{
// Если NavMesh не смог построить полный путь (PathPartial/Invalid) — не даём ходить огрызками
data.TotalFrames = 0;
}
return data;
}
public override void Tick(
float tickDelta,
EntityProperties entity,
BattleActionDecisionResult battleActionDecisionResult,
BattleActionData battleActionData)
{
if(battleActionData is not MovementBattleActionData movementActionData
if (battleActionData is not MovementBattleActionData movementActionData
|| battleActionDecisionResult is not PositionBattleActionDeciderResult positionDecisionResult)
{
battleActionData.Finished = true;
return;
}
var entityMovementController = entity.MovementController;
var movementController = entity.MovementController;
// -------------------------------------------------------------
// РЕЖИМ 1: ТОЛЬКО ПОВОРОТ НА МЕСТЕ (КОГДА ПОЗИЦИЯ ВПЛОТНУЮ)
// -------------------------------------------------------------
if (movementActionData.IsRotateOnly)
{
movementActionData.CurrentFrame++;
float angle = Quaternion.Angle(movementController.Motor.TransientRotation, positionDecisionResult.Rotation);
int framesLeft = Mathf.Max(1, movementActionData.TotalFrames - movementActionData.CurrentFrame + 1);
float stepAngle = angle / framesLeft;
Quaternion nextRot = Quaternion.RotateTowards(
movementController.Motor.TransientRotation,
positionDecisionResult.Rotation,
stepAngle
);
entity.KinematicCharacterMotor.SetRotation(nextRot);
StopMovementAndFinish(movementController);
KinematicCharacterSystem.Simulate(
tickDelta,
KinematicCharacterSystem.CharacterMotors,
KinematicCharacterSystem.PhysicsMovers
);
if (movementActionData.CurrentFrame >= movementActionData.TotalFrames || angle <= 0.5f)
{
entity.KinematicCharacterMotor.SetRotation(positionDecisionResult.Rotation);
battleActionData.Finished = true;
}
return;
}
// -------------------------------------------------------------
// РЕЖИМ 2: ПЕРЕДВИЖЕНИЕ ПО ПУТИ
// -------------------------------------------------------------
if (movementActionData.Corners == null || movementActionData.CurrentCornerIndex >= movementActionData.Corners.Length)
{
StopMovementAndFinish(entityMovementController);
entity.KinematicCharacterMotor.SetRotation(positionDecisionResult.Rotation);
StopMovementAndFinish(movementController);
battleActionData.Finished = true;
return;
}
@@ -39,14 +163,13 @@ namespace Code.OfficeGame.BattleMode.Actions
if (!movementActionData.WasStarted)
{
movementActionData.WasStarted = true;
float transitionDurationSeconds = 10 * BattleTimeline.TickDelta;
entity.animator.CrossFadeInFixedTime(MoveStateHash, transitionDurationSeconds);
}
Vector3 currentPos = entity.KinematicCharacterMotor.TransientPosition;
// Пропускаем пройденные углы
while (movementActionData.CurrentCornerIndex < movementActionData.Corners.Length)
{
Vector3 toCorner = movementActionData.Corners[movementActionData.CurrentCornerIndex] - currentPos;
@@ -58,53 +181,50 @@ namespace Code.OfficeGame.BattleMode.Actions
break;
}
// Если все точки пройдены до шага симуляции
if (movementActionData.CurrentCornerIndex >= movementActionData.Corners.Length)
{
StopMovementAndFinish(entity.MovementController);
entity.KinematicCharacterMotor.SetRotation(positionDecisionResult.Rotation);
StopMovementAndFinish(movementController);
KinematicCharacterSystem.Simulate(
tickDelta,
KinematicCharacterSystem.CharacterMotors,
KinematicCharacterSystem.PhysicsMovers
);
battleActionData.Finished = true;
return;
}
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;
bool preLastPoint = movementActionData.CurrentCornerIndex >= movementActionData.Corners.Length - 1
&& distance < RotateDistance;
float maxStep = entityMovementController.MaxStableMoveSpeed * tickDelta;
float inputMagnitude =
(movementActionData.CurrentCornerIndex == movementActionData.Corners.Length - 1
&& distance < maxStep
&& maxStep > 0f)
? distance / maxStep
: 1f;
Quaternion lookRotation;
if(preLastPoint) lookRotation = positionDecisionResult.Rotation;
else lookRotation = moveNormal.sqrMagnitude > 0.0001f
? Quaternion.LookRotation(moveNormal, entityMovementController.Motor.CharacterUp)
: entityMovementController.Motor.TransientRotation;
bool isLastCorner = movementActionData.CurrentCornerIndex == movementActionData.Corners.Length - 1;
bool preLastPoint = isLastCorner && distance < RotateDistance;
float maxStep = movementController.MaxStableMoveSpeed * tickDelta;
float inputMagnitude = (isLastCorner && distance < maxStep && maxStep > 0f)
? distance / maxStep
: 1f;
Quaternion lookRotation = preLastPoint
? positionDecisionResult.Rotation
: (moveNormal.sqrMagnitude > 0.0001f
? Quaternion.LookRotation(moveNormal, movementController.Motor.CharacterUp)
: movementController.Motor.TransientRotation);
var aiInputs = new AICharacterInputs
{
MoveVector = moveNormal * inputMagnitude,
Look = lookRotation
};
entityMovementController.SetInputs(ref aiInputs);
movementController.SetInputs(ref aiInputs);
KinematicCharacterSystem.Simulate(
tickDelta,
@@ -112,15 +232,15 @@ namespace Code.OfficeGame.BattleMode.Actions
KinematicCharacterSystem.PhysicsMovers
);
Vector3 postMovePos = entityMovementController.Motor.TransientPosition;
// Проверка достижения конечной точки после физического тика
Vector3 postMovePos = movementController.Motor.TransientPosition;
Vector3 toFinal = movementActionData.Corners[^1] - postMovePos;
toFinal.y = 0f;
if (movementActionData.CurrentCornerIndex >= movementActionData.Corners.Length - 1
&& toFinal.sqrMagnitude <= StoppingDistance * StoppingDistance)
if (isLastCorner && toFinal.sqrMagnitude <= StoppingDistance * StoppingDistance)
{
entity.KinematicCharacterMotor.SetRotation(positionDecisionResult.Rotation);
StopMovementAndFinish(entityMovementController);
StopMovementAndFinish(movementController);
battleActionData.Finished = true;
}
}
@@ -136,32 +256,6 @@ namespace Code.OfficeGame.BattleMode.Actions
movementController.Motor.BaseVelocity = Vector3.zero;
}
public override BattleActionType ActionType => BattleActionType.Move;
public override DeciderType DeciderType => DeciderType.Position;
// TODO всегда выдаёт разное количество кадров
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)
@@ -174,17 +268,15 @@ namespace Code.OfficeGame.BattleMode.Actions
Vector3 segment = corners[i] - corners[i - 1];
segment.y = 0f;
totalDistanceXZ += segment.magnitude;
Debug.Log($"{i} corner is {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);
}
return Mathf.Max(1, Mathf.CeilToInt(effectiveDistance / stepPerFrame));
}
}
}