Quick & Easy: My Strategy for UI in Unity 5

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.

Two canvas objects in a Unity 3D scene, offset slightly

The two canvases before adjusting the coordinates to overlap

 

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!

A menu in Unity with three buttons

The menu is working!

Related Posts

Leave a Reply

Your email address will not be published.