Something fixed
This commit is contained in:
1 parent
f40a10912c
commit
7d236d14cd
72 files changed
+270482
-11409
No files matched your search
@@ -9,7 +9,8 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
var snapshot = new AttackBattleActionData()
|
||||
{
|
||||
TotalFrames = TotalFrames,
|
||||
CurrentFrame = CurrentFrame
|
||||
CurrentFrame = CurrentFrame,
|
||||
TransitionFrames = TransitionFrames,
|
||||
};
|
||||
|
||||
return snapshot;
|
||||
@@ -21,6 +22,7 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
|
||||
TotalFrames = neededSnapshot.TotalFrames;
|
||||
CurrentFrame = neededSnapshot.CurrentFrame;
|
||||
TransitionFrames = neededSnapshot.TransitionFrames;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,10 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
Rotation = Rotation,
|
||||
Path = Path,
|
||||
Corners = cornersCopy,
|
||||
CurrentCornerIndex = CurrentCornerIndex
|
||||
WasStarted = WasStarted,
|
||||
CurrentCornerIndex = CurrentCornerIndex,
|
||||
IsRotateOnly = IsRotateOnly,
|
||||
CurrentFrame = CurrentFrame
|
||||
};
|
||||
}
|
||||
|
||||
@@ -41,10 +44,13 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
|
||||
if (snapshot is not MovementBattleActionData moveSnapshot) return;
|
||||
|
||||
CurrentFrame = moveSnapshot.CurrentFrame;
|
||||
Position = moveSnapshot.Position;
|
||||
Rotation = moveSnapshot.Rotation;
|
||||
Path = moveSnapshot.Path;
|
||||
CurrentCornerIndex = moveSnapshot.CurrentCornerIndex;
|
||||
WasStarted = moveSnapshot.WasStarted;
|
||||
IsRotateOnly = moveSnapshot.IsRotateOnly;
|
||||
|
||||
if (moveSnapshot.Corners != null)
|
||||
{
|
||||
|
||||
@@ -9,7 +9,8 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
var snapshot = new AttackBattleActionData()
|
||||
{
|
||||
TotalFrames = TotalFrames,
|
||||
CurrentFrame = CurrentFrame
|
||||
CurrentFrame = CurrentFrame,
|
||||
TransitionFrames = TransitionFrames,
|
||||
};
|
||||
|
||||
return snapshot;
|
||||
@@ -21,6 +22,7 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
|
||||
TotalFrames = neededSnapshot.TotalFrames;
|
||||
CurrentFrame = neededSnapshot.CurrentFrame;
|
||||
TransitionFrames = neededSnapshot.TransitionFrames;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,8 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
return new WaitBattleActionData
|
||||
{
|
||||
TotalFrames = TotalFrames,
|
||||
WaitFrames = WaitFrames
|
||||
WaitFrames = WaitFrames,
|
||||
StartedWaiting = StartedWaiting
|
||||
};
|
||||
}
|
||||
|
||||
@@ -20,6 +21,7 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
if (snapshot is not WaitBattleActionData waitSnapshot) return;
|
||||
|
||||
WaitFrames = waitSnapshot.WaitFrames;
|
||||
StartedWaiting = waitSnapshot.StartedWaiting;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,13 +12,14 @@ 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 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();
|
||||
@@ -27,12 +28,12 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
Vector3 startPos = entity.KinematicCharacterMotor.TransientPosition;
|
||||
Vector3 targetPos = positionDecisionResult.Position;
|
||||
|
||||
// 1. Проверяем дистанцию в плоскости XZ
|
||||
Vector3 toTargetXZ = targetPos - startPos;
|
||||
toTargetXZ.y = 0f;
|
||||
|
||||
Debug.DrawLine(startPos, targetPos, Color.red, 5f);
|
||||
|
||||
float distanceXZ = toTargetXZ.magnitude;
|
||||
|
||||
// ЕСЛИ ВПЛОТНУЮ: отключаем перемещение, оставляем только поворот
|
||||
if (distanceXZ <= StoppingDistance)
|
||||
{
|
||||
data.IsRotateOnly = true;
|
||||
@@ -40,7 +41,6 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
|
||||
float angle = Quaternion.Angle(entity.KinematicCharacterMotor.TransientRotation, positionDecisionResult.Rotation);
|
||||
|
||||
// Если персонаж уже смотрит в нужную сторону — 0 кадров
|
||||
if (angle < 1f)
|
||||
{
|
||||
data.TotalFrames = 0;
|
||||
@@ -54,7 +54,6 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
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);
|
||||
|
||||
@@ -67,7 +66,6 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
Vector3 validStart = startHit.position;
|
||||
Vector3 validTarget = targetHit.position;
|
||||
|
||||
// 3. Прямая видимость: если препятствий нет, идём по идеальной прямой без квантования полигонов NavMesh
|
||||
if (!NavMesh.Raycast(validStart, validTarget, out _, NavMesh.AllAreas))
|
||||
{
|
||||
data.Corners = new[] { startPos, validTarget };
|
||||
@@ -76,13 +74,11 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
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;
|
||||
|
||||
@@ -92,7 +88,6 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
}
|
||||
else
|
||||
{
|
||||
// Если NavMesh не смог построить полный путь (PathPartial/Invalid) — не даём ходить огрызками
|
||||
data.TotalFrames = 0;
|
||||
}
|
||||
|
||||
@@ -114,9 +109,6 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
|
||||
var movementController = entity.MovementController;
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// РЕЖИМ 1: ТОЛЬКО ПОВОРОТ НА МЕСТЕ (КОГДА ПОЗИЦИЯ ВПЛОТНУЮ)
|
||||
// -------------------------------------------------------------
|
||||
if (movementActionData.IsRotateOnly)
|
||||
{
|
||||
movementActionData.CurrentFrame++;
|
||||
@@ -144,19 +136,18 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
{
|
||||
entity.KinematicCharacterMotor.SetRotation(positionDecisionResult.Rotation);
|
||||
battleActionData.Finished = true;
|
||||
Debug.Log("Rotated");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// РЕЖИМ 2: ПЕРЕДВИЖЕНИЕ ПО ПУТИ
|
||||
// -------------------------------------------------------------
|
||||
if (movementActionData.Corners == null || movementActionData.CurrentCornerIndex >= movementActionData.Corners.Length)
|
||||
{
|
||||
entity.KinematicCharacterMotor.SetRotation(positionDecisionResult.Rotation);
|
||||
StopMovementAndFinish(movementController);
|
||||
battleActionData.Finished = true;
|
||||
Debug.Log("Wrong index");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -169,7 +160,6 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
|
||||
Vector3 currentPos = entity.KinematicCharacterMotor.TransientPosition;
|
||||
|
||||
// Пропускаем пройденные углы
|
||||
while (movementActionData.CurrentCornerIndex < movementActionData.Corners.Length)
|
||||
{
|
||||
Vector3 toCorner = movementActionData.Corners[movementActionData.CurrentCornerIndex] - currentPos;
|
||||
@@ -181,7 +171,6 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
break;
|
||||
}
|
||||
|
||||
// Если все точки пройдены до шага симуляции
|
||||
if (movementActionData.CurrentCornerIndex >= movementActionData.Corners.Length)
|
||||
{
|
||||
entity.KinematicCharacterMotor.SetRotation(positionDecisionResult.Rotation);
|
||||
@@ -193,6 +182,7 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
);
|
||||
|
||||
battleActionData.Finished = true;
|
||||
Debug.Log("Last corner");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -232,7 +222,6 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
KinematicCharacterSystem.PhysicsMovers
|
||||
);
|
||||
|
||||
// Проверка достижения конечной точки после физического тика
|
||||
Vector3 postMovePos = movementController.Motor.TransientPosition;
|
||||
Vector3 toFinal = movementActionData.Corners[^1] - postMovePos;
|
||||
toFinal.y = 0f;
|
||||
@@ -241,6 +230,7 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
{
|
||||
entity.KinematicCharacterMotor.SetRotation(positionDecisionResult.Rotation);
|
||||
StopMovementAndFinish(movementController);
|
||||
Debug.Log("Very Last corner");
|
||||
battleActionData.Finished = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +125,12 @@ namespace Code.OfficeGame.BattleMode.Actions.Deciders
|
||||
var position = _cameraStateMachine.Camera.transform.position;
|
||||
var direction = _cameraStateMachine.Camera.transform.forward;
|
||||
|
||||
var hitCount = Physics.RaycastNonAlloc(position, direction, _hitBuffer, Mathf.Infinity, _navMeshMask);
|
||||
var hitCount = Physics.RaycastNonAlloc(
|
||||
position,
|
||||
direction,
|
||||
_hitBuffer,
|
||||
Mathf.Infinity,
|
||||
_navMeshMask);
|
||||
|
||||
if (hitCount == 0
|
||||
|| _entityPosition == Vector3.zero) return;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Code.Game.Gameplay.Player;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Code.OfficeGame.BattleMode.Actions
|
||||
@@ -6,7 +7,9 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
{
|
||||
public Vector3 Position;
|
||||
public Quaternion Rotation;
|
||||
|
||||
public AnimatorSnapshot AnimatorSnapshot;
|
||||
public MovementControllerSnapshot ControllerSnapshot;
|
||||
|
||||
public static EntitySnapshot GetSnapshot(EntityProperties entityProperties)
|
||||
{
|
||||
@@ -14,7 +17,8 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
{
|
||||
Position = entityProperties.KinematicCharacterMotor.TransientPosition,
|
||||
Rotation = entityProperties.KinematicCharacterMotor.TransientRotation,
|
||||
AnimatorSnapshot = AnimatorSnapshot.GetSnapshot(entityProperties.animator)
|
||||
AnimatorSnapshot = AnimatorSnapshot.GetSnapshot(entityProperties.animator),
|
||||
ControllerSnapshot = MovementControllerSnapshot.GetSnapshot(entityProperties.MovementController)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -23,7 +27,9 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
// 1. Возвращаем капсулу KCC
|
||||
entityProperties.KinematicCharacterMotor.SetPositionAndRotation(snapshot.Position, snapshot.Rotation);
|
||||
entityProperties.KinematicCharacterMotor.BaseVelocity = Vector3.zero;
|
||||
|
||||
|
||||
snapshot.ControllerSnapshot.RestoreSnapshot(entityProperties.MovementController);
|
||||
|
||||
// 2. Возвращаем позу и локальное смещение меша
|
||||
snapshot.AnimatorSnapshot.RestoreTo(entityProperties.animator);
|
||||
|
||||
@@ -120,7 +126,6 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
|
||||
targetAnimator.Rebind();
|
||||
|
||||
// Отключаем applyRootMotion на время Update(0f), чтобы Play() не начислил дельту смещения
|
||||
bool prevRootMotion = targetAnimator.applyRootMotion;
|
||||
targetAnimator.applyRootMotion = false;
|
||||
targetAnimator.speed = _speed;
|
||||
@@ -162,7 +167,6 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
|
||||
targetAnimator.Update(0f);
|
||||
|
||||
// ОБЯЗАТЕЛЬНО восстанавливаем сохраненные локальные координаты меша после Update(0f)!
|
||||
Transform targetTransform = targetAnimator.transform;
|
||||
targetTransform.localPosition = _localPosition;
|
||||
targetTransform.localRotation = _localRotation;
|
||||
@@ -170,4 +174,27 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
targetAnimator.applyRootMotion = prevRootMotion;
|
||||
}
|
||||
}
|
||||
|
||||
public struct MovementControllerSnapshot
|
||||
{
|
||||
public Vector3 Velocity;
|
||||
public Vector3 LookInput;
|
||||
public Vector3 MoveInput;
|
||||
|
||||
public static MovementControllerSnapshot GetSnapshot(MovementController movementController)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Velocity = movementController.InternalVelocityAdd,
|
||||
MoveInput = movementController.MoveInputVector,
|
||||
LookInput = movementController.LookInputVector
|
||||
};
|
||||
}
|
||||
public void RestoreSnapshot(MovementController movementController)
|
||||
{
|
||||
movementController.InternalVelocityAdd = Velocity;
|
||||
movementController.MoveInputVector = MoveInput;
|
||||
movementController.LookInputVector = LookInput;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Code.Game.Cleaning.Player;
|
||||
using Code.Game.Gameplay.Player;
|
||||
using Code.Infrastructure.App;
|
||||
using Code.Infrastructure.Camera;
|
||||
using Code.Infrastructure.Generators;
|
||||
@@ -149,6 +150,7 @@ namespace Code.Game.OfficeGame.GameStates
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
entity.animator.enabled = false;
|
||||
entity.MovementController.enabled = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -165,6 +167,7 @@ namespace Code.Game.OfficeGame.GameStates
|
||||
{
|
||||
container.Resolve<EntityAnimator>().Animator.enabled = enabled;
|
||||
container.Resolve<PlayerControlsProvider>().enabled = enabled;
|
||||
container.Resolve<MovementController>().enabled = enabled;
|
||||
}
|
||||
|
||||
private void ToggleBattleMode(InputAction.CallbackContext obj)
|
||||
@@ -194,7 +197,8 @@ namespace Code.Game.OfficeGame.GameStates
|
||||
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
entity.animator.enabled = false;
|
||||
entity.animator.enabled = true;
|
||||
entity.MovementController.enabled = true;
|
||||
}
|
||||
|
||||
Context.ParticleLoader.ReleaseParticles(ParticlePartition.HitParticles);
|
||||
|
||||
@@ -7,7 +7,7 @@ using UnityEngine;
|
||||
|
||||
namespace Code.OfficeGame.BattleMode
|
||||
{
|
||||
public class BattleStateSceneInstaller: MonoBehaviour, IInstaller, IDisposable
|
||||
public class BattleStateSceneInstaller: MonoBehaviour, IInstaller
|
||||
{
|
||||
private IDisposable _onContainerBuildDisposable;
|
||||
|
||||
@@ -27,12 +27,14 @@ namespace Code.OfficeGame.BattleMode
|
||||
|
||||
_onContainerBuildDisposable.Dispose();
|
||||
|
||||
Debug.Log("Containment");
|
||||
|
||||
_sceneContainerManager = container.Resolve<ISceneContainerManager>();
|
||||
_sceneContainerManager.ScenesContainers.Add(SceneId.Battle, container);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
public void OnDestroy(){
|
||||
Debug.Log("Disposement");
|
||||
_sceneContainerManager.ScenesContainers.Remove(SceneId.Battle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,10 @@ namespace Code.OfficeGame.BattleMode.HitBoxes
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
foreach (var part in bodyParts.Values)
|
||||
{
|
||||
part.enabled = false;
|
||||
}
|
||||
// foreach (var part in bodyParts.Values)
|
||||
// {
|
||||
// part.enabled = false;
|
||||
// }
|
||||
_hitBoxLayerMask = LayerMask.GetMask("HitBox");
|
||||
}
|
||||
|
||||
@@ -44,9 +44,9 @@ namespace Code.OfficeGame.BattleMode.HitBoxes
|
||||
{
|
||||
Collider hitCol = _hitResults[i];
|
||||
|
||||
Debug.Log($"Попадание по {hitCol.name}");
|
||||
|
||||
if (hitCol.transform.root == transform.root) continue;
|
||||
|
||||
Debug.Log($"Попадание по {hitCol.name}");
|
||||
|
||||
onNewTargetHit?.Invoke(hitCol);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c4f6108eb6144eb1914a997e0ada6d36
|
||||
timeCreated: 1794237777
|
||||
@@ -0,0 +1,71 @@
|
||||
using System.Collections.Generic;
|
||||
using Code.Game.Entities;
|
||||
using Code.OfficeGame.BattleMode.Actions;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Code.OfficeGame.BattleMode.Snapshots
|
||||
{
|
||||
public class SnapshotUtilityNonStatic
|
||||
{
|
||||
Dictionary<EntityId, BattleActionData> actionDataSnapshots = new();
|
||||
Dictionary<EntityId, BattleActionDecisionResult> resultsSnapshots = new();
|
||||
Dictionary<EntityId, EntitySnapshot> entitySnapshots = new();
|
||||
|
||||
public void GetSnapshots(
|
||||
Dictionary<EntityId, EntityProperties> entities,
|
||||
Dictionary<EntityId, BattleActionDecisionResult> actionDecisionResults,
|
||||
Dictionary<EntityId, BattleActionData> battleActionDatas)
|
||||
{
|
||||
foreach (var (entityId, entity) in entities)
|
||||
{
|
||||
if (!actionDecisionResults.TryGetValue(entityId, out var actionDecisionResult)
|
||||
|| !battleActionDatas.TryGetValue(entityId, out var battleActionData))
|
||||
{
|
||||
entity.EntityPreviewVisuals.ShowPreviewState(spawnStaticSnapshotGhost: false);
|
||||
continue;
|
||||
}
|
||||
entity.EntityPreviewVisuals.ShowPreviewState(spawnStaticSnapshotGhost: true);
|
||||
|
||||
resultsSnapshots[entityId] = actionDecisionResult.GetSnapshot();
|
||||
actionDataSnapshots[entityId] = battleActionData.GetSnapshot();
|
||||
entitySnapshots[entityId] = entity.GetSnapshot();
|
||||
}
|
||||
}
|
||||
|
||||
public void RestoreFromSnapshots(
|
||||
Dictionary<EntityId, EntityProperties> entities,
|
||||
Dictionary<EntityId, BattleActionDecisionResult> actionDecisionResults,
|
||||
Dictionary<EntityId, BattleActionData> battleActionDatas)
|
||||
{
|
||||
foreach (var (entityId, entity) in entities)
|
||||
{
|
||||
if (!resultsSnapshots.TryGetValue(entityId, out var actionDecisionResult)
|
||||
|| !actionDataSnapshots.TryGetValue(entityId, out var battleActionData))
|
||||
continue;
|
||||
|
||||
bool actionChanged = false;
|
||||
|
||||
if (!actionDecisionResults.TryAdd(entityId, actionDecisionResult))
|
||||
{
|
||||
actionChanged = actionDecisionResult.ActionType
|
||||
!= actionDecisionResults[entityId].ActionType;
|
||||
|
||||
if (actionChanged)
|
||||
actionDecisionResults[entityId] = actionDecisionResult.GetSnapshot();
|
||||
else
|
||||
actionDecisionResults[entityId]?.RestoreSnapshot(actionDecisionResult);
|
||||
}
|
||||
|
||||
if (!battleActionDatas.TryAdd(entityId, battleActionData))
|
||||
{
|
||||
if (actionChanged)
|
||||
battleActionDatas[entityId] = battleActionData.GetSnapshot();
|
||||
else
|
||||
battleActionDatas[entityId]?.RestoreSnapshot(battleActionData);
|
||||
}
|
||||
|
||||
entity.RestoreSnapshot(entitySnapshots[entityId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2bf8fe2677b947a781568c78e8dc7e99
|
||||
timeCreated: 1794237839
|
||||
@@ -7,6 +7,7 @@ using Code.Game.OfficeGame.BattleMode;
|
||||
using Code.Infrastructure.Camera;
|
||||
using Code.OfficeGame.BattleMode.Actions;
|
||||
using Code.OfficeGame.BattleMode.Actions.Deciders;
|
||||
using Code.OfficeGame.BattleMode.Snapshots;
|
||||
using Code.OfficeGame.BattleMode.UI;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using R3;
|
||||
@@ -32,9 +33,13 @@ namespace Code.OfficeGame.BattleMode.Timeline
|
||||
private Dictionary<EntityId, BattleActionData> _battleActionDatas = new();
|
||||
private Dictionary<EntityId, BattleActionDecisionResult> _actionDecisionResults = new();
|
||||
|
||||
private SnapshotUtilityNonStatic _snapshotUtility = new();
|
||||
|
||||
private BattleActionDecisionPanel _battleActionDecisionPanel;
|
||||
private CameraStateMachine _cameraStateMachine;
|
||||
|
||||
private SemaphoreSlim _preview = new(1,1);
|
||||
|
||||
public BattleTimeline(
|
||||
BattleTimelinePanel timelinePanel,
|
||||
IEnumerable<BattleAction> battleActions,
|
||||
@@ -113,97 +118,63 @@ namespace Code.OfficeGame.BattleMode.Timeline
|
||||
private async UniTask PreviewBattleLoopAsync(int frameCount,
|
||||
Dictionary<EntityId, EntityProperties> entities,
|
||||
CancellationToken ctx)
|
||||
{
|
||||
Dictionary<EntityId, BattleActionData> actionDataSnapshots = new();
|
||||
Dictionary<EntityId, BattleActionDecisionResult> resultsSnapshots = new();
|
||||
Dictionary<EntityId, EntitySnapshot> entitySnapshots = new();
|
||||
|
||||
foreach (var (entityId, entity) in entities)
|
||||
{
|
||||
if (frameCount <= 0)
|
||||
{
|
||||
|
||||
if (!_actionDecisionResults.TryGetValue(entityId, out var actionDecisionResult)
|
||||
|| !_battleActionDatas.TryGetValue(entityId, out var battleActionData))
|
||||
{
|
||||
entity.EntityPreviewVisuals.ShowPreviewState(spawnStaticSnapshotGhost: false);
|
||||
continue;
|
||||
}
|
||||
entity.EntityPreviewVisuals.ShowPreviewState(spawnStaticSnapshotGhost: true);
|
||||
|
||||
resultsSnapshots[entityId] = actionDecisionResult.GetSnapshot();
|
||||
actionDataSnapshots[entityId] = battleActionData.GetSnapshot();
|
||||
entitySnapshots[entityId] = entity.GetSnapshot();
|
||||
_preview.Release(1);
|
||||
throw new Exception("FrameCount must be > 0");
|
||||
}
|
||||
|
||||
_snapshotUtility.GetSnapshots(
|
||||
entities,
|
||||
_actionDecisionResults,
|
||||
_battleActionDatas);
|
||||
|
||||
try
|
||||
{
|
||||
while (!ctx.IsCancellationRequested)
|
||||
{
|
||||
RestoreFromSnapshots(entities, resultsSnapshots, actionDataSnapshots, entitySnapshots);
|
||||
|
||||
_snapshotUtility.RestoreFromSnapshots(
|
||||
entities,
|
||||
_actionDecisionResults,
|
||||
_battleActionDatas);
|
||||
|
||||
await PlayTimelineForCertainFrames(frameCount, ctx).AttachExternalCancellation(ctx);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
RestoreFromSnapshots(entities, resultsSnapshots, actionDataSnapshots, entitySnapshots);
|
||||
|
||||
_snapshotUtility.RestoreFromSnapshots(
|
||||
entities,
|
||||
_actionDecisionResults,
|
||||
_battleActionDatas);
|
||||
|
||||
foreach (var (_, entity) in entities)
|
||||
{
|
||||
entity.EntityPreviewVisuals.HidePreviewState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RestoreFromSnapshots(Dictionary<EntityId, EntityProperties> entities,
|
||||
Dictionary<EntityId, BattleActionDecisionResult> resultsSnapshots,
|
||||
Dictionary<EntityId, BattleActionData> actionDataSnapshots,
|
||||
Dictionary<EntityId, EntitySnapshot> entitySnapshots)
|
||||
{
|
||||
foreach (var (entityId, entity) in entities)
|
||||
{
|
||||
if (!resultsSnapshots.TryGetValue(entityId, out var actionDecisionResult)
|
||||
|| !actionDataSnapshots.TryGetValue(entityId, out var battleActionData))
|
||||
continue;
|
||||
|
||||
bool actionChanged = false;
|
||||
|
||||
if (!_actionDecisionResults.TryAdd(entityId, actionDecisionResult))
|
||||
{
|
||||
actionChanged = actionDecisionResult.ActionType
|
||||
!= _actionDecisionResults[entityId].ActionType;
|
||||
|
||||
if (actionChanged)
|
||||
_actionDecisionResults[entityId] = actionDecisionResult.GetSnapshot();
|
||||
else
|
||||
_actionDecisionResults[entityId]?.RestoreSnapshot(actionDecisionResult);
|
||||
}
|
||||
|
||||
if (!_battleActionDatas.TryAdd(entityId, battleActionData))
|
||||
{
|
||||
if (actionChanged)
|
||||
_battleActionDatas[entityId] = battleActionData.GetSnapshot();
|
||||
else _battleActionDatas[entityId]?.RestoreSnapshot(battleActionData);
|
||||
}
|
||||
|
||||
entity.RestoreSnapshot(entitySnapshots[entityId]);
|
||||
_preview.Release(1);
|
||||
}
|
||||
}
|
||||
|
||||
private async UniTask PlayTimelineForCertainFrames(int frameCount, CancellationToken ctx)
|
||||
{
|
||||
float accumulator = 0f;
|
||||
int counter = frameCount;
|
||||
|
||||
while (frameCount > 0 && !ctx.IsCancellationRequested)
|
||||
while (counter > 0 && !ctx.IsCancellationRequested)
|
||||
{
|
||||
bool isCanceled = await UniTask.Yield(PlayerLoopTiming.Update, ctx).SuppressCancellationThrow();
|
||||
if (isCanceled) break;
|
||||
|
||||
accumulator += Mathf.Min(Time.unscaledDeltaTime, 0.1f);
|
||||
|
||||
while (accumulator >= TickDelta && frameCount > 0 && !ctx.IsCancellationRequested)
|
||||
while (accumulator >= TickDelta && counter > 0 && !ctx.IsCancellationRequested)
|
||||
{
|
||||
// Debug.Log($"Simulating {counter}/{frameCount}");
|
||||
accumulator -= TickDelta;
|
||||
frameCount--;
|
||||
counter--;
|
||||
|
||||
var readyEntities = SimulateSingleFrame(_entities);
|
||||
|
||||
@@ -211,21 +182,22 @@ namespace Code.OfficeGame.BattleMode.Timeline
|
||||
{
|
||||
foreach (var entityId in readyEntities)
|
||||
{
|
||||
// _battleActionDatas.Remove(entityID);
|
||||
// _actionDecisionResults.Remove(entityID);
|
||||
Debug.Log($"Entity {entityId} is ready");
|
||||
_battleActionDatas.Remove(entityId);
|
||||
_actionDecisionResults.Remove(entityId);
|
||||
|
||||
_battleActionDatas[entityId] = new WaitBattleActionData {
|
||||
TotalFrames = frameCount,
|
||||
WaitFrames = frameCount
|
||||
};
|
||||
_actionDecisionResults[entityId] = new FrameBattleActionDeciderResult
|
||||
{
|
||||
ActionType = BattleActionType.Wait,
|
||||
FrameCount = frameCount,
|
||||
ResultCode = BattleActionDecisionResultCode.Decided
|
||||
};
|
||||
// _battleActionDatas[entityId] = new WaitBattleActionData {
|
||||
// TotalFrames = frameCount,
|
||||
// WaitFrames = frameCount
|
||||
// };
|
||||
// _actionDecisionResults[entityId] = new FrameBattleActionDeciderResult
|
||||
// {
|
||||
// ActionType = BattleActionType.Wait,
|
||||
// FrameCount = frameCount,
|
||||
// ResultCode = BattleActionDecisionResultCode.Decided
|
||||
// };
|
||||
}
|
||||
// return;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -233,10 +205,8 @@ namespace Code.OfficeGame.BattleMode.Timeline
|
||||
|
||||
private EntityId[] SimulateSingleFrame(Dictionary<EntityId, EntityProperties> inEntities)
|
||||
{
|
||||
foreach (var entity in inEntities)
|
||||
foreach (var (entityId, entity) in inEntities)
|
||||
{
|
||||
var entityId = entity.Key;
|
||||
|
||||
if (!_actionDecisionResults.TryGetValue(entityId, out var actionDecisionResult)
|
||||
|| !_battleActionDatas.TryGetValue(entityId, out var battleActionData))
|
||||
continue;
|
||||
@@ -244,11 +214,11 @@ namespace Code.OfficeGame.BattleMode.Timeline
|
||||
_battleActions[actionDecisionResult.ActionType]
|
||||
.Tick(
|
||||
TickDelta,
|
||||
entity.Value,
|
||||
entity,
|
||||
actionDecisionResult,
|
||||
battleActionData);
|
||||
|
||||
entity.Value.animator.Update(TickDelta);
|
||||
entity.animator.Update(TickDelta);
|
||||
}
|
||||
|
||||
Physics.Simulate(TickDelta);
|
||||
@@ -327,11 +297,15 @@ namespace Code.OfficeGame.BattleMode.Timeline
|
||||
decisionResult = await _battleActionDeciders[deciderType].GetDecision();
|
||||
decisionResult.ActionType = decidedActionType;
|
||||
|
||||
actionData = _battleActions[decidedActionType].MountAndGetFrames(entity, decisionResult);
|
||||
|
||||
previewCancellationToken.Cancel();
|
||||
previewCancellationToken.Dispose();
|
||||
|
||||
await _preview.WaitAsync(ctx);
|
||||
|
||||
_preview.Release();
|
||||
|
||||
actionData = _battleActions[decidedActionType].MountAndGetFrames(entity, decisionResult);
|
||||
|
||||
if (decisionResult.ResultCode == BattleActionDecisionResultCode.Deciding)
|
||||
{
|
||||
_actionDecisionResults[entityId] = decisionResult;
|
||||
@@ -339,6 +313,8 @@ namespace Code.OfficeGame.BattleMode.Timeline
|
||||
|
||||
previewCancellationToken = new();
|
||||
|
||||
await _preview.WaitAsync(previewCancellationToken.Token);
|
||||
|
||||
PreviewBattleLoopAsync(actionData.TotalFrames, _entities, previewCancellationToken.Token)
|
||||
.Forget();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using Alchemy.Inspector;
|
||||
using Code.OfficeGame.BattleMode.Actions;
|
||||
using KinematicCharacterController;
|
||||
using LitMotion;
|
||||
using LitMotion.Extensions;
|
||||
@@ -56,12 +57,11 @@ namespace Code.Game.Gameplay.Player
|
||||
|
||||
[TabGroup("Misc")] public List<Collider> IgnoredColliders = new List<Collider>();
|
||||
|
||||
private Vector3 _moveInputVector;
|
||||
private Vector3 _lookInputVector;
|
||||
|
||||
private Vector3 _internalVelocityAdd = Vector3.zero;
|
||||
|
||||
public Vector3 MoveInputVector { get; set; }
|
||||
public Vector3 LookInputVector {get; set;}
|
||||
|
||||
public Vector3 InternalVelocityAdd { get; set; } = Vector3.zero;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// Assign the characterController to the motor
|
||||
@@ -90,15 +90,15 @@ namespace Code.Game.Gameplay.Player
|
||||
Quaternion cameraPlanarRotation = Quaternion.LookRotation(cameraPlanarDirection, Motor.CharacterUp);
|
||||
|
||||
// Move and look inputs
|
||||
_moveInputVector = cameraPlanarRotation * moveInputVector;
|
||||
MoveInputVector = cameraPlanarRotation * moveInputVector;
|
||||
|
||||
switch (OrientationMethod)
|
||||
{
|
||||
case OrientationMethod.TowardsCamera:
|
||||
_lookInputVector = cameraPlanarDirection;
|
||||
LookInputVector = cameraPlanarDirection;
|
||||
break;
|
||||
case OrientationMethod.TowardsMovement:
|
||||
_lookInputVector = _moveInputVector.normalized;
|
||||
LookInputVector = MoveInputVector.normalized;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -117,8 +117,8 @@ namespace Code.Game.Gameplay.Player
|
||||
.normalized;
|
||||
}
|
||||
|
||||
_moveInputVector = inputs.MoveVector;
|
||||
_lookInputVector = cameraPlanarDirection;
|
||||
MoveInputVector = inputs.MoveVector;
|
||||
LookInputVector = cameraPlanarDirection;
|
||||
}
|
||||
|
||||
private Quaternion _tmpTransientRot;
|
||||
@@ -139,11 +139,11 @@ namespace Code.Game.Gameplay.Player
|
||||
/// </summary>
|
||||
public void UpdateRotation(ref Quaternion currentRotation, float deltaTime)
|
||||
{
|
||||
if (_lookInputVector.sqrMagnitude > 0f && OrientationSharpness > 0f)
|
||||
if (LookInputVector.sqrMagnitude > 0f && OrientationSharpness > 0f)
|
||||
{
|
||||
// Smoothly interpolate from current to target look direction
|
||||
|
||||
Vector3 smoothedLookInputDirection = Vector3.Slerp(Motor.CharacterForward, _lookInputVector,
|
||||
Vector3 smoothedLookInputDirection = Vector3.Slerp(Motor.CharacterForward, LookInputVector,
|
||||
1 - Mathf.Exp(-OrientationSharpness * deltaTime)).normalized;
|
||||
|
||||
// Set the current rotation (which will be used by the KinematicCharacterMotor)
|
||||
@@ -215,10 +215,10 @@ namespace Code.Game.Gameplay.Player
|
||||
currentVelocityMagnitude;
|
||||
|
||||
// Calculate target velocity
|
||||
Vector3 inputRight = Vector3.Cross(_moveInputVector, Motor.CharacterUp);
|
||||
Vector3 inputRight = Vector3.Cross(MoveInputVector, Motor.CharacterUp);
|
||||
|
||||
Vector3 reorientedInput = Vector3.Cross(effectiveGroundNormal, inputRight).normalized *
|
||||
_moveInputVector.magnitude;
|
||||
MoveInputVector.magnitude;
|
||||
|
||||
Vector3 targetMovementVelocity;
|
||||
|
||||
@@ -232,9 +232,9 @@ namespace Code.Game.Gameplay.Player
|
||||
else
|
||||
{
|
||||
// Add move input
|
||||
if (_moveInputVector.sqrMagnitude > 0f)
|
||||
if (MoveInputVector.sqrMagnitude > 0f)
|
||||
{
|
||||
Vector3 addedVelocity = _moveInputVector * (AirAccelerationSpeed * deltaTime);
|
||||
Vector3 addedVelocity = MoveInputVector * (AirAccelerationSpeed * deltaTime);
|
||||
|
||||
Vector3 currentVelocityOnInputsPlane =
|
||||
Vector3.ProjectOnPlane(currentVelocity, Motor.CharacterUp);
|
||||
@@ -282,10 +282,10 @@ namespace Code.Game.Gameplay.Player
|
||||
}
|
||||
|
||||
// Take into account additive velocity
|
||||
if (_internalVelocityAdd.sqrMagnitude > 0f)
|
||||
if (InternalVelocityAdd.sqrMagnitude > 0f)
|
||||
{
|
||||
currentVelocity += _internalVelocityAdd;
|
||||
_internalVelocityAdd = Vector3.zero;
|
||||
currentVelocity += InternalVelocityAdd;
|
||||
InternalVelocityAdd = Vector3.zero;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,7 +337,7 @@ namespace Code.Game.Gameplay.Player
|
||||
|
||||
public void AddVelocity(Vector3 velocity)
|
||||
{
|
||||
_internalVelocityAdd += velocity;
|
||||
InternalVelocityAdd += velocity;
|
||||
}
|
||||
|
||||
public void ProcessHitStabilityReport(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint,
|
||||
|
||||
Reference in new issue
Block a user