Modular Interaction System
A powerful, networked, mod-first interaction framework designed for flexibility and depth — far beyond simple "press E to use".
Every object that can be interacted with implements the IInteractable interface. The system handles detection, UI presentation, input forwarding, physics carrying, cancellation logic, and networking — all in one unified pipeline.
Core Components & Flow
Detection Phase (Client-side)
PlayerInteraction performs a raycast from the camera every frame (max interactRange = 3 m by default)
Only objects on the interactLayer are considered
If a valid IInteractable is hit → UI panel appears with object name + list of options
Option Selection & Activation
Player can scroll (mouse wheel) to highlight different options
Options are provided dynamically by the object via GetOptions()
Two main types:
Instant (RequiresHold = false) → triggered on single press (OnPress)
Hold (RequiresHold = true) → supports OnHoldStart / OnHoldUpdate / OnHoldReleased / OnHoldCanceled
Interaction Packet — the heart of the system Every interaction sends a rich InteractionPacket to the server:
public struct InteractionPacket { public string ActionTag; // "OnPress", "OnHoldStart", "OnHoldUpdate", etc. public float HoldDuration; // how long the hold has been active (seconds) public List<KeyCarryingData> Inputs; // all relevant inputs at that moment }KeyCarryingData contains:
Button states (CustomI_XXX keys that are pressed)
Press order (stack index — first pressed = highest priority)
Mouse delta (CustomI_MouseVector2 → Vector2)
Other analog values if needed
→ This packet lets the object know exactly what the player is doing right now.
Server-side Execution
Client → CmdInteract(NetworkObject target, optionId, packet)
Server → finds IInteractable on the target → calls OnInteract(playerGameObject, optionId, packet)
Important Mechanics & Safety Features
Interaction Range
Max distance for raycast detection
3.0 m
Hold Distance Limit
While holding, if player moves too far → interaction is auto-canceled
4.5 m
Distance Check Frequency
Checked every frame on client during hold
—
Forced Termination
Object can call BreakInteractionFromObject() to kick player out of hold (e.g. after throw)
Server → ObserversRpc → client
Cancellation Reasons
Too far / object destroyed / external force / invalid state
Client shows debug log
Hold Cancellation Tags
OnHoldCanceled (abrupt end) vs OnHoldReleased (normal drop)
Used for different reset logic
Input Forwarding
All CustomI_ actions + mouse delta are sent every frame during OnHoldUpdate
Priority stack preserved
Real Example: Forced End + Distance Break
Why This Level of Control Matters
Objects can reject interaction (return empty GetOptions list)
Objects can force-stop holding (e.g. item breaks, explodes, gets stolen)
Players can't abuse range (auto-drop prevents flying objects forever)
Modders can read any custom input without changing core scripts
Debugging is easier (hold timer, input stack, distance shown in OnGUI debug overlay)
This system gives you both player freedom and server authority, while staying extremely modder-friendly.
Last updated