Eye lerping on entities (#8151)

* eye lerping on entities

* small improvements
This commit is contained in:
Flipp Syder 2022-05-13 20:59:49 -07:00 committed by GitHub
parent b7be11d4d4
commit 55e86df25b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 104 additions and 33 deletions

View File

@ -6,6 +6,7 @@ using Robust.Client.Physics;
using Robust.Client.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Timing;
@ -14,37 +15,81 @@ namespace Content.Client.Eye;
public sealed class EyeLerpingSystem : EntitySystem
{
[Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
private Angle? _lastGridAngle;
private Angle? _lerpTo;
private Angle _lerpStartRotation;
private float _accumulator;
// How fast the camera rotates in radians / s
private const float CameraRotateSpeed = MathF.PI;
// Safety override
private const float LerpTimeMax = 1.5f;
// Lerping information for the player's active eye.
private readonly EyeLerpInformation _playerActiveEye = new();
// Eyes other than the primary eye that are currently active.
private readonly Dictionary<EntityUid, EyeLerpInformation> _activeEyes = new();
private readonly List<EntityUid> _toRemove = new();
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<EyeComponent, ComponentShutdown>(OnEyeShutdown);
UpdatesAfter.Add(typeof(TransformSystem));
UpdatesAfter.Add(typeof(PhysicsSystem));
UpdatesBefore.Add(typeof(EyeUpdateSystem));
}
private void OnEyeShutdown(EntityUid uid, EyeComponent component, ComponentShutdown args)
{
RemoveEye(uid);
}
public void AddEye(EntityUid uid)
{
if (!_activeEyes.ContainsKey(uid))
{
_activeEyes.Add(uid, new());
}
}
public void RemoveEye(EntityUid uid)
{
if (_activeEyes.ContainsKey(uid))
{
_activeEyes.Remove(uid);
}
}
public override void FrameUpdate(float frameTime)
{
if (!_gameTiming.IsFirstTimePredicted)
return;
var currentEye = _eyeManager.CurrentEye;
// Always do this one.
LerpPlayerEye(frameTime);
foreach (var (entity, info) in _activeEyes)
{
LerpEntityEye(entity, info, frameTime);
}
if (_toRemove.Count != 0)
{
foreach (var entity in _toRemove)
{
RemoveEye(entity);
}
_toRemove.Clear();
}
}
private void LerpPlayerEye(float frameTime)
{
if (_playerManager.LocalPlayer?.ControlledEntity is not {} mob || Deleted(mob))
return;
@ -52,62 +97,72 @@ public sealed class EyeLerpingSystem : EntitySystem
if (!TryComp(mob, out IMoverComponent? mover))
return;
var moverLastGridAngle = mover.LastGridAngle;
LerpEye(_eyeManager.CurrentEye, frameTime, mover.LastGridAngle, _playerActiveEye);
}
private void LerpEntityEye(EntityUid uid, EyeLerpInformation info, float frameTime)
{
if (!TryComp(uid, out TransformComponent? transform)
|| !TryComp(uid, out EyeComponent? eye)
|| eye.Eye == null
|| !_mapManager.TryGetGrid(transform.GridID, out var grid))
{
_toRemove.Add(uid);
return;
}
LerpEye(eye.Eye, frameTime, grid.WorldRotation, info);
}
private void LerpEye(IEye eye, float frameTime, Angle lastAngle, EyeLerpInformation lerpInfo)
{
// Let's not turn the camera into a washing machine when the game starts.
if (_lastGridAngle == null)
if (lerpInfo.LastGridAngle == null)
{
_lastGridAngle = moverLastGridAngle;
currentEye.Rotation = -moverLastGridAngle;
lerpInfo.LastGridAngle = lastAngle;
eye.Rotation = -lastAngle;
return;
}
// Check if the last lerp grid angle we have is not the same as the last mover grid angle...
if (!_lastGridAngle.Value.EqualsApprox(moverLastGridAngle))
if (!lerpInfo.LastGridAngle.Value.EqualsApprox(lastAngle))
{
// And now, we start lerping.
_lerpTo = moverLastGridAngle;
_lastGridAngle = moverLastGridAngle;
_lerpStartRotation = currentEye.Rotation;
_accumulator = 0f;
lerpInfo.LerpTo = lastAngle;
lerpInfo.LastGridAngle = lastAngle;
lerpInfo.LerpStartRotation = eye.Rotation;
lerpInfo.Accumulator = 0f;
}
if (_lerpTo != null)
if (lerpInfo.LerpTo != null)
{
_accumulator += frameTime;
lerpInfo.Accumulator += frameTime;
var lerpRot = -_lerpTo.Value.FlipPositive().Reduced();
var startRot = _lerpStartRotation.FlipPositive().Reduced();
var lerpRot = -lerpInfo.LerpTo.Value.FlipPositive().Reduced();
var startRot = lerpInfo.LerpStartRotation.FlipPositive().Reduced();
var changeNeeded = Angle.ShortestDistance(startRot, lerpRot);
if (changeNeeded.EqualsApprox(Angle.Zero))
{
// Nothing to do here!
CleanupLerp();
lerpInfo.Cleanup(eye);
return;
}
// Get how much the camera should have moved by now. Make it faster depending on the change needed.
var changeRot = (CameraRotateSpeed * Math.Max(1f, Math.Abs(changeNeeded) * 0.75f)) * _accumulator * Math.Sign(changeNeeded);
var changeRot = (CameraRotateSpeed * Math.Max(1f, Math.Abs(changeNeeded) * 0.75f)) * lerpInfo.Accumulator * Math.Sign(changeNeeded);
// How close is this from reaching the end?
var percentage = (float)Math.Abs(changeRot / changeNeeded);
currentEye.Rotation = Angle.Lerp(startRot, lerpRot, percentage);
eye.Rotation = Angle.Lerp(startRot, lerpRot, percentage);
// Either we have overshot, or we have taken way too long on this, emergency reset time
if (percentage >= 1.0f || _accumulator >= LerpTimeMax)
if (percentage >= 1.0f || lerpInfo.Accumulator >= LerpTimeMax)
{
CleanupLerp();
}
void CleanupLerp()
{
currentEye.Rotation = -_lerpTo.Value;
_lerpStartRotation = currentEye.Rotation;
_lerpTo = null;
_accumulator = 0f;
lerpInfo.Cleanup(eye);
}
}
else
@ -117,7 +172,23 @@ public sealed class EyeLerpingSystem : EntitySystem
// ghosting, this system listening for attached mob changes, and the eye rotation being reset after our
// changes back to zero because of an EyeComponent state coming from the server being applied.
// At some point we'll need to come up with a solution for that. But for now, I just want to fix this.
currentEye.Rotation = -moverLastGridAngle;
eye.Rotation = -lastAngle;
}
}
private sealed class EyeLerpInformation
{
public Angle? LastGridAngle { get; set; }
public Angle? LerpTo { get; set; }
public Angle LerpStartRotation { get; set; }
public float Accumulator { get; set; }
public void Cleanup(IEye eye)
{
eye.Rotation = -LerpTo ?? Angle.Zero;
LerpStartRotation = eye.Rotation;
LerpTo = null;
Accumulator = 0;
}
}
}