233 lines
7.7 KiB
TypeScript
233 lines
7.7 KiB
TypeScript
// Input/Components/AC_InputDevice.ts
|
|
|
|
import type { AC_DebugHUD } from '#root/Debug/Components/AC_DebugHUD.ts';
|
|
import type { AC_ToastSystem } from '#root/Toasts/Components/AC_ToastSystem.ts';
|
|
import { ActorComponent } from '#root/UE/ActorComponent.ts';
|
|
import { EHardwareDevicePrimaryType } from '#root/UE/EHardwareDevicePrimaryType.ts';
|
|
import type { Float } from '#root/UE/Float.ts';
|
|
import { InputDeviceSubsystem } from '#root/UE/InputDeviceSubsystem.ts';
|
|
import type { Integer } from '#root/UE/Integer.ts';
|
|
import { SystemLibrary } from '#root/UE/SystemLibrary.ts';
|
|
import { E_MessageType } from '#root/UI/Enums/E_MessageType.ts';
|
|
|
|
/**
|
|
* Input Device Detection Component
|
|
* Minimal wrapper around Unreal Engine's native InputDeviceSubsystem
|
|
* Provides simple binary classification: Gamepad vs Everything Else
|
|
*/
|
|
export class AC_InputDevice extends ActorComponent {
|
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
|
// FUNCTIONS
|
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
|
|
|
/**
|
|
* Get current active input device type
|
|
* @returns Current device type (cached from delegate events)
|
|
* @category Device Queries
|
|
* @pure true
|
|
*/
|
|
public GetCurrentInputDevice(): EHardwareDevicePrimaryType {
|
|
return this.CurrentDevice;
|
|
}
|
|
|
|
/**
|
|
* Check if current device is keyboard/mouse
|
|
* @returns True if keyboard is active
|
|
* @category Device Queries
|
|
* @pure true
|
|
*/
|
|
public IsKeyboard(): boolean {
|
|
return this.CurrentDevice === EHardwareDevicePrimaryType.KeyboardAndMouse;
|
|
}
|
|
|
|
/**
|
|
* Check if current device is gamepad/controller
|
|
* @returns True if gamepad is active
|
|
* @category Device Queries
|
|
* @pure true
|
|
*/
|
|
public IsGamepad(): boolean {
|
|
return this.CurrentDevice === EHardwareDevicePrimaryType.Gamepad;
|
|
}
|
|
|
|
/**
|
|
* Initialize device detection system with delegate registration
|
|
* @param ToastComponentRef - Toast system for debug notifications
|
|
* @param DebugHUDComponentRef - Optional debug HUD for displaying device info
|
|
* @category System Setup
|
|
*/
|
|
public InitializeDeviceDetection(
|
|
ToastComponentRef: AC_ToastSystem,
|
|
DebugHUDComponentRef: AC_DebugHUD
|
|
): void {
|
|
this.ToastComponent = ToastComponentRef;
|
|
this.DebugHUDComponent = DebugHUDComponentRef;
|
|
this.RegisterHardwareDeviceDelegate();
|
|
this.DetectInitialDevice();
|
|
this.IsInitialized = true;
|
|
|
|
if (SystemLibrary.IsValid(this.DebugHUDComponent)) {
|
|
this.DebugHUDComponent.AddDebugPage(
|
|
this.DebugPageID,
|
|
'Input Device Info',
|
|
60
|
|
);
|
|
}
|
|
|
|
if (SystemLibrary.IsValid(this.ToastComponent)) {
|
|
this.ToastComponent.ShowToast(
|
|
`Device Detection Initialized: ${this.CurrentDevice}`,
|
|
E_MessageType.Success
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Register OnInputHardwareDeviceChanged delegate
|
|
* Core of the event-driven approach
|
|
* @category System Setup
|
|
*/
|
|
private RegisterHardwareDeviceDelegate(): void {
|
|
InputDeviceSubsystem.OnInputHardwareDeviceChanged.BindEvent(
|
|
this.OnInputHardwareDeviceChanged.bind(this)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Detect initial device on system startup
|
|
* Fallback for getting device before first hardware change event
|
|
* @category System Setup
|
|
*/
|
|
private DetectInitialDevice(): void {
|
|
this.CurrentDevice =
|
|
InputDeviceSubsystem.GetMostRecentlyUsedHardwareDevice().PrimaryDeviceType;
|
|
}
|
|
|
|
/**
|
|
* Handle hardware device change events
|
|
* Called automatically when input hardware changes
|
|
* @param UserId - User ID (usually 0 for single-player)
|
|
* @param DeviceId - Device name string
|
|
* @category Event Handling
|
|
*/
|
|
private OnInputHardwareDeviceChanged(
|
|
UserId?: Integer,
|
|
DeviceId?: Integer
|
|
): void {
|
|
this.ProcessDeviceChange(
|
|
InputDeviceSubsystem.GetInputDeviceHardwareIdentifier(DeviceId ?? 0)
|
|
.PrimaryDeviceType
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Process device change with debouncing
|
|
* Prevents rapid switching and manages device state
|
|
* @param NewDevice - Newly detected device type
|
|
* @category Device Management
|
|
*/
|
|
private ProcessDeviceChange(NewDevice: EHardwareDevicePrimaryType): void {
|
|
if (NewDevice !== this.CurrentDevice && this.CanProcessDeviceChange()) {
|
|
this.CurrentDevice = NewDevice;
|
|
this.LastDeviceChangeTime = SystemLibrary.GetGameTimeInSeconds();
|
|
|
|
if (SystemLibrary.IsValid(this.ToastComponent)) {
|
|
this.ToastComponent.ShowToast(
|
|
`Input switched to ${NewDevice}`,
|
|
E_MessageType.Info
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if device change can be processed (debouncing)
|
|
* Prevents rapid device switching within cooldown period
|
|
* @returns True if enough time has passed since last change
|
|
* @category Debouncing
|
|
* @pure true
|
|
*/
|
|
private CanProcessDeviceChange(): boolean {
|
|
const HasCooldownExpired = (): boolean =>
|
|
SystemLibrary.GetGameTimeInSeconds() - this.LastDeviceChangeTime >=
|
|
this.DeviceChangeCooldown;
|
|
|
|
return HasCooldownExpired();
|
|
}
|
|
|
|
/**
|
|
* Update debug HUD with current device info
|
|
* @category Debug
|
|
*/
|
|
public UpdateDebugPage(): void {
|
|
if (SystemLibrary.IsValid(this.DebugHUDComponent)) {
|
|
if (
|
|
this.DebugHUDComponent.ShouldUpdatePage(
|
|
this.DebugPageID,
|
|
SystemLibrary.GetGameTimeInSeconds()
|
|
)
|
|
) {
|
|
this.DebugHUDComponent.UpdatePageContent(
|
|
this.DebugPageID,
|
|
`Hardware Device Identifier: ${this.GetCurrentInputDevice()}` +
|
|
`Initialized: ${this.IsInitialized}` +
|
|
`Last Change: ${this.LastDeviceChangeTime}`
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
|
// VARIABLES
|
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
|
|
|
/**
|
|
* Reference to toast system for debug notifications
|
|
* @category Components
|
|
*/
|
|
private ToastComponent: AC_ToastSystem | null = null;
|
|
|
|
/**
|
|
* Reference to debug HUD component for displaying camera info
|
|
* Optional, used for debugging purposes
|
|
* @category Components
|
|
*/
|
|
public DebugHUDComponent: AC_DebugHUD | null = null;
|
|
|
|
/**
|
|
* Current active input device type
|
|
* Updated by hardware device change events
|
|
* @category Device State
|
|
*/
|
|
private CurrentDevice: EHardwareDevicePrimaryType =
|
|
EHardwareDevicePrimaryType.Unspecified;
|
|
|
|
/**
|
|
* System initialization status
|
|
* @category System State
|
|
*/
|
|
public IsInitialized: boolean = false;
|
|
|
|
/**
|
|
* Last device change timestamp for debouncing
|
|
* @category Debouncing
|
|
*/
|
|
public LastDeviceChangeTime: Float = 0;
|
|
|
|
/**
|
|
* Minimum time between device changes (prevents flickering)
|
|
* Recommended: 300-500ms for most games
|
|
* @category Debouncing
|
|
* @instanceEditable true
|
|
*/
|
|
private DeviceChangeCooldown: Float = 0.3;
|
|
|
|
/**
|
|
* Debug page identifier for organizing debug output
|
|
* Used by debug HUD to categorize information
|
|
* @category Debug
|
|
* @instanceEditable true
|
|
*/
|
|
public readonly DebugPageID: string = 'InputDeviceInfo';
|
|
}
|