316 lines
8.9 KiB
Markdown
316 lines
8.9 KiB
Markdown
# Input Testing Guide
|
|
|
|
## Overview
|
|
|
|
Input testing validates that all supported input devices work correctly across platforms. Poor input handling frustrates players instantly—responsive, accurate input is foundational to game feel.
|
|
|
|
## Input Categories
|
|
|
|
### Device Types
|
|
|
|
| Device | Platforms | Key Concerns |
|
|
| ----------------- | -------------- | ----------------------------------- |
|
|
| Keyboard + Mouse | PC | Key conflicts, DPI sensitivity |
|
|
| Gamepad (Xbox/PS) | PC, Console | Deadzone, vibration, button prompts |
|
|
| Touch | Mobile, Switch | Multi-touch, gesture recognition |
|
|
| Motion Controls | Switch, VR | Calibration, drift, fatigue |
|
|
| Specialty | Various | Flight sticks, wheels, fight sticks |
|
|
|
|
### Input Characteristics
|
|
|
|
| Characteristic | Description | Test Focus |
|
|
| -------------- | ---------------------------- | -------------------------------- |
|
|
| Responsiveness | Input-to-action delay | Should feel instant (< 100ms) |
|
|
| Accuracy | Input maps to correct action | No ghost inputs or missed inputs |
|
|
| Consistency | Same input = same result | Deterministic behavior |
|
|
| Accessibility | Alternative input support | Remapping, assist options |
|
|
|
|
## Test Scenarios
|
|
|
|
### Keyboard and Mouse
|
|
|
|
```
|
|
SCENARIO: All Keybinds Functional
|
|
GIVEN default keyboard bindings
|
|
WHEN each bound key is pressed
|
|
THEN corresponding action triggers
|
|
AND no key conflicts exist
|
|
|
|
SCENARIO: Key Remapping
|
|
GIVEN player remaps "Jump" from Space to F
|
|
WHEN F is pressed
|
|
THEN jump action triggers
|
|
AND Space no longer triggers jump
|
|
AND remapping persists after restart
|
|
|
|
SCENARIO: Mouse Sensitivity
|
|
GIVEN sensitivity set to 5 (mid-range)
|
|
WHEN mouse moves 10cm
|
|
THEN camera rotation matches expected degrees
|
|
AND movement feels consistent at different frame rates
|
|
|
|
SCENARIO: Mouse Button Support
|
|
GIVEN mouse with 5+ buttons
|
|
WHEN side buttons are pressed
|
|
THEN they can be bound to actions
|
|
AND they function correctly in gameplay
|
|
```
|
|
|
|
### Gamepad
|
|
|
|
```
|
|
SCENARIO: Analog Stick Deadzone
|
|
GIVEN controller with slight stick drift
|
|
WHEN stick is in neutral position
|
|
THEN no movement occurs (deadzone filters drift)
|
|
AND intentional small movements still register
|
|
|
|
SCENARIO: Trigger Pressure
|
|
GIVEN analog triggers
|
|
WHEN trigger is partially pressed
|
|
THEN partial values are read (e.g., 0.5 for half-press)
|
|
AND full press reaches 1.0
|
|
|
|
SCENARIO: Controller Hot-Swap
|
|
GIVEN game running with keyboard
|
|
WHEN gamepad is connected
|
|
THEN input prompts switch to gamepad icons
|
|
AND gamepad input works immediately
|
|
AND keyboard still works if used
|
|
|
|
SCENARIO: Vibration Feedback
|
|
GIVEN rumble-enabled controller
|
|
WHEN damage is taken
|
|
THEN controller vibrates appropriately
|
|
AND vibration intensity matches damage severity
|
|
```
|
|
|
|
### Touch Input
|
|
|
|
```
|
|
SCENARIO: Multi-Touch Accuracy
|
|
GIVEN virtual joystick and buttons
|
|
WHEN left thumb on joystick AND right thumb on button
|
|
THEN both inputs register simultaneously
|
|
AND no interference between touch points
|
|
|
|
SCENARIO: Gesture Recognition
|
|
GIVEN swipe-to-attack mechanic
|
|
WHEN player swipes right
|
|
THEN attack direction matches swipe
|
|
AND swipe is distinguished from tap
|
|
|
|
SCENARIO: Touch Target Size
|
|
GIVEN minimum touch target of 44x44 points
|
|
WHEN buttons are placed
|
|
THEN all interactive elements meet minimum size
|
|
AND elements have adequate spacing
|
|
```
|
|
|
|
## Platform-Specific Testing
|
|
|
|
### PC
|
|
|
|
- Multiple keyboard layouts (QWERTY, AZERTY, QWERTZ)
|
|
- Different mouse DPI settings (400-3200+)
|
|
- Multiple monitors (cursor confinement)
|
|
- Background application conflicts
|
|
- Steam Input API integration
|
|
|
|
### Console
|
|
|
|
| Platform | Specific Tests |
|
|
| ----------- | ------------------------------------------ |
|
|
| PlayStation | Touchpad, adaptive triggers, haptics |
|
|
| Xbox | Impulse triggers, Elite controller paddles |
|
|
| Switch | Joy-Con detachment, gyro, HD rumble |
|
|
|
|
### Mobile
|
|
|
|
- Different screen sizes and aspect ratios
|
|
- Notch/cutout avoidance
|
|
- External controller support
|
|
- Apple MFi / Android gamepad compatibility
|
|
|
|
## Automated Test Examples
|
|
|
|
### Unity
|
|
|
|
```csharp
|
|
using UnityEngine.InputSystem;
|
|
|
|
[UnityTest]
|
|
public IEnumerator Movement_WithGamepad_RespondsToStick()
|
|
{
|
|
var gamepad = InputSystem.AddDevice<Gamepad>();
|
|
|
|
yield return null;
|
|
|
|
// Simulate stick input
|
|
Set(gamepad.leftStick, new Vector2(1, 0));
|
|
yield return new WaitForSeconds(0.1f);
|
|
|
|
Assert.Greater(player.transform.position.x, 0f,
|
|
"Player should move right");
|
|
|
|
InputSystem.RemoveDevice(gamepad);
|
|
}
|
|
|
|
[UnityTest]
|
|
public IEnumerator InputLatency_UnderLoad_StaysAcceptable()
|
|
{
|
|
float inputTime = Time.realtimeSinceStartup;
|
|
bool actionTriggered = false;
|
|
|
|
player.OnJump += () => {
|
|
float latency = (Time.realtimeSinceStartup - inputTime) * 1000;
|
|
Assert.Less(latency, 100f, "Input latency should be under 100ms");
|
|
actionTriggered = true;
|
|
};
|
|
|
|
var keyboard = InputSystem.AddDevice<Keyboard>();
|
|
Press(keyboard.spaceKey);
|
|
|
|
yield return new WaitForSeconds(0.2f);
|
|
|
|
Assert.IsTrue(actionTriggered, "Jump should have triggered");
|
|
}
|
|
|
|
[Test]
|
|
public void Deadzone_FiltersSmallInputs()
|
|
{
|
|
var settings = new InputSettings { stickDeadzone = 0.2f };
|
|
|
|
// Input below deadzone
|
|
var filtered = InputProcessor.ApplyDeadzone(new Vector2(0.1f, 0.1f), settings);
|
|
Assert.AreEqual(Vector2.zero, filtered);
|
|
|
|
// Input above deadzone
|
|
filtered = InputProcessor.ApplyDeadzone(new Vector2(0.5f, 0.5f), settings);
|
|
Assert.AreNotEqual(Vector2.zero, filtered);
|
|
}
|
|
```
|
|
|
|
### Unreal
|
|
|
|
```cpp
|
|
bool FInputTest::RunTest(const FString& Parameters)
|
|
{
|
|
// Test gamepad input mapping
|
|
APlayerController* PC = GetWorld()->GetFirstPlayerController();
|
|
|
|
// Simulate gamepad stick input
|
|
FInputKeyParams Params;
|
|
Params.Key = EKeys::Gamepad_LeftX;
|
|
Params.Delta = FVector(1.0f, 0, 0);
|
|
PC->InputKey(Params);
|
|
|
|
// Verify movement
|
|
APawn* Pawn = PC->GetPawn();
|
|
FVector Velocity = Pawn->GetVelocity();
|
|
|
|
TestTrue("Pawn should be moving", Velocity.SizeSquared() > 0);
|
|
|
|
return true;
|
|
}
|
|
```
|
|
|
|
### Godot
|
|
|
|
```gdscript
|
|
func test_input_action_mapping():
|
|
# Verify action exists
|
|
assert_true(InputMap.has_action("jump"))
|
|
|
|
# Simulate input
|
|
var event = InputEventKey.new()
|
|
event.keycode = KEY_SPACE
|
|
event.pressed = true
|
|
|
|
Input.parse_input_event(event)
|
|
await get_tree().process_frame
|
|
|
|
assert_true(Input.is_action_just_pressed("jump"))
|
|
|
|
func test_gamepad_deadzone():
|
|
var input = Vector2(0.15, 0.1)
|
|
var deadzone = 0.2
|
|
|
|
var processed = input_processor.apply_deadzone(input, deadzone)
|
|
|
|
assert_eq(processed, Vector2.ZERO, "Small input should be filtered")
|
|
|
|
func test_controller_hotswap():
|
|
# Simulate controller connect
|
|
Input.joy_connection_changed(0, true)
|
|
await get_tree().process_frame
|
|
|
|
var prompt_icon = ui.get_action_prompt("jump")
|
|
|
|
assert_true(prompt_icon.texture.resource_path.contains("gamepad"),
|
|
"Should show gamepad prompts after controller connect")
|
|
```
|
|
|
|
## Accessibility Testing
|
|
|
|
### Requirements Checklist
|
|
|
|
- [ ] Full keyboard navigation (no mouse required)
|
|
- [ ] Remappable controls for all actions
|
|
- [ ] Button hold alternatives to rapid press
|
|
- [ ] Toggle options for hold actions
|
|
- [ ] One-handed control schemes
|
|
- [ ] Colorblind-friendly UI indicators
|
|
- [ ] Screen reader support for menus
|
|
|
|
### Accessibility Test Scenarios
|
|
|
|
```
|
|
SCENARIO: Keyboard-Only Navigation
|
|
GIVEN mouse is disconnected
|
|
WHEN navigating through all menus
|
|
THEN all menu items are reachable via keyboard
|
|
AND focus indicators are clearly visible
|
|
|
|
SCENARIO: Button Hold Toggle
|
|
GIVEN "sprint requires hold" is toggled OFF
|
|
WHEN sprint button is tapped once
|
|
THEN sprint activates
|
|
AND sprint stays active until tapped again
|
|
|
|
SCENARIO: Reduced Button Mashing
|
|
GIVEN QTE assist mode enabled
|
|
WHEN QTE sequence appears
|
|
THEN single press advances sequence
|
|
AND no rapid input required
|
|
```
|
|
|
|
## Performance Metrics
|
|
|
|
| Metric | Target | Maximum Acceptable |
|
|
| ----------------------- | --------------- | ------------------ |
|
|
| Input-to-render latency | < 50ms | 100ms |
|
|
| Polling rate match | 1:1 with device | No input loss |
|
|
| Deadzone processing | < 1ms | 5ms |
|
|
| Rebind save/load | < 100ms | 500ms |
|
|
|
|
## Best Practices
|
|
|
|
### DO
|
|
|
|
- Test with actual hardware, not just simulated input
|
|
- Support simultaneous keyboard + gamepad
|
|
- Provide sensible default deadzones
|
|
- Show device-appropriate button prompts
|
|
- Allow complete control remapping
|
|
- Test at different frame rates
|
|
|
|
### DON'T
|
|
|
|
- Assume controller layout (Xbox vs PlayStation)
|
|
- Hard-code input mappings
|
|
- Ignore analog input precision
|
|
- Skip accessibility considerations
|
|
- Forget about input during loading/cutscenes
|
|
- Neglect testing with worn/drifting controllers
|