Pawn/Camera/SkateCamera.cs
namespace Skateboard;
internal sealed class SkateCamera : Component
{
[Property] public CameraComponent Camera { get; set; }
[Property] public Skateboard.Player.SkatePawn Target { get; set; }
private Skateboard.Player.SkatePawn _cachedTarget;
private float orbitDistance = 200f;
private float orbitHeight = 80f;
private float vertCameraHeight = 400f;
private float vertCameraTall = 50f;
private float lookDown;
private float minimumDistance = 0.2f;
private float rotationLerpSpeed = 3f;
private float speedLerpSpeed = 5f;
private float speedDistanceMultiplier = 0.05f;
private float currentSpeedDistance;
private float currentVertLerp;
private float vertLerpSpeed = 1f;
private float vertRotationSpeed = 10f;
private float targetTowardsOriginOffset = 1f;
protected override void OnUpdate()
{
var camera = Camera ?? Components.GetOrCreate<CameraComponent>();
var pawn = Target ?? _cachedTarget;
if ( pawn is null || !pawn.IsValid() )
{
var localId = Connection.Local.Id;
pawn = Scene.GetAllComponents<Skateboard.Player.SkatePawn>()
.FirstOrDefault( x => x.GameObject.Network.IsOwner || x.OwningConnectionId == localId );
_cachedTarget = pawn;
}
if ( pawn is null )
return;
camera.FieldOfView = 90f;
var ragdoll = pawn.Ragdoll;
if ( pawn.Bailed && ragdoll.IsValid() )
{
var bailFocusPosition = ragdoll.WorldPosition;
var renderer = ragdoll.Components.Get<SkinnedModelRenderer>( FindMode.EverythingInSelfAndDescendants );
if ( renderer is not null )
{
if ( renderer.TryGetBoneTransform( "pelvis", out var pelvis ) ||
renderer.TryGetBoneTransform( "Pelvis", out pelvis ) ||
renderer.TryGetBoneTransform( "hips", out pelvis ) ||
renderer.TryGetBoneTransform( "Hips", out pelvis ) )
{
bailFocusPosition = pelvis.Position;
}
}
WorldPosition = bailFocusPosition + Vector3.Up * vertCameraHeight;
WorldRotation = Rotation.LookAt( Vector3.Down, pawn.WorldRotation.Forward );
return;
}
var controller = pawn.Controller;
var planarSpeed = pawn.Velocity.WithZ( 0 ).Length;
var pawnForward = pawn.WorldRotation.Forward;
var focusPosition = pawn.WorldPosition;
var focusUp = pawn.WorldRotation.Up;
if ( planarSpeed > Skateboard.Player.SkateController.StoppedVelocity && (controller?.IsGrounded ?? false) == false )
pawnForward = pawn.Velocity.WithZ( 0 ).Normal;
var pawnLook = Rotation.LookAt( pawnForward, Vector3.Up )
.RotateAroundAxis( Vector3.Left, lookDown );
var center = focusPosition + focusUp * orbitHeight;
var targetPos = center + WorldRotation.Backward * orbitDistance;
var speedOffset = planarSpeed * speedDistanceMultiplier;
currentSpeedDistance = MathX.Lerp(
currentSpeedDistance,
speedOffset,
speedLerpSpeed * Time.Delta
);
targetPos += WorldRotation.Backward * currentSpeedDistance;
var vertCamPos =
focusPosition +
focusUp * vertCameraTall +
Vector3.Up * vertCameraHeight;
var vertCenter = focusPosition + Vector3.Up * vertCameraHeight;
currentVertLerp = MathX.Lerp(
currentVertLerp,
pawn.OnVert ? 1f : 0f,
(pawn.OnVert ? vertLerpSpeed : speedLerpSpeed) * Time.Delta
);
targetPos = Vector3.Lerp( targetPos, vertCamPos, currentVertLerp );
center = Vector3.Lerp( center, vertCenter, currentVertLerp );
var dir = (targetPos - center).Normal;
center += dir * targetTowardsOriginOffset;
var trace = Scene.Trace.Ray( center, targetPos )
.WithAnyTags( "solid" )
.IgnoreGameObjectHierarchy( pawn.GameObject )
.IgnoreGameObjectHierarchy( ragdoll )
.Radius( 8f )
.Run();
WorldPosition =
trace.Fraction <= minimumDistance
? Vector3.Lerp( center, targetPos, minimumDistance )
: trace.EndPosition;
if ( pawn.OnVert )
{
pawnLook = Rotation.LookAt(
(pawn.WorldPosition - WorldPosition + pawn.WorldRotation.Up * vertCameraTall).Normal,
pawn.VertNormal
);
WorldRotation = Rotation.Slerp(
WorldRotation,
pawnLook,
vertRotationSpeed * Time.Delta
);
}
else
{
WorldRotation = Rotation.Slerp(
WorldRotation,
pawnLook,
rotationLerpSpeed * Time.Delta
);
}
}
}