BMAD-METHOD/src/modules/bmgd/gametest/knowledge/input-testing.md

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