BeaconColorPicker: fix VR/controller compatibility (panel flashing, d-pad, VR keyboard) - #1
Open
Gugarion wants to merge 1 commit into
Open
BeaconColorPicker: fix VR/controller compatibility (panel flashing, d-pad, VR keyboard)#1Gugarion wants to merge 1 commit into
Gugarion wants to merge 1 commit into
Conversation
Three independent bugs, found and fixed while getting this mod working in a VR + gamepad co-op setup: 1. Panel flashes shut immediately when opened in VR or with a controller. Cursor.lockState == Locked is a mouse-specific proxy for "player clicked back into the game world, close this menu" -- it's already true from ordinary gameplay in VR (cursor effectively always locked there) and for controller input (no mouse-look capture/release cycle for a controller press to trigger), so the panel closed within a frame of ever opening for either. 2. The "+" button is invisible to d-pad/controller navigation. uGUI_PingEntry.GetSelectables() builds its navigable list directly and only from colorSelectors[], so a control that isn't a genuine member of that array is unreachable by controller no matter how it's wired up otherwise. Fixed by keeping the clone as a Toggle (instead of replacing it with a Button) and appending it onto colorSelectors, so the game's own unmodified GetSelectables() picks it up automatically. As a side benefit, this also replaces the previous RemoveAllListeners() (which the code's own comment already flagged as unreliable for clearing persistent/prefab-serialized listeners) with assigning a brand-new ToggleEvent, which is guaranteed clean. 3. Clicking the hex field doesn't open a VR virtual keyboard. SubmersedVR (and likely other VR mods) hook their keyboard support as a Harmony postfix on TMPro.TMP_InputField.ActivateInputField specifically -- the legacy InputField's ActivateInputField never triggers it, so the field still accepted clicks/physical-keyboard input (looked like it worked) but never opened a VR keyboard. Fixed by switching to TMP_InputField, the same primitive the base game's own UI and SubmersedVR's hook both expect. Also added, since fixing #2 exposed that the panel's own controls (sliders, hex field, Apply/Close) had no controller navigation at all once opened: - A minimal uGUI_INavigableIconGrid (ColorPickerPanel.NavigableGrid) over the panel's controls, seized/restored around Show()/Hide(). GamepadInputModule already dispatches value changes based on the selected item's runtime type (right stick onto a Slider, UISubmit onto anything IPointerClickHandler), so this only needs to move focus between controls, not manipulate their values. - Self-healing grid re-assertion in Update(): uGUI_PDA.OnSelect resets GamepadInputModule's current grid on every PDA focus change (e.g. simply reopening the PDA while this panel is still up) with no awareness the panel might be open on top of it. Re-asserting every frame is more robust than trying to patch every place that might reset it. - uGUI_PDA_OnDeselect_Patch: closes the panel automatically when the PDA itself closes, rather than leaving it open with lost focus. - An explicit focus-ring indicator (ShowFocusRing/CreateFocusRing), since Unity's own Selectable ColorTint transition turned out unreliable here -- targetGraphic is assigned after AddComponent<Button>/<Slider>, so the very first automatic state transition (which fires immediately on enable) runs against a null targetGraphic and never visibly recovers even once targetGraphic is set later. Every fix was diagnosed against this exact codebase (not guessed) -- several went through 2-4 rounds each, most of them because a first theory was disproven by adding logging and reading BepInEx's own log output from a live session rather than trusting static analysis alone. Happy to share more detail on any of them if useful. One caveat: Shared/ColorPicker/ColorPickerPanel.cs is linked into both BeaconColorPicker and BeaconColorPicker.BZ. I added the two new references (Unity.TextMeshPro, UnityEngine.VRModule) to both csproj files for symmetry, and this compiles cleanly against a real Subnautica (non-BZ) install, but I don't have Below Zero to verify against -- worth a check before merging if BZ is still maintained. Tested against Subnautica v5.4.23.5 with BepInEx 5, SubmersedVR, and a physical Xbox controller. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Author
|
Hope this helps! Tested with Submersed/Nitrox/Buddy System using flat mode (vrmode -none) using a controller or a mouse and with VR mode (vrmode -openvr). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three independent bugs found and fixed while getting this mod working in a VR + gamepad co-op setup, plus controller navigation added to the picker panel itself once fixing one of them exposed it had none:
Panel flashes shut immediately when opened in VR or with a controller.
Cursor.lockState == Lockedis a mouse-specific proxy for "player clicked back into the game world, close this menu" — it's already true from ordinary gameplay in VR (cursor is effectively always locked there) and for controller input (there's no mouse-look capture/release cycle for a controller press to trigger), so the panel closed within a frame of ever opening for either.The "+" button is invisible to d-pad/controller navigation.
uGUI_PingEntry.GetSelectables()builds its navigable list directly and only fromcolorSelectors[], so a control that isn't a genuine member of that array is unreachable by controller no matter how its click handler is wired. Fixed by keeping the clone as aToggle(instead of replacing it with aButton) and appending it ontocolorSelectors, so the game's own unmodifiedGetSelectables()picks it up automatically. As a side benefit this also replacesRemoveAllListeners()— which the existing code comment already flagged as unreliable for clearing persistent/prefab-serialized listeners — with assigning a brand-newToggleEvent, which is guaranteed clean.Clicking the hex field doesn't open a VR virtual keyboard. SubmersedVR (and likely other VR mods) hook their keyboard support as a Harmony postfix on
TMPro.TMP_InputField.ActivateInputFieldspecifically — the legacyInputField'sActivateInputFieldnever triggers it, so the field still accepted clicks and physical-keyboard input (looked like it worked) but never opened a VR keyboard. Fixed by switching toTMP_InputField, the same primitive the base game's own UI and SubmersedVR's hook both expect.Fixing #2 exposed that the panel's own controls (sliders, hex field, Apply/Close) had no controller navigation once opened at all, so this PR also adds:
uGUI_INavigableIconGrid(ColorPickerPanel.NavigableGrid) over the panel's controls, seized/restored aroundShow()/Hide().GamepadInputModulealready dispatches value changes based on the selected item's runtime type (right stick onto aSlider,UISubmitonto anythingIPointerClickHandler), so this only needs to move focus between controls, not manipulate their values.Update():uGUI_PDA.OnSelectresetsGamepadInputModule's current grid on every PDA focus change (e.g. simply reopening the PDA while this panel is still up), with no awareness the panel might be open on top of it. Re-asserting every frame turned out more robust than trying to patch every place that might reset it.uGUI_PDA_OnDeselect_Patch: closes the panel automatically when the PDA itself closes, rather than leaving it open with lost controller focus.ShowFocusRing/CreateFocusRing), since Unity's ownSelectableColorTinttransition turned out unreliable as a focus indicator here —targetGraphicis assigned afterAddComponent<Button>()/<Slider>()inCreateButton/CreateSlider, so the very first automatic state transition (which fires immediately on enable) runs against a nulltargetGraphicand never visibly recovers even oncetargetGraphicis set.Every fix was diagnosed against this exact codebase, not guessed — several took 2-4 rounds each, mostly because a first theory got disproven by adding logging and reading the actual
BepInExlog from a live session rather than trusting static analysis alone. Happy to share more detail on any of them if useful for review.Caveat
Shared/ColorPicker/ColorPickerPanel.csis linked into bothBeaconColorPickerandBeaconColorPicker.BZ. I added the two new references (Unity.TextMeshPro,UnityEngine.VRModule) to both.csprojfiles for symmetry, and this compiles cleanly against a real Subnautica (non-BZ) install, but I don't have Below Zero installed to verify against — worth a check before merging if BZ is still maintained.Test plan
dotnet build -c Release) against a real Subnautica install, 0 warnings/errors🤖 Generated with Claude Code