UGTK / Toolkit / Mobile / Motion System Completed
Motion System
Summary
The Motion System reads real device motion on WebGL through a JavaScript bridge (WebXR pose when available, DeviceOrientation/DeviceMotion sensors as fallback) and exposes orientation, rotation, world velocity/displacement and shake. It raises high-level events for jump, crouch, shake, stationary state and configurable angle thresholds.
Content
Modules Dependencies
This module has no hard UGTK dependencies. On WebGL it relies on a companion .jslib exposing the UGTK_Motion_* bridge functions (permission, start/stop, sample JSON, shake).
Only in the demo scene. The Debug scene of this module also uses UIModule/TextUpdaterSystem.
Nothing in the runtime code needs it: import this module on its own and it works, but
the demo scene opens with a missing script where that component was.
Setup
- Add the
MotionObjprefab, which carriesMn_MotionManager. - Describe the gestures you care about as
AngleThresholdentries - anAxis, anOpand a value. Jump, crouch and shake are configured this way rather than hard-coded. - Read
MotionSample(with itsOrientationDeg,AccelandRotRate) if you need the raw values instead.
On WebGL the sensors come from the browser through Plugins/WebGL/UGTK_Motion.jslib, and three
conditions must all hold or you get silence rather than an error:
- the page must be served over HTTPS - browsers block motion sensors on plain HTTP;
- iOS requires a user gesture first, so the permission can only be requested from inside a tap handler, never at startup;
- a denied permission arrives as no data at all, so treat "no samples" as a state your UI handles, not as an impossibility.
In the editor there are no sensors. That is what the inspector's buttons are for: they fire the same gestures the sensors would, so the game logic can be finished at a desk and only the sensors themselves need a phone.
How To Use
- Add
Mn_MotionManagerto a GameObject. OnStart(WebGL) it initialises the JS bridge with the GameObject name andjsCallbackMethod, and, ifconnectOnStartis true, starts listening (on iOS it may wait for a user tap for permission). - Configure the sensor fallback filters, jump/crouch acceleration thresholds, shake gain/smoothing, integration caps and the
angleThresholdslist (axis Yaw/Pitch/Roll, operator, threshold, hysteresis, one-shot rearm). - Handle permissions with
RequestPermission(),ShowPermissionOverlay(),HasPermission(); control the stream withStartMotion()/StopMotion()and preferences withSetXRPreferred(bool),SetOriginToCurrent(),SetPositionScale(float),ResetDisplacement(). - Subscribe to
OnSample(MotionSample),OnJump,OnCrouch,OnStationary(bool),OnShake(float),OnXRActiveChanged(bool), and eachAngleThreshold.OnTriggered. Read state viaIsXRActive,IsStationary,Shake01,WorldVelocity,CurrentRotation,LastSample. - In the Editor there is no real sensor data; holding SPACE simulates shake for quick testing.
Testing Scene
Open MotionSceneTest in the module's Debug/ folder.
What to do, in order:
- In the editor, fire jump, crouch, shake and stationary from the inspector.
- Build for WebGL, serve it over HTTPS, and open it on a phone.
- On iOS, check the permission prompt appears after a tap.
- Deny the permission and see what the game does.
Step 1 is how the game logic gets finished without a phone; steps 2 to 4 are the only way to know the sensors themselves work. The failures are silent and in this order of frequency: the page is not HTTPS so sensors are blocked, iOS needs a user gesture before it will even ask, and a denied permission reaches Unity as nothing at all.
The recording is the Editor Test Tools block on MotionObj, which is where this module is
legible without a phone: hold the space bar with the Game view focused and the Shake reading
climbs to 1 and falls back. Everything else stays still, and says so - there is no sensor in the
editor, so Source reads "no sample" and the orientation stays at zero. The buttons underneath
raise the events directly, which is enough to finish the game logic before any device is involved.
The scene's own panel shows the same readings inside the game, one line per value:
Mn_MotionReadout, on the scene's Canvas, fills it. Every line whose value can only come from the
browser says no sensor rather than 0 while you are in the editor: a tilt of exactly zero and a
tilt that was never measured look identical otherwise, and the second one is what the editor has.
Technical Info
- Mn_MotionManager: receives motion samples from the JS bridge (
JS_OnMotion), integrates velocity/displacement, detects jump/crouch/shake/stationary and evaluates angle-threshold events. - MotionSample / OrientationDeg / Accel / RotRate / Vec3 / Quat: serializable data structs describing a motion frame.
- AngleThreshold: serializable rule (axis, operator, threshold, hysteresis, one-shot rearm) firing a UnityEvent.
© 2026 Marcello De Bonis. All rights reserved
Depends on 2
Used by 0
- No other module depends on it.
Measured from the repository: code references (asmdef) plus prefab and asset GUIDs. Importing this module alone brings in 3 modules in total.
UGTKengine within an engine