Files
sbox-public/engine/Sandbox.Engine/Systems/Input/Controller/Input.Controller.cs
s&box team 71f266059a Open source release
This commit imports the C# engine code and game files, excluding C++ source code.

[Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
2025-11-24 09:05:18 +00:00

126 lines
3.3 KiB
C#

using Sandbox.Utility;
namespace Sandbox;
public static partial class Input
{
/// <summary>
/// What's our current player index (for input scoping)?
/// -1 is the default behavior, where it'll accept keyboard AND gamepad inputs.
/// Anything above that, is targeting a specific controller.
/// </summary>
internal static int CurrentPlayerScope { get; private set; } = -1;
/// <summary>
/// How many controllers are active right now?
/// </summary>
public static int ControllerCount => Controller.All.Count();
/// <summary>
/// Whether or not the Virtual Cursor should show when using a controller. Disable this to control the cursor manually.
/// </summary>
public static bool EnableVirtualCursor { get; set; } = true;
/// <summary>
/// Tries to find the current controller to use.
/// </summary>
internal static Controller CurrentController
{
get
{
// Fallback if we're not using any input scoping
if ( CurrentPlayerScope == -1 ) return Controller.First;
// Out of range?
if ( CurrentPlayerScope >= Controller.All.Count() )
{
return null;
}
if ( Controller.All.ElementAt( CurrentPlayerScope ) is { } controller )
{
return controller;
}
return null;
}
}
/// <summary>
/// An analog input, when fetched, is between -1 and 1 (0 being default)
/// </summary>
public static float GetAnalog( InputAnalog analog )
{
if ( Suppressed ) return default;
if ( Input.CurrentController is { } controller && UsingController )
{
return controller.GetAxis( analog.ToAxis() );
}
return 0f;
}
/// <summary>
/// Processes controller inputs based on a player index (for input scoping). This can be called many times a frame.
/// </summary>
private static void ProcessControllerInput( int playerIndex = -1 )
{
CurrentPlayerScope = playerIndex;
// Default input accepts gamepad and keyboard and mouse, so we don't want to reset analogs
if ( CurrentPlayerScope != -1 )
{
// Reset analogs
AnalogLook = default;
AnalogMove = 0;
}
if ( Input.CurrentController is { } controller && UsingController )
{
// Use controller's input context
// Doesn't need to be flipped, we do this once a frame for each controller.
controller.InputContext?.Push();
var lookX = controller.GetAxis( NativeEngine.GameControllerAxis.RightX ) * Time.Delta * Preferences.ControllerLookYawSpeed;
var lookY = controller.GetAxis( NativeEngine.GameControllerAxis.RightY ) * Time.Delta * Preferences.ControllerLookPitchSpeed;
AnalogLook = new Angles( lookY, -lookX, 0 );
var moveX = controller.GetAxis( NativeEngine.GameControllerAxis.LeftX );
var moveY = controller.GetAxis( NativeEngine.GameControllerAxis.LeftY );
AnalogMove = new Vector3( -moveY, -moveX, 0 );
MotionData = new()
{
Gyroscope = controller.Gyroscope,
Accelerometer = controller.Accelerometer
};
controller.UpdateHaptics();
}
}
/// <summary>
/// Push a specific player scope to be active
/// </summary>
public static IDisposable PlayerScope( int index )
{
index = index.Clamp( 0, int.MaxValue );
var old = CurrentPlayerScope;
// Process input for our new scope
ProcessControllerInput( index );
return DisposeAction.Create( () =>
{
if ( CurrentPlayerScope == index )
{
ProcessControllerInput( old );
}
} );
}
}