CAGD 458 - Blog Post 2 - 4/12/2026
This sprint I finalized the compass getting it to track the devices orientation, it is not perfect, but it is very close to what a compass app would show you. In the demo, the compass rotates in real time as the device turns, and that movement is driven by a JavaScript bridge that listens to deviceorientation events in the browser. I handle multiple heading cases in the JavaScript, using alpha when absolute data is available, falling back to webkitCompassHeading on iOS, and converting values when needed to keep the heading consistent. That value is sent into my UpdateHeading method using SendMessage, where I parse it and store it as the current heading. I also added validation checks to make sure the value is not NaN or invalid before applying it, which helped prevent occasional glitches where the compass would jump or freeze.
To make the compass feel stable, I focused heavily on filtering and smoothing the incoming data. I use Mathf.DeltaAngle to calculate the difference between the current and previous heading, which helps handle wraparound at 360 degrees correctly. I ignore very small changes to reduce jitter and skip large spikes that usually come from bad sensor reads. After that, I apply Mathf.LerpAngle over time so the dial rotates smoothly instead of snapping to new values. The final rotation is applied directly to a VisualElement using style.rotate, which keeps everything inside the UI Toolkit and avoids using transforms. I also exposed a heading offset and invert option so I can quickly align the compass with the UI design without changing the underlying math. This setup keeps the compass responsive while handling inconsistent sensor data across devices.
After I finished the compass, I moved on to getting the help menu to display. In the demo, the map acts as the main container, and I control all UI visibility through UI Toolkit instead of toggling GameObjects. In OnEnable, I query the rootVisualElement from the UIDocument and grab references to the help menu, main map container, and all relevant buttons. I added null checks for each element so I can catch UXML naming issues early, which makes debugging much easier. I also define the initial state here by setting the help menu to DisplayStyle.None and the main map to DisplayStyle.Flex so the correct view is shown on startup.
The interaction logic is handled through button click callbacks that toggle display states directly. When the help button is clicked, I set the help menu to visible and hide the help button, so it does not overlap. When the back button is pressed, I reverse that state and return to the main map view. I also added a separate handler to hide the main map when interacting with other UI elements like the POI button. All callbacks are registered in OnEnable and removed in OnDisable to prevent duplicate bindings if the UI reloads. This approach keeps the system lightweight and predictable, since I am only changing style properties instead of enabling and disabling objects. It also makes the UI easy to extend, since adding new panels or states only requires updating display rules rather than restructuring the hierarchy.
.gif)
.png)
.gif)
Comments
Post a Comment