LocalCameraControl.cs
using Sandbox;
using Sandbox.Diagnostics;
/// <summary>
/// Controls the camera that can be found in the local hierarchy rather than the global scene camera.
/// </summary>
[Icon( "videocam" )]
public sealed class LocalCameraControl : Component, IPlayerHealthEvent
{
private CameraComponent cam;
private PlayerController playerController;
private float _cameraDistance = 100f;
private float _eyez;
private string _uniqueRenderExcludeTag;
[Property]
public bool UseCameraControls { get; set; } = true;
[Property]
public float EyeDistanceFromTop { get; set; } = 8f;
[Property]
public bool ThirdPerson { get; set; } = true;
[Property]
public bool HideBodyInFirstPerson { get; set; } = true;
[Property]
public bool RotateWithGround { get; set; } = true;
[Property]
public bool UseFovFromPreferences { get; set; } = true;
[Property]
public Vector3 CameraOffset { get; set; } = new Vector3( 256f, 0f, 12f );
[Property]
[InputAction]
public string ToggleCameraModeButton { get; set; } = "view";
public string ExcludeRenderTag => _uniqueRenderExcludeTag;
protected override void OnEnabled()
{
base.OnEnabled();
playerController = GetComponent<PlayerController>();
Assert.IsValid( playerController );
cam = GetComponentInChildren<CameraComponent>();
Assert.IsValid( cam );
_uniqueRenderExcludeTag = "viewer_" + GameObject.Id.ToString().Truncate( 4 );
}
public bool FollowBone = false;
protected override void OnUpdate()
{
if ( !FollowBone )
{
UpdateCameraPosition();
UpdateBodyVisibility();
}
}
private void UpdateCameraPosition()
{
if ( !UseCameraControls )
{
return;
}
if ( !string.IsNullOrWhiteSpace( ToggleCameraModeButton ) && Input.Pressed( ToggleCameraModeButton ) )
{
ThirdPerson = !ThirdPerson;
_cameraDistance = 20f;
}
Rotation worldRotation = playerController.EyeAngles.ToRotation();
cam.WorldRotation = worldRotation;
Vector3 from = base.WorldPosition + Vector3.Up * (playerController.BodyHeight - EyeDistanceFromTop);
if ( playerController.IsOnGround && _eyez != 0f )
{
from.z = _eyez.LerpTo( from.z, Time.Delta * 50f );
}
_eyez = from.z;
if ( !cam.RenderExcludeTags.Contains( _uniqueRenderExcludeTag ) )
{
cam.RenderExcludeTags.Add( _uniqueRenderExcludeTag );
}
if ( ThirdPerson )
{
Vector3 vector = worldRotation.Forward * (0f - CameraOffset.x) + worldRotation.Up * CameraOffset.z + worldRotation.Right * CameraOffset.y;
SceneTrace trace = base.Scene.Trace;
Vector3 to = from + vector;
SceneTraceResult sceneTraceResult = trace.FromTo( in from, in to ).IgnoreGameObjectHierarchy( base.GameObject ).Radius( 8f )
.Run();
if ( sceneTraceResult.StartedSolid )
{
_cameraDistance = _cameraDistance.LerpTo( vector.Length, Time.Delta * 100f );
}
else if ( sceneTraceResult.Distance < _cameraDistance )
{
_cameraDistance = _cameraDistance.LerpTo( sceneTraceResult.Distance, Time.Delta * 200f );
}
else
{
_cameraDistance = _cameraDistance.LerpTo( sceneTraceResult.Distance, Time.Delta * 2f );
}
from += vector.Normal * _cameraDistance;
}
cam.WorldPosition = from;
if ( UseFovFromPreferences )
{
cam.FieldOfView = Preferences.FieldOfView;
}
ISceneEvent<PlayerController.IEvents>.PostToGameObject( base.GameObject, delegate ( PlayerController.IEvents x )
{
x.PostCameraSetup( cam );
} );
}
private void UpdateBodyVisibility()
{
if ( UseCameraControls && cam != null )
{
bool state = !ThirdPerson && HideBodyInFirstPerson && !base.IsProxy;
if ( !base.IsProxy && _cameraDistance < 20f )
{
state = true;
}
if ( base.IsProxy )
{
state = false;
}
GameObject gameObject = playerController.Renderer?.GameObject ?? base.GameObject;
if ( gameObject.IsValid() )
{
gameObject.Tags.Set( _uniqueRenderExcludeTag, state );
}
}
}
void IPlayerHealthEvent.OnDied( GameObject player )
{
if ( player != GameObject ) return;
FollowBone = true;
cam.GameObject.SetParent( GetComponentInChildren<ModelRenderer>().GetAttachmentObject( "eyes" ), false );
cam.GameObject.LocalPosition = 0;
cam.GameObject.LocalRotation = Rotation.Identity;
TurnOffCameraOnDeath( player );
}
async void TurnOffCameraOnDeath( GameObject player )
{
await Task.DelayRealtimeSeconds( 2.0f );
player.Enabled = false;
Scene.GetComponentInChildren<PlayerReplicator>().AddViewport( cam.Viewport );
IPlayerHealthEvent.Post( x => x.OnPostDeath( player ) );
cam.DestroyGameObject();
player.Destroy();
}
}