mirror of
https://github.com/celisej567/gm_construct_port.git
synced 2026-09-04 03:39:18 +03:00
229 lines
6.8 KiB
C#
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 );
|
|
}
|
|
}
|
|
|