Agent skill
ta-input-validation
Player input validation testing patterns for WASD, mouse, and touch controls. Use when testing input systems, validating controls.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/data/ta-input-validation
SKILL.md
Input Validation Pattern Skill
"Test input systems early – inverted controls make games unplayable."
When to Use This Skill
Use whenever implementing or modifying player input systems (WASD, mouse, touch, gamepad).
Quick Start
# After input changes, always verify:
npm run type-check && npm run lint
# Manual testing checklist:
# 1. W = forward, S = backward, A = left, D = right
# 2. Diagonal movement (W+A, W+D, S+A, S+D)
# 3. Movement relative to camera direction
The Input Validation Framework
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Implementation │───▶│ Unit Tests │───▶│ Manual Verify │
│ (WASD/Mouse) │ │ (getInputDir) │ │ (Play test) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
▼ ▼ ▼
Type-safe code Prevent regression User experience
Critical Input Tests
WASD Movement (P0 - Game Breaking)
Test Cases:
| Input | Expected Output | Bug Risk |
|---|---|---|
| W key | Move forward (away from camera) | HIGH |
| S key | Move backward (toward camera) | HIGH |
| A key | Move left (screen left) | HIGH |
| D key | Move right (screen right) | HIGH |
| W+A | Move forward-left (diagonal) | MEDIUM |
| W+D | Move forward-right (diagonal) | MEDIUM |
| S+A | Move backward-left (diagonal) | MEDIUM |
| S+D | Move backward-right (diagonal) | MEDIUM |
Camera-Relative Movement
Movement MUST be relative to camera direction, not world axes:
// ❌ WRONG: World-space movement (classic bug)
const direction = new Vector3(x, 0, z);
// ✅ CORRECT: Camera-relative movement
const forward = new Vector3();
camera.getWorldDirection(forward);
forward.y = 0;
forward.normalize();
const right = new Vector3();
right.crossVectors(forward, new Vector3(0, 1, 0));
const direction = new Vector3();
direction.addScaledVector(forward, -z); // W/S
direction.addScaledVector(right, x); // A/D
Mouse Look Validation
| Test Case | Expected Behavior |
|---|---|
| Mouse left | Camera rotates left |
| Mouse right | Camera rotates right |
| Mouse up | Camera looks up (clamped) |
| Mouse down | Camera looks down (clamped) |
| Sensitivity | ~180°/second rotation speed |
Progressive Guide
Level 1: Implementation Checklist
When implementing input systems:
// ✅ DO: Create input helper with clear types
interface InputState {
forward: boolean;
backward: boolean;
left: boolean;
right: boolean;
jump: boolean;
sprint: boolean;
crouch: boolean;
}
function getInputDirection(input: InputState, camera: Camera3D): Vector3 {
// Implementation...
}
// ❌ DON'T: Use magic numbers or implicit axes
const dir = new Vector3(
keys['KeyD'] ? 1 : keys['KeyA'] ? -1 : 0, // Wrong! Non-obvious
0,
keys['KeyS'] ? 1 : keys['KeyW'] ? -1 : 0 // Wrong! Inverted
);
Level 2: Unit Testing
Create unit tests for input functions:
// tests/unit/input.test.ts
import { describe, it, expect } from 'vitest';
import { getInputDirection } from '../PlayerController';
describe('getInputDirection', () => {
it('returns forward vector for W key', () => {
const input = { forward: true, backward: false, left: false, right: false };
const result = getInputDirection(input, mockCamera);
expect(result.z).toBeCloseTo(-1); // Forward is negative Z
});
it('returns backward vector for S key', () => {
const input = { forward: false, backward: true, left: false, right: false };
const result = getInputDirection(input, mockCamera);
expect(result.z).toBeCloseTo(1); // Backward is positive Z
});
it('returns left vector for A key', () => {
const input = { forward: false, backward: false, left: true, right: false };
const result = getInputDirection(input, mockCamera);
expect(result.x).toBeCloseTo(-1); // Left is negative X
});
it('returns right vector for D key', () => {
const input = { forward: false, backward: false, left: false, right: true };
const result = getInputDirection(input, mockCamera);
expect(result.x).toBeCloseTo(1); // Right is positive X
});
it('handles diagonal input correctly', () => {
const input = { forward: true, backward: false, left: true, right: false };
const result = getInputDirection(input, mockCamera);
expect(result.length()).toBeCloseTo(1); // Normalized
expect(result.x).toBeCloseTo(-0.707); // Diagonal
expect(result.z).toBeCloseTo(-0.707);
});
});
Level 3: Visual Debug Mode
Add debug visualization for input direction:
// In PlayerController.tsx
const showDebug = useDebugStore(state => state.showInputDebug);
useEffect(() => {
if (showDebug) {
// Add arrow helper to show input direction
const arrowHelper = new ArrowHelper(
inputDirection.clone().normalize(),
playerPosition,
2, // Length
0x00ff00 // Green color
);
scene.add(arrowHelper);
return () => scene.remove(arrowHelper);
}
}, [inputDirection, showDebug]);
Level 4: Regression Prevention
After fixing input bugs (like bugfix-002):
- Add unit test covering the exact bug scenario
- Add E2E test for manual verification
- Document in PRD validation steps
Common Input Bugs
| Bug | Symptom | Fix |
|---|---|---|
| Inverted WASD | W moves backward | Swap vector signs |
| World-space movement | W moves north regardless of camera | Use camera direction |
| Non-normalized diagonal | Diagonal faster than cardinal | Normalize vector |
| Wrong key mapping | Keys don't match labels | Check KeyCode names |
Anti-Patterns
❌ DON'T:
- Commit input changes without manual testing
- Assume keyboard layouts are consistent (use
KeyWnotcode: "KeyW") - Mix up forward/backward vector signs
- Skip diagonal testing
- Use hardcoded world directions for WASD
✅ DO:
- Always test WASD manually before commit
- Add unit tests for getInputDirection()
- Use camera-relative movement
- Test all diagonal combinations
- Add debug visualization for development
Checklist
Before committing input changes:
- W = forward (away from camera)
- S = backward (toward camera)
- A = left (screen left)
- D = right (screen right)
- All diagonals work (W+A, W+D, S+A, S+D)
- Movement is camera-relative
- Diagonal speed equals cardinal speed
- Unit tests added for getInputDirection()
- Debug mode shows input direction
-
npm run type-check && npm run lintpasses
Related Skills
For camera controls: Skill("ta-camera-tps")
For physics input: Skill("ta-r3f-physics")
External References
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?