Quick & Easy: My Strategy for UI in Unity 5
Blog Posts ,Cardboard VR ,Programming ,Tutorials ,Uncategorized ,Unity ,Virtual RealityJune 13, 2015
misslivirose
I’ve been working on a new project, AllegroVR, where I combine my love of virtual reality, computer science, and music composition to bring you a mobile VR app that lets you make a lot of noise music by gazing at various things in your environment and initializing a sound clip attached to it. Right now, one stage is complete (dubbed ‘Chaos’, code named ‘Synesthesia’) and I have a second planned. When the user launches the application, they land on a screen where they can choose their music making mode, or read an ‘About’ text for the app.
In this project, I’m experimenting with the Cardboard SDK, and I was pretty quickly able to get a test gaze input up and running. I’ve learned a ton about EventSystems in Unity and how particular GameObjects can be when they’re copied & pasted, but that’s a tale for another day. Today, I’m going to share a different problem I ran into, one that I think may be more global than just in VR projects, especially as people begin using Unity 5 more frequently.
Within Unity, there are only so many things that you can edit programatically when it comes to GameObject components; for example, there is an Alpha property of a CanvasGroup component that can be changed within a script, but you cannot directly edit a child of a Canvas by getting their Image component and modifying the color.a value. Because of the access limitations on components in Panels, I went ahead and created two separate canvases in my scene: one to hold the navigation buttons, and one to display the ‘About’ text panel.
For consistency, I wanted to overlay the canvases at the same coordinates so that to a viewer, clicking the ‘About’ button would simply display a new panel. Once I placed the two panels overlapping, the panels started intercepting each other’s raycast collisions, resulting in broken ‘OnClick()’ behaviors for each of the Canvases. Setting the alpha (transparency) of the non-active canvas didn’t fix this issue, so I poked around for a few minutes and found this solution, which works perfectly even if it does feel a bit hack-y. Shoot me a note if there’s something better I’m missing!
In my main MenuControl.cs script, I created two functions that serve as a Show/Hide for the two Canvases. In addition to setting the alpha values for each one, I programatically disable raycasting for the non-active screen to prevent interference with the active one:
// Initially hide the information panel void HideAboutPanel() { GameObject.FindGameObjectWithTag ("mainPanel"). GetComponent<CanvasGroup> ().alpha = 1f; GameObject.FindGameObjectWithTag ("aboutPanel"). GetComponent<CanvasGroup> ().alpha = 0f; GameObject.FindGameObjectWithTag ("aboutPanel"). GetComponent<CanvasGroup> ().blocksRaycasts = false; GameObject.FindGameObjectWithTag ("mainPanel"). GetComponent<CanvasGroup> ().blocksRaycasts = true; } // Show the 'About' panel void ShowAboutPanel() { GameObject.FindGameObjectWithTag ("aboutPanel"). GetComponent<CanvasGroup> ().alpha = 1f; GameObject.FindGameObjectWithTag ("mainPanel"). GetComponent<CanvasGroup> ().alpha = 0f; GameObject.FindGameObjectWithTag ("aboutPanel"). GetComponent<CanvasGroup> ().blocksRaycasts = true; GameObject.FindGameObjectWithTag ("mainPanel"). GetComponent<CanvasGroup> ().blocksRaycasts = false; }
In school, I often got berated by team members for having too many global variables in my code, so my first attempt (above) was a bit wordy and I realized that I didn’t have to call
GameObject.FindGameObjectWithTag ("mainPanel").GetComponent<CanvasGroup> ()
as many times as I did – so I polished it up with assigning those two components in the inspector, and changed that hunk of text to something smaller and cleaner.
public CanvasGroup _mainCanvas; public CanvasGroup _aboutCanvas; /** Other methods here **/ // Initially hide the information panel void HideAboutPanel() { _mainCanvas.alpha = 1f; _aboutCanvas.alpha = 0f; _aboutCanvas.blocksRaycasts = false; _mainCanvas.blocksRaycasts = true; } // Show the 'About' panel void ShowAboutPanel() { _aboutCanvas.alpha = 1f; _mainCanvas.alpha = 0f; _aboutCanvas.blocksRaycasts = true; _mainCanvas.blocksRaycasts = false; }
Note: Don’t forget to assign the CanvasGroup objects to their appropriate spots in the inspector when you add in the global variables!