Files
sbox-public/engine/Sandbox.Engine/Scene/Components/Game/PlayerController/PlayerController.Camera.cs
Garry Newman ca96c2a945 Make Inventory, BaseWeapon, AmmoResource, Screenshake first party (#5245)
https://sbox.game/dev/doc/gameplay/inventory-weapons/

Added

InventoryComponent - a slot based inventory, hotbar or shared bucket slots, with a starting loadout
BaseInventoryItem - base class for inventory items, with hooks to veto switching, holstering and dropping
BaseWeapon - a GMod style base weapon - fire modes, magazines and reserve ammo, reloading, dry fire, ballistics, spread, crosshair and HUD
BaseWeaponModel - view/world model base with muzzle flash, brass ejection, tracers and animation hooks
Ammo types are resources (.ammo) - weapons share reserve pools by type, clamped to a max reserve
BaseWeapon.ShootBullets / ShootBullet - bullet tracing with damage, impulse and hitbox tags
Weapon hold types editor
Animgraph enum parameters can be set by option name - Renderer.Set( "holdtype", "pistol" )
NpcUsage on BaseWeapon - engagement range and burst behaviour so NPCs can use any weapon
ICameraModifier - cameras compose their view through an ordered modifier chain, read the result from CameraComponent.View
CameraEffectSystem - screen shake, directional punches and tilts, with world epicenter falloff - camera.AddShake / AddPunch / AddTilt
EnvShake component - shakes the view, rumbles controllers, kicks physics
TracerEffect - an animated tracer line, ITargetedEffect for effects that fly at a target
SceneAnchor - a world position that can follow an object or bone
SkinnedModelRenderer.GetClosestBone( worldPosition )
Vector3.WithAimCone can take a custom Random for reproducible spread
Improve/Fix

PlayerController searches parents for IPressable when pressing
PlayerController camera runs through the modifier chain - IEvents.PostCameraSetup is obsolete
SoundPlayer preview uses shortcuts - Space toggles playback, Ctrl+Space restarts
Fixed Radius( 0 ) traces missing everything
Fixed NRE in AssetPreviewWidget
Removed

Removed CameraComponent.ISceneCameraSetup (obsolete since October, nothing uses)
2026-07-04 12:46:59 +01:00

108 lines
3.5 KiB
C#

namespace Sandbox;
public sealed partial class PlayerController : ICameraModifier
{
[Property, FeatureEnabled( "Camera", Icon = "videocam", Description = "Built-in camera controls. Remove this to control the Camera yourself." )]
public bool UseCameraControls { get; set; } = true;
[Property, Feature( "Camera" )] public float EyeDistanceFromTop { get; set; } = 8;
[Property, Feature( "Camera" )] public bool ThirdPerson { get; set; } = true;
[Property, Feature( "Camera" )] public bool HideBodyInFirstPerson { get; set; } = true;
[Property, Feature( "Camera" )] public bool UseFovFromPreferences { get; set; } = true;
[Property, Feature( "Camera" )] public Vector3 CameraOffset { get; set; } = new Vector3( 256, 0, 12 );
[Property, Feature( "Camera" ), InputAction] public string ToggleCameraModeButton { get; set; } = "view";
float _cameraDistance = 100;
float _eyez;
// Input is read in the update loop - the view itself is composed in the camera's modifier chain.
void UpdateCameraInput()
{
if ( !UseCameraControls ) return;
if ( !string.IsNullOrWhiteSpace( ToggleCameraModeButton ) && Input.Pressed( ToggleCameraModeButton ) )
{
ThirdPerson = !ThirdPerson;
_cameraDistance = 20;
}
}
int ICameraModifier.CameraOrder => 0;
/// <summary>
/// Stage one of the camera pipeline - writes the player's eye view (and the third person boom)
/// into the base view. The obsolete <see cref="IEvents.PostCameraSetup"/> hook still runs against
/// the camera component afterwards, its changes folded back into the view.
/// </summary>
void ICameraModifier.ModifyCamera( CameraComponent cam, ref CameraView view )
{
if ( !UseCameraControls || IsProxy ) return;
if ( Scene.Camera != cam ) return;
UpdateEyeTransform();
var rot = EyeTransform.Rotation;
var eyePosition = EyeTransform.Position;
if ( !IsAirborne && _eyez != 0 )
eyePosition.z = _eyez.LerpTo( eyePosition.z, Time.Delta * 50 );
_eyez = eyePosition.z;
if ( !cam.RenderExcludeTags.Contains( "viewer" ) )
{
cam.RenderExcludeTags.Add( "viewer" );
}
if ( ThirdPerson )
{
var cameraDelta = rot.Forward * -CameraOffset.x + rot.Up * CameraOffset.z + rot.Right * CameraOffset.y;
// clip the camera
var tr = Scene.Trace.FromTo( eyePosition, eyePosition + cameraDelta )
.IgnoreGameObjectHierarchy( GameObject.Root )
.Radius( 8 )
.Run();
// smooth the zoom in and out
if ( tr.StartedSolid )
{
_cameraDistance = _cameraDistance.LerpTo( cameraDelta.Length, Time.Delta * 100.0f );
}
else if ( tr.Distance < _cameraDistance )
{
_cameraDistance = _cameraDistance.LerpTo( tr.Distance, Time.Delta * 200.0f );
}
else
{
_cameraDistance = _cameraDistance.LerpTo( tr.Distance, Time.Delta * 2.0f );
}
eyePosition = eyePosition + cameraDelta.Normal * _cameraDistance;
}
view.Position = eyePosition;
view.Rotation = rot;
if ( UseFovFromPreferences )
view.FieldOfView = Preferences.FieldOfView;
// The active move mode reshapes the view - it doesn't join the modifier chain itself.
Mode?.ModifyCamera( ref view );
// Legacy bridge - PostCameraSetup mutates the camera component directly. Write the view out,
// let it run, fold its changes back into the view.
cam.WorldPosition = view.Position;
cam.WorldRotation = view.Rotation;
cam.FieldOfView = view.FieldOfView;
#pragma warning disable CS0618
IEvents.PostToGameObject( GameObject, x => x.PostCameraSetup( cam ) );
#pragma warning restore CS0618
view.Position = cam.WorldPosition;
view.Rotation = cam.WorldRotation;
view.FieldOfView = cam.FieldOfView;
}
}