Design patterns in use
- Fluent builder:
InputMessageBuilderextends the sharedMessageBuilderbase. Callers chainCreate,AddParam,BuildMessage, andSend. The builder fixes the verb and subject at creation and adds the subject as a param automatically. - C# events:
ClickDetector.OnClickandDoubleClickDetector.OnDoubleClickare plainActionevents.InputManagersubscribes to them in its constructor, so each detector stays unaware of the message system. - Flow controller registration:
InputFlowControllerowns the Input domain. It registers its command handler with the command bus through its base class, and it registersInputManagerwith the update system. - Per-frame tick:
InputManagerimplementsIUpdateable. The update system callsOnUpdateevery frame. - Compile-time platform selection: The
UNITY_ANDROIDdefine chooses betweenHandleTouchandHandleMouse. Only one input path is compiled per platform.
Connections to other systems
- Update system:
InputFlowController.InitcreatesInputManagerand sends it throughUpdateCommandFactory.RegisterUpdatable. The update system then callsOnUpdateeach frame. - Flow and command system:
InputFlowControllerderives fromFlowControllerand returnsDomain.Input. Commands addressed to that domain reachHandleCommand, which only logs a warning. - Message system:
InputMessageBuildercreates anEventonDomain.Input.Sendpasses it to the shared message dispatch. - Params system:
InputParamKeysuses the sharedParamKey<T>type. Each event stores its data inParams, read by listeners withGetorTryGet. - Contracts:
Domain.Inputidentifies the domain. The verb and subject enums are local to this system.
Key terms
- Verb: what happened, from
InputEventVerb(for examplePressed,Clicked,Started). - Subject: what the event is about, from
InputSubject:PointerorDrag. - Press: a button-down (mouse) or
TouchPhase.Began(touch). - Click: a press and release that finish within
InputConstants.ClickThresholdseconds, with no drag in between. - Double-click: two presses within
InputConstants.DoubleClickTimeseconds. - Drag: a press whose pointer moves more than
InputConstants.DragThresholdpixels from the press position.
Event and data flow
Each frame, InputManager.OnUpdate runs the platform path. For a mouse press, the code does the following, in order:
- Store the press position and clear the drag flag.
- Send a
Pressedevent with the subjectPointer. - Call
ClickDetector.OnPressed, which records the time. - Raycast against the
EventSystem. If the hit object or its parents haveIInteractablecomponents, callOnInteractionon each. - Call
DoubleClickDetector.RegisterClickwithTime.time.
While the button is held, the code sends a Held event each frame. When the pointer moves past the drag threshold, the code sends Started with the subject Drag, then calls Reset on both detectors. Resetting the click detector prevents a click from being raised for a drag that ends quickly.
On release, the code calls ClickDetector.OnReleased, which raises Clicked only if the press was short enough and no drag cancelled it. Then the code sends Released. If a drag was active, it also sends Ended with the subject Drag.
Double-click detection runs on press, not release. The second press that falls inside the window raises DoubleClicked, which then resets. Click and double-click are independent, so a quick double tap can produce Clicked on each release and DoubleClicked on the second press.
Touch follows the same pattern, but it reads only touch index 0 and handles Began, Moved, Stationary, and Ended. Touch sends Held for Moved and Stationary, and it does not send Moved events. The touch path has no Canceled case, so a canceled touch leaves the press and drag state as it was.
Timing notes
ClickDetector and DoubleClickDetector use Time.time. Unity's Time.time is scaled time, so gesture timing stops advancing while Time.timeScale is zero.