Snapshots. Better actions
This commit is contained in:
1 parent
71bb4c3b5b
commit
31850e77bc
73 files changed
+31948
-80048
No files matched your search
@@ -0,0 +1,17 @@
|
||||
using Code.OfficeGame.BattleMode.Actions;
|
||||
|
||||
namespace Code.Game.Entities
|
||||
{
|
||||
public static class EntitySnapshotExtensions
|
||||
{
|
||||
public static EntitySnapshot GetSnapshot(this EntityProperties entityProperties)
|
||||
{
|
||||
return EntitySnapshot.GetSnapshot(entityProperties);
|
||||
}
|
||||
|
||||
public static void RestoreSnapshot(this EntityProperties entityProperties, EntitySnapshot snapshot)
|
||||
{
|
||||
EntitySnapshot.RestoreSnapshot(entityProperties, snapshot);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5ee879ec01aa444299cfdf84914718d3
|
||||
timeCreated: 1793383803
|
||||
@@ -0,0 +1,18 @@
|
||||
using Code.OfficeGame.BattleMode.Actions;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Code.Infrastructure.Utils
|
||||
{
|
||||
public static class AnimatorSnapshotExtensions
|
||||
{
|
||||
public static AnimatorSnapshot GetSnapshot(this Animator animator)
|
||||
{
|
||||
return AnimatorSnapshot.GetSnapshot(animator);
|
||||
}
|
||||
|
||||
public static void RestoreSnapshot(this Animator animator, AnimatorSnapshot snapshot)
|
||||
{
|
||||
snapshot.RestoreTo(animator);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3cd2b2d431ab42b6b65c04b4516e6281
|
||||
timeCreated: 1793382687
|
||||
BIN
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 924f8182d7fa4e48a0ce1447e05f794f
|
||||
timeCreated: 1793372499
|
||||
+3
@@ -3,5 +3,8 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
public abstract class BattleActionData
|
||||
{
|
||||
public int TotalFrames;
|
||||
|
||||
public abstract BattleActionData GetSnapshot();
|
||||
public abstract void RestoreSnapshot(BattleActionData snapshot);
|
||||
}
|
||||
}
|
||||
File renamed without changes.
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI;
|
||||
|
||||
namespace Code.OfficeGame.BattleMode.Actions
|
||||
{
|
||||
public class MovementBattleActionData : BattleActionData
|
||||
{
|
||||
public Vector3 Position;
|
||||
public Quaternion Rotation;
|
||||
public NavMeshPath Path;
|
||||
public Vector3[] Corners;
|
||||
public int CurrentCornerIndex;
|
||||
public override BattleActionData GetSnapshot()
|
||||
{
|
||||
Vector3[] cornersCopy = null;
|
||||
if (Corners != null)
|
||||
{
|
||||
cornersCopy = new Vector3[Corners.Length];
|
||||
Array.Copy(Corners, cornersCopy, Corners.Length);
|
||||
}
|
||||
|
||||
return new MovementBattleActionData
|
||||
{
|
||||
TotalFrames = TotalFrames,
|
||||
Position = Position,
|
||||
Rotation = Rotation,
|
||||
Path = Path,
|
||||
Corners = cornersCopy,
|
||||
CurrentCornerIndex = CurrentCornerIndex
|
||||
};
|
||||
}
|
||||
|
||||
public override void RestoreSnapshot(BattleActionData snapshot)
|
||||
{
|
||||
TotalFrames = snapshot.TotalFrames;
|
||||
|
||||
if (snapshot is not MovementBattleActionData moveSnapshot) return;
|
||||
|
||||
Position = moveSnapshot.Position;
|
||||
Rotation = moveSnapshot.Rotation;
|
||||
Path = moveSnapshot.Path;
|
||||
CurrentCornerIndex = moveSnapshot.CurrentCornerIndex;
|
||||
|
||||
if (moveSnapshot.Corners != null)
|
||||
{
|
||||
if (Corners == null || Corners.Length != moveSnapshot.Corners.Length)
|
||||
Corners = new Vector3[moveSnapshot.Corners.Length];
|
||||
|
||||
Array.Copy(moveSnapshot.Corners, Corners, moveSnapshot.Corners.Length);
|
||||
}
|
||||
else Corners = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
File renamed without changes.
@@ -0,0 +1,15 @@
|
||||
namespace Code.OfficeGame.BattleMode.Actions
|
||||
{
|
||||
public class NoneBattleActionData : BattleActionData
|
||||
{
|
||||
public override BattleActionData GetSnapshot()
|
||||
{
|
||||
return new NoneBattleActionData();
|
||||
}
|
||||
|
||||
public override void RestoreSnapshot(BattleActionData snapshot)
|
||||
{
|
||||
TotalFrames = snapshot.TotalFrames;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 73b4bda13326404db2b0ad653ad43dce
|
||||
timeCreated: 1793372539
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace Code.OfficeGame.BattleMode.Actions
|
||||
{
|
||||
public class WaitBattleActionData : BattleActionData
|
||||
{
|
||||
public int WaitFrames;
|
||||
public override BattleActionData GetSnapshot()
|
||||
{
|
||||
return new WaitBattleActionData
|
||||
{
|
||||
TotalFrames = TotalFrames,
|
||||
WaitFrames = WaitFrames
|
||||
};
|
||||
}
|
||||
|
||||
public override void RestoreSnapshot(BattleActionData snapshot)
|
||||
{
|
||||
TotalFrames = snapshot.TotalFrames;
|
||||
|
||||
if (snapshot is not WaitBattleActionData waitSnapshot) return;
|
||||
|
||||
WaitFrames = waitSnapshot.WaitFrames;
|
||||
}
|
||||
}
|
||||
}
|
||||
File renamed without changes.
@@ -0,0 +1,20 @@
|
||||
using Code.OfficeGame.BattleMode.Actions.Deciders;
|
||||
|
||||
namespace Code.OfficeGame.BattleMode.Actions
|
||||
{
|
||||
public class BattleActionNone : BattleAction
|
||||
{
|
||||
public override bool Tick(float tickDelta, EntityProperties entity, BattleActionDecisionResult battleActionDecisionResult,
|
||||
BattleActionData battleActionData)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override BattleActionType ActionType => BattleActionType.None;
|
||||
public override DeciderType DeciderType => DeciderType.AsIs;
|
||||
public override BattleActionData MountAndGetFrames(EntityProperties entity, BattleActionDecisionResult result)
|
||||
{
|
||||
return new NoneBattleActionData();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2371c1e8c84b444e9cec026acea67469
|
||||
timeCreated: 1793377460
|
||||
@@ -6,17 +6,84 @@ namespace Code.OfficeGame.BattleMode.Actions
|
||||
{
|
||||
public BattleActionType ActionType;
|
||||
public BattleActionDecisionResultCode ResultCode;
|
||||
|
||||
public abstract BattleActionDecisionResult GetSnapshot();
|
||||
public abstract void RestoreSnapshot(BattleActionDecisionResult snapshot);
|
||||
}
|
||||
|
||||
public class PositionBattleActionDeciderResult : BattleActionDecisionResult
|
||||
{
|
||||
public Vector3 Position;
|
||||
public Quaternion Rotation;
|
||||
|
||||
public override BattleActionDecisionResult GetSnapshot()
|
||||
{
|
||||
var snapshot = new PositionBattleActionDeciderResult
|
||||
{
|
||||
Position = Position,
|
||||
Rotation = Rotation,
|
||||
ActionType = ActionType,
|
||||
ResultCode = ResultCode
|
||||
};
|
||||
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
public override void RestoreSnapshot(BattleActionDecisionResult snapshot)
|
||||
{
|
||||
if (snapshot is not PositionBattleActionDeciderResult) return;
|
||||
|
||||
var deciderResult = (PositionBattleActionDeciderResult)snapshot;
|
||||
|
||||
Position = deciderResult.Position;
|
||||
Rotation = deciderResult.Rotation;
|
||||
ActionType = deciderResult.ActionType;
|
||||
ResultCode = deciderResult.ResultCode;
|
||||
}
|
||||
}
|
||||
|
||||
public class FrameBattleActionDeciderResult : BattleActionDecisionResult
|
||||
{
|
||||
public int FrameCount;
|
||||
|
||||
public override BattleActionDecisionResult GetSnapshot()
|
||||
{
|
||||
var snapshot = new FrameBattleActionDeciderResult
|
||||
{
|
||||
FrameCount = FrameCount,
|
||||
ActionType = ActionType,
|
||||
ResultCode = ResultCode
|
||||
};
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
public override void RestoreSnapshot(BattleActionDecisionResult snapshot)
|
||||
{
|
||||
if (snapshot is not FrameBattleActionDeciderResult) return;
|
||||
|
||||
var deciderResult = (FrameBattleActionDeciderResult)snapshot;
|
||||
|
||||
FrameCount = deciderResult.FrameCount;
|
||||
ActionType = deciderResult.ActionType;
|
||||
ResultCode = deciderResult.ResultCode;
|
||||
}
|
||||
}
|
||||
|
||||
public class AsIsBattleActionDeciderResult : BattleActionDecisionResult
|
||||
{
|
||||
public override BattleActionDecisionResult GetSnapshot()
|
||||
{
|
||||
var snapshot = new AsIsBattleActionDeciderResult();
|
||||
|
||||
snapshot.ActionType = ActionType;
|
||||
snapshot.ResultCode = ResultCode;
|
||||
|
||||
return snapshot;
|
||||
}
|
||||
public override void RestoreSnapshot(BattleActionDecisionResult snapshot)
|
||||
{
|
||||
ActionType = snapshot.ActionType;
|
||||
ResultCode = snapshot.ResultCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@ namespace Code.OfficeGame.BattleMode.Actions.Deciders
|
||||
if (!IsMounted) throw new Exception("Decider is not mounted"); // А почему бы и нет?
|
||||
|
||||
_waitForEnterTokenSource = new CancellationTokenSource();
|
||||
_decisionResultCode = BattleActionDecisionResultCode.CancelDecision;
|
||||
_decisionResultCode = BattleActionDecisionResultCode.Deciding;
|
||||
|
||||
await UniTask.WaitUntilCanceled(_waitForEnterTokenSource.Token).SuppressCancellationThrow();
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace Code.OfficeGame.BattleMode.Actions.Deciders
|
||||
return new PositionBattleActionDeciderResult()
|
||||
{
|
||||
ResultCode = _decisionResultCode,
|
||||
Position = _targetPosition,
|
||||
Position = _targetPosition
|
||||
};
|
||||
}
|
||||
|
||||
@@ -137,6 +137,8 @@ namespace Code.OfficeGame.BattleMode.Actions.Deciders
|
||||
|
||||
_playerLockedDecisionMarker.MoveMarker(_targetPosition);
|
||||
_playerLockedDecisionMarker.Toggle(true);
|
||||
|
||||
_waitForEnterTokenSource?.Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
using System;
|
||||
using Code.Infrastructure.Utils;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Code.OfficeGame.BattleMode.Actions
|
||||
{
|
||||
public struct EntitySnapshot
|
||||
{
|
||||
// Transforms
|
||||
public Vector3 Position;
|
||||
public Quaternion Rotation;
|
||||
|
||||
// Animator
|
||||
AnimatorSnapshot AnimatorSnapshot;
|
||||
|
||||
public static EntitySnapshot GetSnapshot(EntityProperties entityProperties)
|
||||
{
|
||||
EntitySnapshot snapshot = new EntitySnapshot()
|
||||
{
|
||||
Position = entityProperties.KinematicCharacterMotor.TransientPosition,
|
||||
Rotation = entityProperties.KinematicCharacterMotor.TransientRotation,
|
||||
AnimatorSnapshot = entityProperties.animator.GetSnapshot()
|
||||
};
|
||||
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
public static void RestoreSnapshot(EntityProperties entityProperties, EntitySnapshot snapshot)
|
||||
{
|
||||
entityProperties.animator.RestoreSnapshot(snapshot.AnimatorSnapshot);
|
||||
|
||||
entityProperties.KinematicCharacterMotor.SetPositionAndRotation(snapshot.Position, snapshot.Rotation);
|
||||
}
|
||||
}
|
||||
|
||||
public struct AnimatorSnapshot
|
||||
{
|
||||
public struct LayerStateSnapshot
|
||||
{
|
||||
public int FullPathHash;
|
||||
public float NormalizedTime;
|
||||
public float LayerWeight;
|
||||
}
|
||||
|
||||
public struct ParameterSnapshot
|
||||
{
|
||||
public int NameHash;
|
||||
public AnimatorControllerParameterType Type;
|
||||
public float FloatValue;
|
||||
public int IntValue;
|
||||
public bool BoolValue;
|
||||
}
|
||||
|
||||
private LayerStateSnapshot[] _layers;
|
||||
private ParameterSnapshot[] _parameters;
|
||||
|
||||
private Vector3 _localPosition;
|
||||
private Quaternion _localRotation;
|
||||
private float _speed;
|
||||
|
||||
/// <summary>
|
||||
/// Создаёт новый экземпляр снапшота с текущим состоянием аниматора
|
||||
/// </summary>
|
||||
public static AnimatorSnapshot GetSnapshot(Animator animator)
|
||||
{
|
||||
var snapshot = new AnimatorSnapshot();
|
||||
snapshot.CaptureFrom(animator);
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Записывает текущее состояние аниматора в этот снапшот (без лишних аллокаций при повторном вызове)
|
||||
/// </summary>
|
||||
public void CaptureFrom(Animator animator)
|
||||
{
|
||||
if (animator == null) return;
|
||||
|
||||
// 1. Сохраняем локальный трансформ модели (защита от смещения Root Motion)
|
||||
Transform animTransform = animator.transform;
|
||||
_localPosition = animTransform.localPosition;
|
||||
_localRotation = animTransform.localRotation;
|
||||
_speed = animator.speed;
|
||||
|
||||
// 2. Сохраняем состояния и кадры всех слоёв
|
||||
int layerCount = animator.layerCount;
|
||||
if (_layers == null || _layers.Length != layerCount)
|
||||
{
|
||||
_layers = new LayerStateSnapshot[layerCount];
|
||||
}
|
||||
|
||||
for (int i = 0; i < layerCount; i++)
|
||||
{
|
||||
// Если аниматор находился в процессе перехода (Transition),
|
||||
// можно при необходимости брать GetNextAnimatorStateInfo(i), но для покадрового Play обычно используется текущий:
|
||||
AnimatorStateInfo stateInfo = animator.GetCurrentAnimatorStateInfo(i);
|
||||
|
||||
_layers[i] = new LayerStateSnapshot
|
||||
{
|
||||
FullPathHash = stateInfo.fullPathHash,
|
||||
NormalizedTime = stateInfo.normalizedTime,
|
||||
LayerWeight = animator.GetLayerWeight(i)
|
||||
};
|
||||
}
|
||||
|
||||
// 3. Сохраняем значения всех параметров
|
||||
int paramCount = animator.parameterCount;
|
||||
if (_parameters == null || _parameters.Length != paramCount)
|
||||
{
|
||||
_parameters = new ParameterSnapshot[paramCount];
|
||||
}
|
||||
|
||||
for (int i = 0; i < paramCount; i++)
|
||||
{
|
||||
AnimatorControllerParameter param = animator.GetParameter(i);
|
||||
int hash = param.nameHash;
|
||||
|
||||
var paramSnapshot = new ParameterSnapshot
|
||||
{
|
||||
NameHash = hash,
|
||||
Type = param.type
|
||||
};
|
||||
|
||||
switch (param.type)
|
||||
{
|
||||
case AnimatorControllerParameterType.Float:
|
||||
paramSnapshot.FloatValue = animator.GetFloat(hash);
|
||||
break;
|
||||
case AnimatorControllerParameterType.Int:
|
||||
paramSnapshot.IntValue = animator.GetInteger(hash);
|
||||
break;
|
||||
case AnimatorControllerParameterType.Bool:
|
||||
case AnimatorControllerParameterType.Trigger:
|
||||
paramSnapshot.BoolValue = animator.GetBool(hash);
|
||||
break;
|
||||
}
|
||||
|
||||
_parameters[i] = paramSnapshot;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Восстанавливает состояние в целевой аниматор (оригинал или клон-призрак)
|
||||
/// </summary>
|
||||
public void RestoreTo(Animator targetAnimator)
|
||||
{
|
||||
if (targetAnimator == null || _layers == null) return;
|
||||
|
||||
// 1. Временно выключаем Root Motion, чтобы Update(0f) не сдвинул модель повторно
|
||||
bool prevRootMotion = targetAnimator.applyRootMotion;
|
||||
targetAnimator.applyRootMotion = false;
|
||||
targetAnimator.speed = _speed;
|
||||
|
||||
// 2. Восстанавливаем параметры
|
||||
if (_parameters != null)
|
||||
{
|
||||
for (int i = 0; i < _parameters.Length; i++)
|
||||
{
|
||||
var p = _parameters[i];
|
||||
|
||||
// Пропускаем параметры, которые управляются кривыми внутри самой анимации
|
||||
if (targetAnimator.IsParameterControlledByCurve(p.NameHash))
|
||||
continue;
|
||||
|
||||
switch (p.Type)
|
||||
{
|
||||
case AnimatorControllerParameterType.Float:
|
||||
targetAnimator.SetFloat(p.NameHash, p.FloatValue);
|
||||
break;
|
||||
case AnimatorControllerParameterType.Int:
|
||||
targetAnimator.SetInteger(p.NameHash, p.IntValue);
|
||||
break;
|
||||
case AnimatorControllerParameterType.Bool:
|
||||
targetAnimator.SetBool(p.NameHash, p.BoolValue);
|
||||
break;
|
||||
case AnimatorControllerParameterType.Trigger:
|
||||
if (p.BoolValue) targetAnimator.SetTrigger(p.NameHash);
|
||||
else targetAnimator.ResetTrigger(p.NameHash);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Восстанавливаем стейты и точный кадр (NormalizedTime) на каждом слое
|
||||
int layerCount = Mathf.Min(_layers.Length, targetAnimator.layerCount);
|
||||
for (int i = 0; i < layerCount; i++)
|
||||
{
|
||||
var layer = _layers[i];
|
||||
targetAnimator.SetLayerWeight(i, layer.LayerWeight);
|
||||
targetAnimator.Play(layer.FullPathHash, i, layer.NormalizedTime);
|
||||
}
|
||||
|
||||
// 4. Принудительно пересчитываем кости скелета в текущем кадре
|
||||
targetAnimator.Update(0f);
|
||||
|
||||
// 5. Возвращаем локальное положение модели и настройки Root Motion
|
||||
Transform targetTransform = targetAnimator.transform;
|
||||
targetTransform.localPosition = _localPosition;
|
||||
targetTransform.localRotation = _localRotation;
|
||||
|
||||
targetAnimator.applyRootMotion = prevRootMotion;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Создаёт глубокую копию самого снапшота
|
||||
/// </summary>
|
||||
public AnimatorSnapshot Clone()
|
||||
{
|
||||
var clone = new AnimatorSnapshot
|
||||
{
|
||||
_localPosition = _localPosition,
|
||||
_localRotation = _localRotation,
|
||||
_speed = _speed
|
||||
};
|
||||
|
||||
if (_layers != null)
|
||||
{
|
||||
clone._layers = new LayerStateSnapshot[_layers.Length];
|
||||
Array.Copy(_layers, clone._layers, _layers.Length);
|
||||
}
|
||||
|
||||
if (_parameters != null)
|
||||
{
|
||||
clone._parameters = new ParameterSnapshot[_parameters.Length];
|
||||
Array.Copy(_parameters, clone._parameters, _parameters.Length);
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b014c8950464495f9b5022a81d6f5e2e
|
||||
timeCreated: 1793381535
|
||||
@@ -1,14 +0,0 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI;
|
||||
|
||||
namespace Code.OfficeGame.BattleMode.Actions
|
||||
{
|
||||
public class MovementBattleActionData : BattleActionData
|
||||
{
|
||||
public Vector3 Position;
|
||||
public Quaternion Rotation;
|
||||
public NavMeshPath Path;
|
||||
public Vector3[] Corners;
|
||||
public int CurrentCornerIndex;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace Code.OfficeGame.BattleMode.Actions
|
||||
{
|
||||
public class WaitBattleActionData : BattleActionData
|
||||
{
|
||||
public int WaitFrames;
|
||||
}
|
||||
}
|
||||
@@ -102,6 +102,7 @@ namespace Code.Game.OfficeGame.GameStates
|
||||
|
||||
builder.RegisterType(typeof(BattleActionMove), new[]{ typeof(BattleAction) }, Lifetime.Singleton, Resolution.Eager);
|
||||
builder.RegisterType(typeof(BattleActionWait), new[]{ typeof(BattleAction) }, Lifetime.Singleton, Resolution.Eager);
|
||||
builder.RegisterType(typeof(BattleActionNone), new[]{ typeof(BattleAction) }, Lifetime.Singleton, Resolution.Eager);
|
||||
|
||||
builder.RegisterType(typeof(BattleActionPositionDecider), new[]{ typeof(BattleActionDecider) } , Lifetime.Singleton, Resolution.Eager);
|
||||
builder.RegisterType(typeof(AsIsBattleActionDecider), new[]{ typeof(BattleActionDecider) } , Lifetime.Singleton, Resolution.Eager);
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Code.Game.Entities;
|
||||
using Code.Game.OfficeGame.BattleMode;
|
||||
using Code.Infrastructure.Camera;
|
||||
using Code.OfficeGame.BattleMode.Actions;
|
||||
@@ -24,18 +25,19 @@ namespace Code.OfficeGame.BattleMode.Timeline
|
||||
private readonly BattleTimelinePanel _timelinePanel;
|
||||
private List<EntityProperties> _entities;
|
||||
private DisposableBag _battleSubscriptions;
|
||||
|
||||
|
||||
private Dictionary<BattleActionType, BattleAction> _battleActions;
|
||||
private Dictionary<DeciderType, BattleActionDecider> _battleActionDeciders;
|
||||
|
||||
private Dictionary<EntityId, BattleActionDecisionResult> _actionDecisionResults = new();
|
||||
private Dictionary<EntityId, BattleActionData> _battleActionDatas = new();
|
||||
|
||||
private BattleActionDecisionPanel _battleActionDecisionPanel;
|
||||
private CameraStateMachine _cameraStateMachine;
|
||||
|
||||
public BattleTimeline(
|
||||
BattleTimelinePanel timelinePanel,
|
||||
IEnumerable<BattleAction> battleActions,
|
||||
BattleTimelinePanel timelinePanel,
|
||||
IEnumerable<BattleAction> battleActions,
|
||||
IEnumerable<BattleActionDecider> battleActionDeciders,
|
||||
BattleActionDecisionPanel battleActionDecisionPanel,
|
||||
CameraStateMachine cameraStateMachine)
|
||||
@@ -43,9 +45,10 @@ namespace Code.OfficeGame.BattleMode.Timeline
|
||||
_cameraStateMachine = cameraStateMachine;
|
||||
_battleActionDecisionPanel = battleActionDecisionPanel;
|
||||
_timelinePanel = timelinePanel;
|
||||
_battleActions = battleActions.ToDictionary(x=>x.ActionType);
|
||||
_battleActions = battleActions.ToDictionary(x => x.ActionType);
|
||||
_battleActionDeciders = battleActionDeciders.ToDictionary(x => x.DeciderType);
|
||||
}
|
||||
|
||||
public async UniTask RunBattleLoopAsync(List<EntityProperties> entities, CancellationToken ctx)
|
||||
{
|
||||
_entities = entities;
|
||||
@@ -67,7 +70,7 @@ namespace Code.OfficeGame.BattleMode.Timeline
|
||||
// _timelinePanel.SetAction(action);
|
||||
// })
|
||||
// .AddTo(ref _battleSubscriptions);
|
||||
|
||||
|
||||
await DecideActions(_entities, ctx);
|
||||
|
||||
while (!ctx.IsCancellationRequested)
|
||||
@@ -83,28 +86,6 @@ namespace Code.OfficeGame.BattleMode.Timeline
|
||||
}
|
||||
}
|
||||
|
||||
private async UniTask DecideActions(List<EntityProperties> entities, CancellationToken ctx)
|
||||
{
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
Debug.Log($"Deciding action for {entity.name}");
|
||||
|
||||
var entityId = entity.GetEntityId();
|
||||
|
||||
_actionDecisionResults.Remove(entityId);
|
||||
|
||||
_actionDecisionResults[entityId] = await DecideAction(ctx, entity);
|
||||
|
||||
_battleActionDatas.Remove(entityId);
|
||||
|
||||
var decidedAction = _actionDecisionResults[entityId];
|
||||
|
||||
_battleActionDatas[entityId] =
|
||||
_battleActions[decidedAction.ActionType]
|
||||
.MountAndGetFrames(entity, decidedAction);
|
||||
}
|
||||
}
|
||||
|
||||
private async UniTask<List<EntityProperties>> PlayTimelineUntilPause(CancellationToken ctx)
|
||||
{
|
||||
float accumulator = 0f;
|
||||
@@ -119,7 +100,10 @@ namespace Code.OfficeGame.BattleMode.Timeline
|
||||
while (accumulator >= TickDelta)
|
||||
{
|
||||
accumulator -= TickDelta;
|
||||
SimulateSingleFrame(readyEntities);
|
||||
|
||||
_currentBattleFrame.Value++;
|
||||
|
||||
SimulateSingleFrame(readyEntities, _entities);
|
||||
|
||||
if (readyEntities.Count > 0) return readyEntities;
|
||||
}
|
||||
@@ -128,23 +112,110 @@ namespace Code.OfficeGame.BattleMode.Timeline
|
||||
return readyEntities;
|
||||
}
|
||||
|
||||
private void SimulateSingleFrame(List<EntityProperties> outReadyEntities)
|
||||
public async UniTask PreviewBattleLoopAsync(int frameCount,
|
||||
List<EntityProperties> entities,
|
||||
CancellationToken ctx)
|
||||
{
|
||||
_currentBattleFrame.Value++;
|
||||
|
||||
foreach (var entity in _entities)
|
||||
while (!ctx.IsCancellationRequested)
|
||||
{
|
||||
Dictionary<EntityId, BattleActionData> actionDataSnapshots = new();
|
||||
Dictionary<EntityId, BattleActionDecisionResult> resultsSnapshots = new();
|
||||
Dictionary<EntityId, EntitySnapshot> entitySnapshots = new();
|
||||
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
var entityId = entity.GetEntityId();
|
||||
|
||||
if (!_actionDecisionResults.TryGetValue(entityId, out var actionDecisionResult)
|
||||
|| !_battleActionDatas.TryGetValue(entityId, out var battleActionData))
|
||||
continue;
|
||||
|
||||
resultsSnapshots[entityId] = actionDecisionResult.GetSnapshot();
|
||||
actionDataSnapshots[entityId] = battleActionData.GetSnapshot();
|
||||
entitySnapshots[entityId] = entity.GetSnapshot();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Debug.Log(frameCount);
|
||||
|
||||
await PlayTimelineForCertainFrames(frameCount, ctx).AttachExternalCancellation(ctx);
|
||||
}
|
||||
finally
|
||||
{
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
var entityId = entity.GetEntityId();
|
||||
|
||||
if (!resultsSnapshots.TryGetValue(entityId, out var actionDecisionResult)
|
||||
|| !actionDataSnapshots.TryGetValue(entityId, out var battleActionData))
|
||||
continue;
|
||||
|
||||
if(!_actionDecisionResults.TryAdd(entityId, actionDecisionResult))
|
||||
_actionDecisionResults[entityId]?.RestoreSnapshot(actionDecisionResult);
|
||||
|
||||
if(!_battleActionDatas.TryAdd(entityId, battleActionData))
|
||||
_battleActionDatas[entityId]?.RestoreSnapshot(battleActionData);
|
||||
|
||||
entity.RestoreSnapshot(entitySnapshots[entityId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async UniTask PlayTimelineForCertainFrames(int frameCount, CancellationToken ctx)
|
||||
{
|
||||
float accumulator = 0f;
|
||||
var readyEntities = new List<EntityProperties>();
|
||||
|
||||
while (frameCount > 0 && !ctx.IsCancellationRequested)
|
||||
{
|
||||
await UniTask.Yield(PlayerLoopTiming.Update, ctx);
|
||||
|
||||
accumulator += Time.unscaledDeltaTime;
|
||||
|
||||
while (accumulator >= TickDelta)
|
||||
{
|
||||
accumulator -= TickDelta;
|
||||
|
||||
frameCount--;
|
||||
|
||||
SimulateSingleFrame(readyEntities, _entities);
|
||||
|
||||
if (readyEntities.Count > 0)
|
||||
{
|
||||
foreach (var entity in readyEntities)
|
||||
{
|
||||
var entityID = entity.GetEntityId();
|
||||
|
||||
_battleActionDatas.Remove(entityID);
|
||||
|
||||
_actionDecisionResults.Remove(entityID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SimulateSingleFrame(List<EntityProperties> outReadyEntities, List<EntityProperties> inEntities)
|
||||
{
|
||||
foreach (var entity in inEntities)
|
||||
{
|
||||
var entityId = entity.GetEntityId();
|
||||
|
||||
var finished = _battleActions[_actionDecisionResults[entityId].ActionType]
|
||||
|
||||
if (!_actionDecisionResults.TryGetValue(entityId, out var actionDecisionResult)
|
||||
|| !_battleActionDatas.TryGetValue(entityId, out var battleActionData))
|
||||
continue;
|
||||
|
||||
var finished = _battleActions[actionDecisionResult.ActionType]
|
||||
.Tick(
|
||||
TickDelta,
|
||||
entity,
|
||||
_actionDecisionResults[entityId],
|
||||
_battleActionDatas[entityId]);
|
||||
|
||||
TickDelta,
|
||||
entity,
|
||||
actionDecisionResult,
|
||||
battleActionData);
|
||||
|
||||
entity.animator.Update(TickDelta);
|
||||
|
||||
|
||||
if (finished)
|
||||
{
|
||||
outReadyEntities.Add(entity);
|
||||
@@ -155,55 +226,111 @@ namespace Code.OfficeGame.BattleMode.Timeline
|
||||
Physics.SyncTransforms();
|
||||
}
|
||||
|
||||
private async UniTask<BattleActionDecisionResult> DecideAction(CancellationToken ctx, EntityProperties entity)
|
||||
private async UniTask DecideActions(List<EntityProperties> entities, CancellationToken ctx)
|
||||
{
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
Debug.Log($"Deciding action for {entity.name}");
|
||||
|
||||
var entityId = entity.GetEntityId();
|
||||
|
||||
var (decisionResult, battleActionData) = await DecideAction(ctx, entity);
|
||||
|
||||
_actionDecisionResults[entityId] = decisionResult;
|
||||
_battleActionDatas[entityId] = battleActionData;
|
||||
}
|
||||
}
|
||||
|
||||
private async UniTask<(BattleActionDecisionResult, BattleActionData)> DecideAction(CancellationToken ctx,
|
||||
EntityProperties entity)
|
||||
|
||||
{
|
||||
_battleActionDecisionPanel.SetEntityInfo(entity.battleActionTypes);
|
||||
|
||||
BattleActionType decidedAction = BattleActionType.None;
|
||||
BattleActionType decidedActionType = BattleActionType.None;
|
||||
|
||||
BattleActionDecisionResult decision = null;
|
||||
BattleActionDecisionResult decisionResult = null;
|
||||
BattleActionData actionData = null;
|
||||
|
||||
while (decidedAction == BattleActionType.None || ctx.IsCancellationRequested)
|
||||
CancellationTokenSource previewCancellationToken = new();
|
||||
|
||||
var entityId = entity.GetEntityId();
|
||||
|
||||
BattleActionData entityActionDataSnapshot = null;
|
||||
BattleActionDecisionResult entityResultSnapshot = null;
|
||||
|
||||
if (_battleActionDatas.TryGetValue(entityId, out var battleActionData))
|
||||
entityActionDataSnapshot = battleActionData.GetSnapshot();
|
||||
|
||||
if (_actionDecisionResults.TryGetValue(entityId, out var actionDecisionResult))
|
||||
entityResultSnapshot = actionDecisionResult.GetSnapshot();
|
||||
|
||||
while (decidedActionType == BattleActionType.None || ctx.IsCancellationRequested)
|
||||
{
|
||||
Cursor.lockState = CursorLockMode.Confined;
|
||||
|
||||
((CameraBattleModeState)_cameraStateMachine.CurrentBaseState).battleDeciding = true;
|
||||
|
||||
|
||||
await _battleActionDecisionPanel.Open(ctx);
|
||||
|
||||
decidedAction = await _battleActionDecisionPanel.GetDecision(ctx);
|
||||
decidedActionType = await _battleActionDecisionPanel.GetDecision(ctx);
|
||||
|
||||
if (decidedActionType == BattleActionType.None) continue;
|
||||
|
||||
var deciderType = _battleActions[decidedActionType].DeciderType;
|
||||
|
||||
if (decidedAction == BattleActionType.None) continue;
|
||||
|
||||
var deciderType = _battleActions[decidedAction].DeciderType;
|
||||
|
||||
_battleActionDeciders[deciderType].MountDecider();
|
||||
|
||||
|
||||
while (!ctx.IsCancellationRequested)
|
||||
{
|
||||
Cursor.lockState = CursorLockMode.Locked;
|
||||
|
||||
((CameraBattleModeState)_cameraStateMachine.CurrentBaseState).battleDeciding = false;
|
||||
|
||||
await _battleActionDecisionPanel.Close(ctx);
|
||||
|
||||
decision = await _battleActionDeciders[deciderType].GetDecision();
|
||||
|
||||
if (decision.ResultCode == BattleActionDecisionResultCode.CancelDecision)
|
||||
decidedAction = BattleActionType.None;
|
||||
await _battleActionDecisionPanel.Close(ctx);
|
||||
|
||||
decisionResult = await _battleActionDeciders[deciderType].GetDecision();
|
||||
decisionResult.ActionType = decidedActionType;
|
||||
|
||||
Debug.Log(decisionResult.ResultCode);
|
||||
|
||||
if (decision.ResultCode != BattleActionDecisionResultCode.Deciding)
|
||||
actionData = _battleActions[decidedActionType].MountAndGetFrames(entity, decisionResult);
|
||||
|
||||
previewCancellationToken.Cancel();
|
||||
previewCancellationToken.Dispose();
|
||||
|
||||
if(decisionResult.ResultCode == BattleActionDecisionResultCode.Deciding)
|
||||
{
|
||||
_actionDecisionResults[entityId] = decisionResult;
|
||||
_battleActionDatas[entityId] = actionData;
|
||||
|
||||
previewCancellationToken = new();
|
||||
|
||||
PreviewBattleLoopAsync(actionData.TotalFrames, _entities, previewCancellationToken.Token)
|
||||
.Forget();
|
||||
}
|
||||
|
||||
if (decisionResult.ResultCode == BattleActionDecisionResultCode.CancelDecision)
|
||||
decidedActionType = BattleActionType.None;
|
||||
|
||||
if (decisionResult.ResultCode != BattleActionDecisionResultCode.Deciding)
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
_battleActionDeciders[deciderType].UnmountDecider();
|
||||
}
|
||||
|
||||
if (decision == null) throw new ArgumentNullException(nameof(decision));
|
||||
|
||||
decision.ActionType = decidedAction;
|
||||
if(entityResultSnapshot != null
|
||||
&& !_actionDecisionResults.TryAdd(entityId, entityResultSnapshot))
|
||||
_actionDecisionResults[entityId]?.RestoreSnapshot(entityResultSnapshot);
|
||||
|
||||
return decision;
|
||||
if(entityActionDataSnapshot != null
|
||||
&& !_battleActionDatas.TryAdd(entityId, entityActionDataSnapshot))
|
||||
_battleActionDatas[entityId]?.RestoreSnapshot(entityActionDataSnapshot);
|
||||
|
||||
if (decisionResult == null || actionData == null) throw new ArgumentNullException(nameof(decisionResult));
|
||||
|
||||
return (decisionResult, actionData);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user