All work

The Interactable Framework

The foundation everything interactive sits on. The framework is deliberately generic and universal: it does not know about game context. A document does not know it is a document, and it does not know what a pen is. It receives signals and reacts, and that is the full extent of its concern. The framework provides exactly three things: a universal mode layer, a controller-owned state machine, and a pluggable module system.

The universal mode layer

Sitting above every controller is a shared mode axis: Normal or Locked. When the mode isn't Normal, input is gated at the framework level before it ever reaches the controller. A scene-singleton InteractionModeManager holds the current mode and fires a C# event on change; interactables subscribe on enable and unsubscribe on disable. When the pen activates, a single broadcast locks every draggable on the desk, and none of them know what a pen is. Mode is orthogonal to a controller's own state: an object can be Locked while still internally Hovered.

Controllers

Each controller is a concrete implementation that owns its own state enum, its own state machine, the input events it listens to, and its transition logic. The two starting controllers are ClickableInteractable (Idle, Hovered, Pressed, Released) and DraggableInteractable (Idle, Hovered, Dragging, Released). New interactive object types, rotary dials, drawers, lamps, are added as new controllers without ever touching the framework. Type-specific behaviour lives in the controller's folder; anything reusable lives in the shared module pool.

Pluggable modules

Modules are components attached per instance that subscribe to state changes and react independently; the controller never calls them directly. PointerInputModule detects hover, click, and drag via camera-relative raycasts that work in 2.5D. On the output side, AnimationOutputModule maps states to Animator triggers, SoundOutputModule maps them to audio clips, and TransformOutputModule moves an object during drag. Output modules are configured in the inspector with string keys, trading compile-time safety for authoring flexibility, with a custom validator inspector noted as a future nicety.