Player/CameraController.cs
using Sandbox.UI;
namespace PlanetMeat;
[Group( "Planet Meat - Player" ), Title( "Camera Controller" ), Icon( "photo_camera_front" )]
public sealed class CameraController : Component, IAgendaPhaseListener
{
const float PAN_SPEED = 0.75f;
const float ORBIT_SPEED = -35.0f;
const float MIN_Z_DIST = 800.0f;
const float MAX_Z_DIST = MIN_Z_DIST + ZOOM_STEP * 5;
const float ZOOM_STEP = 500.0f;
const float ZOOM_SPEED = 10.0f;
const float SPEED_MODIFIER_STRENGTH = 2.0f;
const float MIN_PITCH = -80f;
const float MAX_PITCH = -10f;
public static CameraController Local { get; private set; }
private static Plane AimPlane { get; } = new( Vector3.Zero, Vector3.Up );
public GameObject Armature;
public Vector3 AimPosition { get; private set; } = Vector3.Zero;
public Vector3 PreviousAimPosition { get; private set; } = Vector3.Zero;
public float ViewYaw => Armature.WorldRotation.Angles().yaw;
public Ray MouseRay => Scene.Camera.ScreenPixelToRay( Mouse.Position );
[Property] private Vector2 Bounds { get; set; }
private Panel _previousFocus;
private float TargetZ { get; set; } = -1.0f;
protected override void OnStart()
{
base.OnStart();
Armature = GameObject.Parent;
if ( !IsProxy )
Local = this;
Mouse.Visibility = MouseVisibility.Visible;
TargetZ = MAX_Z_DIST;
}
private void ForceFocusToPassInputs( Panel focus )
{
if ( _previousFocus != focus )
{
if ( focus?.IsValid ?? false )
{
focus.ButtonInput = PanelInputType.Game;
_previousFocus = focus;
}
else
{
_previousFocus = null;
}
}
}
protected override void OnUpdate()
{
base.OnUpdate();
ForceFocusToPassInputs( InputFocus.Current );
if ( Input.Pressed( "ZoomIn" ) )
TargetZ -= ZOOM_STEP;
else if ( Input.Pressed( "ZoomOut" ) )
TargetZ += ZOOM_STEP;
TargetZ = TargetZ.Clamp( MIN_Z_DIST, MAX_Z_DIST );
if ( !LocalPosition.z.AlmostEqual( TargetZ ) )
LocalPosition = LocalPosition.LerpTo( LocalPosition.WithZ( TargetZ ), ZOOM_SPEED * Time.Delta );
var angles = Armature.WorldRotation.Angles();
var movt = Input.AnalogMove.RotateAround( Vector3.Zero, new Angles( 0.0f, angles.yaw, 0.0f ) ).ClampLength( 1.0f );
if ( Input.Down( "Faster" ) )
movt *= SPEED_MODIFIER_STRENGTH;
else if ( Input.Down( "Slower" ) )
movt /= SPEED_MODIFIER_STRENGTH;
var nextPosition = Armature.WorldPosition + movt * PAN_SPEED * Time.Delta * LocalPosition.z;
if ( Bounds.x > 0 )
nextPosition.x = nextPosition.x.Clamp( -Bounds.x, Bounds.x );
if ( Bounds.y > 0 )
nextPosition.y = nextPosition.y.Clamp( -Bounds.y, Bounds.y );
Armature.WorldPosition = nextPosition;
if ( Input.Down( "CamRotate" ) )
Armature.WorldRotation = angles
.WithYaw( angles.yaw + ORBIT_SPEED * Mouse.Delta.x * Time.Delta )
.WithPitch( (angles.pitch - ORBIT_SPEED * Mouse.Delta.y * Time.Delta).Clamp( MIN_PITCH, MAX_PITCH ) );
PreviousAimPosition = AimPosition;
AimPosition = AimPlane.IntersectRay( MouseRay ) ?? Vector3.Zero;
var tool = PlayerTool.Selected;
if ( tool is null )
return;
tool.OnHover( AimPosition );
if ( Input.Pressed( "Place" ) )
{
tool.OnPlaceStarted( AimPosition );
}
else
{
var placeDown = Input.Down( "Place" );
var placeReleased = Input.Released( "Place" );
if ( placeDown || placeReleased )
tool.OnPlaceUpdated( PreviousAimPosition, AimPosition );
if ( placeReleased )
tool.OnPlaceEnded( AimPosition );
}
if ( Input.Pressed( "Cancel" ) )
tool.OnCancel( AimPosition );
}
public void OnTimelinePhase( Agenda.Phases _prev, Agenda.Phases _current )
{
PlayerTool.Selected?.EndUse();
}
}