Player/PlayerController.Camera.cs
using System.Net.Security;
namespace Opium;
public partial class PlayerController
{
[Property] public bool LockMovement { get; set; } = false;
[Property] public bool LockCameraMovement { get; set; } = false;
[Property] public CameraComponent MainCamera { get; set; }
[Property] public CameraComponent ViewModelCamera { get; set; }
[Property] public GameObject ViewModelOrigin { get; set; }
public Vector3 CameraPositionOffset { get; set; } = Vector3.Zero;
public Rotation CameraRotationOffset { get; set; } = Rotation.Identity;
public float FieldOfViewOffset { get; set; } = 0f;
// Actor
public override GameObject CameraObject => MainCamera.GameObject;
// Fields
Rotation lerpedRotationOffset = Rotation.Identity;
Vector3 lerpedAimOffset = Vector3.Zero;
Vector3 velocity = Vector3.Zero;
float cameraSway = 0;
float cameraBobSpeed = 5.0f;
float cameraBobAmount = 5.0f;
float lean = 0.0f;
float fovOffset = 0.0f;
float lookyawoffset = 0.0f;
float jumpoffset = 0.0f;
float duckoffset = 0.0f;
float avoidanceDistance = 0f;
public Vector3 ExtraCamOffset { get; set; }
/// <summary>
/// Runs every update for the camera. This code's a big mess.
/// </summary>
private void UpdateCamera()
{
var camera = MainCamera;
if ( camera is null ) return;
camera.FieldOfView = Screen.CreateVerticalFieldOfView( GameSettingsSystem.Current.FieldOfView );
if ( ViewModelCamera is not null )
{
ViewModelCamera.FieldOfView = Screen.CreateVerticalFieldOfView( GameSettingsSystem.Current.WeaponFieldOfView );
}
if ( Opium.GameManager.IsPaused ) return;
var targetCameraPos = Transform.Position + new Vector3( 0, 0, EyeHeight );
if ( !IsAlive )
{
camera.Transform.Position = targetCameraPos + CameraPositionOffset + ExtraCamOffset;
camera.Transform.Rotation = EyeAngles;
lerpedRotationOffset = Rotation.Lerp( lerpedRotationOffset, CameraRotationOffset, Time.Delta * 7.5f );
camera.Transform.Rotation *= lerpedRotationOffset;
CameraRotationOffset = Rotation.Identity;
return;
}
var targetEyeHeight = GetEyeHeightOffset();
EyeHeight = EyeHeight.LerpTo( targetEyeHeight, Time.Delta * 10.0f );
// smooth view z, so when going up and down stairs or ducking, it's smooth af
if ( lastUngrounded > 0.2f )
{
targetCameraPos.z = camera.Transform.Position.z.LerpTo( targetCameraPos.z, Time.Delta * 25.0f );
}
var localCameraOffset = Vector3.Zero;
var bobSpeed = cameraBobSpeed * GameSettingsSystem.Current.ViewbobScale;
velocity = velocity.LerpTo( CharacterController.Velocity, Time.Delta * 10f );
if ( !LockCameraMovement )
{
if ( !velocity.Length.AlmostEqual( 0 ) && CharacterController.IsOnGround )
{
cameraBobAmount += Time.Delta * bobSpeed * velocity.Length.LerpInverse( 0, 75 );
float xOffset = MathF.Sin( cameraBobAmount * 1.25f ) * 0.25f;
float yOffset = MathF.Cos( cameraBobAmount * 2 ) * 0.25f;
localCameraOffset += camera.Transform.Rotation.Right * xOffset;
localCameraOffset += camera.Transform.Rotation.Up * yOffset;
targetCameraPos += localCameraOffset;
lean = lean.LerpTo( velocity.Dot( camera.Transform.Rotation.Right ), Time.Delta * 20f );
lookyawoffset = velocity.Dot( camera.Transform.Rotation.Right ) * 0.1f;
}
else
{
cameraBobAmount = 0.0f;
cameraSway = (MathF.Sin( Time.Now * 2.0f ) * 0.75f).SnapToGrid( 0.08f );
targetCameraPos += camera.Transform.Rotation.Right * cameraSway * 0.2f;
lean = lean.LerpTo( 0.0f, Time.Delta * 2.0f );
lookyawoffset = lookyawoffset.LerpTo( Input.AnalogLook.yaw * 4.0f, Time.Delta * 2.0f );
}
}
jumpoffset = jumpoffset.LerpTo( velocity.z * 0.5f, Time.Delta * 2f );
bool hasExtraCamOffset = !ExtraCamOffset.IsNearlyZero();
ViewModelOrigin.Transform.LocalRotation = Rotation.Identity;
if ( hasExtraCamOffset )
{
targetCameraPos = Transform.Position + new Vector3( 0, 0, EyeHeight );
bobSpeed = 0;
cameraSway = 0;
lean = 0;
lookyawoffset = 0;
}
else
{
ViewModelOrigin.Transform.LocalPosition = localCameraOffset;
ViewModelOrigin.Transform.LocalPosition += Vector3.Up * jumpoffset * 0.075f;
ViewModelOrigin.Transform.LocalRotation *= Rotation.FromPitch( -cameraSway );
}
camera.Transform.Position = targetCameraPos + CameraPositionOffset + ExtraCamOffset;
camera.Transform.Rotation = EyeAngles;
lerpedRotationOffset = Rotation.Lerp( lerpedRotationOffset, CameraRotationOffset, Time.Delta * 7.5f );
camera.Transform.Rotation *= lerpedRotationOffset;
if ( !hasExtraCamOffset )
{
ViewModelOrigin.Transform.LocalRotation *= lerpedRotationOffset * 0.2f;
ViewModelOrigin.Transform.LocalPosition += CameraPositionOffset * 0.2f;
}
if ( Inventory.Current?.ViewModel?.Components.Get<ViewModel>() is ViewModel vm && vm.WeaponLength > 0f && !hasExtraCamOffset )
{
var bone = vm.ModelRenderer.SceneModel.GetBoneLocalTransform( "camera" );
camera.Transform.Position += bone.Position;
camera.Transform.Rotation *= bone.Rotation;
var dist = vm.WeaponLength;
Vector3 offset = vm.WeaponAvoidanceOffset;
Angles angles = vm.WeaponAvoidanceAngles;
if ( Components.Get<InteractUse>()?.LookingAtObject is not null )
{
dist /= 2;
}
if ( Inventory.Current is MeleeWeapon meleeWeapon && meleeWeapon.CurrentAttack?.State != AttackState.Inactive )
{
dist /= 4;
}
var tr = Scene.Trace.Ray( ViewModelOrigin.Transform.Position, ViewModelOrigin.Transform.Position + ViewModelOrigin.Transform.Rotation.Forward * dist )
.IgnoreGameObjectHierarchy( GameObject )
.Run();
var distToMove = dist - tr.Distance;
avoidanceDistance = avoidanceDistance.LerpTo( distToMove, Time.Delta * 10f );
ViewModelOrigin.Transform.LocalPosition += ViewModelOrigin.Transform.Rotation.Forward * avoidanceDistance * offset.x;
ViewModelOrigin.Transform.LocalPosition += ViewModelOrigin.Transform.Rotation.Right * avoidanceDistance * offset.y;
ViewModelOrigin.Transform.LocalPosition += ViewModelOrigin.Transform.Rotation.Up * avoidanceDistance * offset.z;
ViewModelOrigin.Transform.LocalRotation *= Rotation.From( angles * -(avoidanceDistance / dist) );
}
else
{
ViewModelOrigin.Transform.LocalPosition = 0;
avoidanceDistance = avoidanceDistance.LerpTo( 0f, Time.Delta * 10f );
}
float targetFov = FieldOfViewOffset;
if ( Inventory.Current is MeleeWeapon melee )
{
if ( melee.CurrentAttack != null && melee.CurrentAttack.CurrentStateInfo.FieldOfViewOffset != 0f )
{
targetFov += melee.CurrentAttack.CurrentStateInfo.FieldOfViewOffset;
duckoffset = duckoffset.LerpTo( 0.0f, Time.Delta * 2.0f );
}
if ( melee.CurrentAttack == null )
{
var lookpitchoffet = EyeAngles.pitch.Remap( -90, 90, 10, -10 );
ViewModelOrigin.Transform.LocalRotation *= Rotation.FromPitch( -lookpitchoffet );
ViewModelOrigin.Transform.LocalRotation *= Rotation.FromRoll( -lookyawoffset );
if ( Crouching )
{
duckoffset = duckoffset.LerpTo( -75.0f, Time.Delta * 10.0f );
}
else
{
duckoffset = duckoffset.LerpTo( 0.0f, Time.Delta * 10.0f );
}
}
}
if ( Inventory.Current is RangedWeapon range )
{
var lookpitchoffet = EyeAngles.pitch.Remap( -90, 90, 5, -5 );
duckoffset = duckoffset.LerpTo( 0, Time.Delta * 5f );
ViewModelOrigin.Transform.LocalRotation *= Rotation.FromPitch( -lookpitchoffet );
ViewModelOrigin.Transform.LocalRotation *= Rotation.FromRoll( -lookyawoffset );
var up = Vector3.Up * 2f + Vector3.Right * -2f;
var targetAimOffset = range.IsAiming ? up : 0;
lerpedAimOffset = lerpedAimOffset.LerpTo( targetAimOffset, Time.Delta * 10f );
ViewModelOrigin.Transform.LocalPosition += lerpedAimOffset;
}
ViewModelOrigin.Transform.LocalRotation *= Rotation.FromRoll( duckoffset );
fovOffset = fovOffset.LerpTo( targetFov, Time.Delta * 6f );
camera.FieldOfView += fovOffset;
camera.Transform.Rotation *= Rotation.FromPitch( cameraSway );
camera.Transform.Position += camera.Transform.Rotation.Forward * cameraSway * 0.5f;
camera.Transform.Rotation *= Rotation.FromRoll( lean * 0.03f );
CameraPositionOffset = Vector3.Zero;
CameraRotationOffset = Rotation.Identity;
FieldOfViewOffset = 0;
CharacterController.Height = targetEyeHeight;
}
public override bool SetupViewModel( BaseWeapon weapon, bool isActive )
{
if ( !isActive )
{
if ( weapon.ViewModel.IsValid() )
{
weapon.ViewModel.Enabled = false;
}
}
else
{
if ( weapon.ViewModel.IsValid() )
{
weapon.ViewModel.Enabled = true;
}
else
{
weapon.ViewModel = weapon.ViewModelPrefab.Clone( new CloneConfig()
{
Parent = ViewModelOrigin,
Transform = global::Transform.Zero.WithScale( 1f ),
StartEnabled = true
} );
weapon.ViewModel.Tags.Add( "viewmodel" );
var vm = weapon.ViewModel.Components.Get<ViewModel>( true );
vm.Weapon = weapon;
vm.DoCodeAnimations();
weapon.OnCreateViewModel();
}
}
return true;
}
}