I’m trying to navigate a GUI with a game pad, and wondering how you give focus to a control.
In the reference above it says you just use a string for a named control, but I can’t find how to name a control…
For example, how would I give focus to a GUI.Toggle using a game pad button, if the Toggle constructor returns a boolean, rather than a reference to the actual toggle…?
You call SetNextControlName just before the control you want to have a specific name.
(I’ve now updated the docs to show that)
var username = "username";
function OnGUI () {
// Set the internal name of the textfield
GUI.SetNextControlName ("MyTextField");
// Make the actual text field.
username = GUI.TextField (Rect (10,10,100,20), username);
// If the user presses this button, keyboard focus will move.
if (GUI.Button (Rect (10,40,80,20), "Move Focus"))
GUI.FocusControl ("MyTextField");
}
Could you help me out with my actual code, as it’s throwing this error:
“NullReferenceException: Object reference not set to an instance of an object”
// selected controls is an array containing the control names, as set with GUI.SetNextControlName
if (Input.GetAxis("DPadVertical"))
{
if (!channelControlUpdated)
{
channelControlUpdated = true;
selectedControlIndex += Input.GetAxis("DPadVertical");
if (selectedControlIndex < 0)
{
selectedControlIndex = channelControls.length-1;
}
else if (selectedControlIndex > channelControls.length-1)
{
selectedControlIndex = 0;
}
selectedControl = channelControls[selectedControlIndex];
print("Selected Channel Control: " + channelControls[selectedControlIndex]);
// this is causing a problem
GUI.FocusControl(selectedControl);
}
}
else
{
channelControlUpdated = false;
}
The underlying variable of the toggle is a variable of a game object component script, which the toggle is turning on and off.
If I change the underlying variable to a proxy variable storing the boolean value, how do I then update the game object value? Is there an event I can intercept when the toggle is changed?
you should be able to just update the toggle directly and ignore the whoel concept of underlying variable.
E.g. attach this script to a light
class ToggleLightGUI {
void OnGUI () {
light.enabled = GUILayout.Toggle (light.enabled, "Turn light on and off");
}
}
if you change the light’s enabled state from anywhere else (from another script or the editor inspector), the GUI will automatically reflect it - the toggle button doesn’t remember its state, you feed it to it every frame.
Ok, after several hours scratching my head I’ve acheived what I wanted using a helper object inside the main script:
class GUIHelper
{
var name:String; // name of the toggle
var target:Component; // target script the toggle is tied to
var func:String; // function name to call when a player hits a button to toggle the focused control
}
The helper allows me to associate a function call with the toggle.
if (Input.GetButtonDown("Action"))
{
if (selectedControl)
{
selectedControl.target.SendMessage(selectedControl.func, "true");
}
}
After working in Flash ActionScript for many years, the idea of a GUI that doesn’t maintain state between frames has been hard to get my head around.
[EDIT: thinking about it, the GUI must maintain state to some extent, because you can request the name of the focused control, and the control remains focused between frames - so a GetFocusedControl should be possible, no?]
Anyhow, this code works for me, but it’s a long workaround, and I’d like to hear from anyone who could acheive this in a way that fits better with Unity’s GUI system.
This is interesting, but I had the same issue. I was ‘spoilt’ with Flash and Flex, so I couldn’t work this way no more (procedural, scripting, whatever you call it). I mean, I could do it - but obviously with poor results (no reusability etc.)
I think it’s crucial to build your own framework, at least for GUI.
Here’ the example class that I wrote, which handles focus:
using System;
using Event=Com.DankoKozar.Unity.Gui.Core.Events.Event;
namespace Com.DankoKozar.Unity.Gui.Core.Managers
{
/// <summary>
/// Singleton class for handling focus
/// Coded by Danko Kozar
/// </summary>
public class FocusManager
{
#region Singleton
private static FocusManager _instance;
private FocusManager()
{
// Constructor is protected!
}
/// <summary>
/// Singleton instance
/// </summary>
public static FocusManager instance
{
get
{
if (_instance == null)
{
_instance = new FocusManager();
_instance.Initialize();
}
return _instance;
}
}
#endregion
/// <summary>
/// Initialization routine
/// Put inside the initialization stuff, if needed
/// </summary>
private void Initialize()
{
}
public UIComponent focusedComponent;
/// <summary>
/// Is any of the components in focus
/// </summary>
public bool isFocused
{
get
{
return null != focusedComponent;
}
}
/// <summary>
/// Sets focus to the component
/// Blurs previously focused component
/// </summary>
/// <param name="component">Component to focus</param>
public void SetFocus(UIComponent component)
{
/**
* 0) Do this only if component is not currently focused
* */
if (focusedComponent == component)
return; // else do nothing
/**
* 1) Blur previuosly focused component
* */
Blur();
/**
* 2) Focus current component
* */
component.DispatchEvent(new Event(UIComponent.EVENT_FOCUS, true));
/**
* 3) Set current component as focused component
* */
focusedComponent = component;
}
/// <summary>
/// SetFocus override with component ID
/// </summary>
/// <param name="componentId">The ID of the component</param>
public void SetFocus(string componentId)
{
UIComponent c = ComponentManager.instance.Get(componentId);
if (null != c)
SetFocus(c);
}
/// <summary>
/// Blurs everything
/// </summary>
public void Blur()
{
if (null != focusedComponent)
focusedComponent.DispatchEvent(new Event(UIComponent.EVENT_BLUR, true));
focusedComponent = null;
}
/// <summary>
/// Blurs the referenced component but only if currently in focus
/// </summary>
/// <param name="component"></param>
public void Blur(UIComponent component)
{
if (focusedComponent == component)
Blur();
else
throw new FocusManagerException(FocusManagerException.COMPONENT_NOT_IN_FOCUS);
}
}
public class FocusManagerException : Exception
{
public const string COMPONENT_NOT_IN_FOCUS = "Component not in focus, so cannot be blurred";
public FocusManagerException()
{
}
public FocusManagerException(string message)
: base(message)
{
}
}
}
Note: UIComponent mentioned in the code is my base class that handles all GUI elements.
The basis for a proper GUI framework is a Composite pattern (handles tree-like structure of nodes in the hierarchy, parent and child nodes) and separate Update() and Render() methods for each element, which are recursively called through all the branches of the tree on GUI’s Update() and OnGUI() events. Render() methods ‘wraps up’ Unity GUI render calls, for instance, for the Button control:
protected override void Render()
{
base.Render();
string s = styleName;
// disabled?
if (!enabled !string.IsNullOrEmpty(disabledStyleName))
s = disabledStyleName;
bool clicked = false;
if (!string.IsNullOrEmpty(s))
{
if (GUI.Button(bounds, content, s))
clicked = true;
}
else
{
if (GUI.Button(bounds, content))
clicked = true;
}
if (clicked)
DispatchEvent(new Event(EVENT_CLICK, this));
}