Files
gm_construct_port/code/entities/car/CarCamera.cs
2021-08-05 17:54:51 +03:00

229 lines
6.8 KiB
C#

using Sandbox;
using System;
public class CarCamera : Camera
{
protected virtual float MinFov => 80.0f;
protected virtual float MaxFov => 100.0f;
protected virtual float MaxFovSpeed => 1000.0f;
protected virtual float FovSmoothingSpeed => 4.0f;
protected virtual float OrbitCooldown => 0.6f;
protected virtual float OrbitSmoothingSpeed => 25.0f;
protected virtual float OrbitReturnSmoothingSpeed => 4.0f;
protected virtual float MinOrbitPitch => -25.0f;
protected virtual float MaxOrbitPitch => 70.0f;
protected virtual float FixedOrbitPitch => 10.0f;
protected virtual float OrbitHeight => 35.0f;
protected virtual float OrbitDistance => 150.0f;
protected virtual float MaxOrbitReturnSpeed => 100.0f;
protected virtual float MinCarPitch => -60.0f;
protected virtual float MaxCarPitch => 60.0f;
protected virtual float FirstPersonPitch => 10.0f;
protected virtual float CarPitchSmoothingSpeed => 1.0f;
protected virtual float CollisionRadius => 8.0f;
protected virtual float ShakeSpeed => 10.0f;
protected virtual float ShakeSpeedThreshold => 1500.0f;
protected virtual float ShakeMaxSpeed => 2500.0f;
protected virtual float ShakeMaxLength => 1.0f;
private bool orbitEnabled;
private TimeSince timeSinceOrbit;
private Angles orbitAngles;
private Rotation orbitYawRot;
private Rotation orbitPitchRot;
private float currentFov;
private float carPitch;
private bool firstPerson;
public override void Activated()
{
var pawn = Local.Pawn;
if ( pawn == null ) return;
orbitEnabled = false;
timeSinceOrbit = 0.0f;
orbitAngles = Angles.Zero;
orbitYawRot = Rotation.Identity;
orbitPitchRot = Rotation.Identity;
currentFov = MinFov;
carPitch = 0;
firstPerson = false;
var car = (pawn as SandboxPlayer)?.Vehicle as CarEntity;
if ( !car.IsValid() ) return;
orbitYawRot = firstPerson ? Rotation.Identity : Rotation.FromYaw( car.Rotation.Yaw() );
orbitPitchRot = firstPerson ? Rotation.FromPitch( FirstPersonPitch ) : Rotation.Identity;
orbitAngles = (orbitYawRot * orbitPitchRot).Angles();
}
public override void Update()
{
var pawn = Local.Pawn;
if ( pawn == null ) return;
var car = (pawn as SandboxPlayer)?.Vehicle as CarEntity;
if ( !car.IsValid() ) return;
var body = car.PhysicsBody;
if ( !body.IsValid() )
return;
var speed = car.MovementSpeed;
var speedAbs = Math.Abs( speed );
if ( orbitEnabled && timeSinceOrbit > OrbitCooldown )
orbitEnabled = false;
var carRot = car.Rotation;
carPitch = carPitch.LerpTo( car.Grounded ? carRot.Pitch().Clamp( MinCarPitch, MaxCarPitch ) * (speed < 0.0f ? -1.0f : 1.0f) : 0.0f, Time.Delta * CarPitchSmoothingSpeed );
if ( orbitEnabled )
{
var slerpAmount = Time.Delta * OrbitSmoothingSpeed;
orbitYawRot = Rotation.Slerp( orbitYawRot, Rotation.From( 0.0f, orbitAngles.yaw, 0.0f ), slerpAmount );
orbitPitchRot = Rotation.Slerp( orbitPitchRot, Rotation.From( orbitAngles.pitch + carPitch, 0.0f, 0.0f ), slerpAmount );
}
else
{
if ( firstPerson )
{
var targetYaw = 0;
var targetPitch = FirstPersonPitch;
var slerpAmount = Time.Delta * OrbitReturnSmoothingSpeed;
orbitYawRot = Rotation.Slerp( orbitYawRot, Rotation.FromYaw( targetYaw ), slerpAmount );
orbitPitchRot = Rotation.Slerp( orbitPitchRot, Rotation.FromPitch( targetPitch ), slerpAmount );
}
else
{
var targetPitch = FixedOrbitPitch.Clamp( MinOrbitPitch, MaxOrbitPitch );
var targetYaw = !firstPerson && speed < 0.0f ? carRot.Yaw() + 180.0f : carRot.Yaw();
var slerpAmount = MaxOrbitReturnSpeed > 0.0f ? Time.Delta * (speedAbs / MaxOrbitReturnSpeed).Clamp( 0.0f, OrbitReturnSmoothingSpeed ) : 1.0f;
orbitYawRot = Rotation.Slerp( orbitYawRot, Rotation.FromYaw( targetYaw ), slerpAmount );
orbitPitchRot = Rotation.Slerp( orbitPitchRot, Rotation.FromPitch( targetPitch + carPitch ), slerpAmount );
}
orbitAngles.pitch = orbitPitchRot.Pitch();
orbitAngles.yaw = orbitYawRot.Yaw();
orbitAngles = orbitAngles.Normal;
}
if ( firstPerson )
{
DoFirstPerson();
}
else
{
DoThirdPerson( car, body );
}
currentFov = MaxFovSpeed > 0.0f ? currentFov.LerpTo( MinFov.LerpTo( MaxFov, speedAbs / MaxFovSpeed ), Time.Delta * FovSmoothingSpeed ) : MaxFov;
FieldOfView = currentFov;
ApplyShake( speedAbs );
}
private void DoFirstPerson()
{
var pawn = Local.Pawn;
if ( pawn == null ) return;
Pos = pawn.EyePos;
Rot = pawn.Rotation * (orbitYawRot * orbitPitchRot);
Viewer = pawn;
}
private void DoThirdPerson( CarEntity car, PhysicsBody body )
{
Rot = orbitYawRot * orbitPitchRot;
var carPos = car.Position + car.Rotation * (body.LocalMassCenter * car.Scale);
var startPos = carPos;
var targetPos = startPos + Rot.Backward * (OrbitDistance * car.Scale) + (Vector3.Up * (OrbitHeight * car.Scale));
var tr = Trace.Ray( startPos, targetPos )
.Ignore( car )
.Radius( Math.Clamp( CollisionRadius * car.Scale, 2.0f, 10.0f ) )
.WorldOnly()
.Run();
Pos = tr.EndPos;
Viewer = null;
}
public override void BuildInput( InputBuilder input )
{
base.BuildInput( input );
var pawn = Local.Pawn;
if ( pawn == null ) return;
var car = (pawn as SandboxPlayer)?.Vehicle as CarEntity;
if ( !car.IsValid() ) return;
if ( input.Pressed( InputButton.View ) )
{
firstPerson = !firstPerson;
orbitYawRot = firstPerson ? Rotation.Identity : Rotation.FromYaw( car.Rotation.Yaw() );
orbitPitchRot = firstPerson ? Rotation.FromPitch( FirstPersonPitch ) : Rotation.Identity;
orbitAngles = (orbitYawRot * orbitPitchRot).Angles();
orbitEnabled = false;
timeSinceOrbit = 0.0f;
}
if ( (Math.Abs( input.AnalogLook.pitch ) + Math.Abs( input.AnalogLook.yaw )) > 0.0f )
{
if ( !orbitEnabled )
{
orbitAngles = (orbitYawRot * orbitPitchRot).Angles();
orbitAngles = orbitAngles.Normal;
orbitYawRot = Rotation.From( 0.0f, orbitAngles.yaw, 0.0f );
orbitPitchRot = Rotation.From( orbitAngles.pitch, 0.0f, 0.0f );
}
orbitEnabled = true;
timeSinceOrbit = 0.0f;
orbitAngles.yaw += input.AnalogLook.yaw;
orbitAngles.pitch += input.AnalogLook.pitch;
orbitAngles = orbitAngles.Normal;
orbitAngles.pitch = orbitAngles.pitch.Clamp( MinOrbitPitch, MaxOrbitPitch );
}
if ( firstPerson )
{
input.ViewAngles = (car.Rotation * Rotation.From( orbitAngles )).Angles();
}
else
{
input.ViewAngles = orbitEnabled ? orbitAngles : car.Rotation.Angles();
}
input.ViewAngles = input.ViewAngles.Normal;
}
private void ApplyShake( float speed )
{
if ( speed < ShakeSpeedThreshold )
return;
var pos = (Time.Now % MathF.PI) * ShakeSpeed;
var length = (speed - ShakeSpeedThreshold) / (ShakeMaxSpeed - ShakeSpeedThreshold);
length = length.Clamp( 0, ShakeMaxLength );
float x = Noise.Perlin( pos, 0, 0 ) * length;
float y = Noise.Perlin( pos, 5.0f, 0 ) * length;
Pos += Rot.Right * x + Rot.Up * y;
Rot *= Rotation.FromAxis( Vector3.Up, x );
Rot *= Rotation.FromAxis( Vector3.Right, y );
}
}