The Input System in the Untold Engine allows you to detect user inputs, such as keystrokes and mouse movements, to control entities and interact with the game. This guide will explain how to use the Input System effectively.
To detect if a specific key is pressed, use the keyState object from the Input System.
Example: Detecting the 'W' Key
func init(){
// Make sure that you have enabled keyevents in your init function:
InputSystem.shared.registerKeyboardEvents()
}
// Then in the handleInput callback, you can do this:
func handleInput() {
// Skip logic if not in game mode
if gameMode == false { return }
let inputSystem = InputSystem.shared
// Handle input here
if inputSystem.keyState.wPressed{
Logger.log(message: "w pressed")
}
}You can use the same logic for other keys like A, S, and D:
let inputSystem = InputSystem.shared
if inputSystem.keyState.aPressed == true {
// Move left
}
if inputSystem.keyState.sPressed == true {
// Move backward
}
if inputSystem.keyState.dPressed == true {
// Move right
}###Step 2: Using Input to Control Entities
Here's an example function that moves a car entity based on keyboard inputs:
func moveCar(entityId: EntityID, dt: Float) {
let inputSystem = InputSystem.shared
// Ensure we are in game mode
if gameMode == false {
return
}
var position = simd_float3(0.0, 0.0, 0.0)
// Move forward
if inputSystem.keyState.wPressed == true {
position.z += 1.0 * dt
}
// Move backward
if inputSystem.keyState.sPressed == true {
position.z -= 1.0 * dt
}
// Move left
if inputSystem.keyState.aPressed == true {
position.x -= 1.0 * dt
}
// Move right
if inputSystem.keyState.dPressed == true {
position.x += 1.0 * dt
}
// Apply the translation to the entity
translateTo(entityId: entityId, position: position)
}To detect if a specific button is pressed, use the gameControllerState object from the Input System.
Example: Detecting the 'A' button
func init(){
// Make sure that you have enabled game controller events in your init function:
InputSystem.shared.registerGameControllerEvents()
}
// Then in the handleInput callback, you can do this:
func handleInput() {
// Skip logic if not in game mode
if gameMode == false { return }
let inputSystem = InputSystem.shared
// Handle input here
if inputSystem.gameControllerState.aPressed {
Logger.log(message: "Pressed A key")
}
}When developing for visionOS, use the setInput facade and free functions to configure XR input without touching the shared singleton directly.
Before any spatial input is received, register the XR event pipeline in your init:
func gameInit() {
registerXREvents()
}Call unregisterXREvents() to stop receiving spatial events when leaving XR mode.
// Choose the spatial picking backend
setInput(.xr(.pickingBackend(.octreeGPUPreferred)))
// Set how the two-hand rotate axis is derived
setInput(.xr(.twoHandRotateAxisMode(.dynamicSnapped)))
// Signal that the XR scene is ready to receive input
setInput(.xr(.sceneReady(true)))Available two-hand rotate axis modes:
.cameraForward— rotates around the camera-forward axis (screen-style twist).dynamic— derives the axis from actual two-hand motion.dynamicSnapped— dynamic axis snapped to the dominant world axis (x,y, orz)
func handleInput() {
let state = getXRSpatialInputState()
if state.spatialTapActive, let entityId = state.pickedEntityId {
Logger.log(message: "Tapped entity: \(entityId)")
}
}let ready = isXRSceneReady()- Debouncing: If you want to execute an action only once per key press, track the key's previous state to avoid repeated triggers.
- Game Mode Check: Always ensure the game is in the appropriate mode (e.g., Game Mode) before processing inputs.
- Smooth Movement: Use dt (delta time) to ensure frame-rate-independent movement.