- Home /
binding properties to UI Text/Image etc
Managing GUI Elements
Can someone point me in the right direction as to 'binding' game object properties to UI as although what I have works, it has me stuck up to my nose in code where I would imagine there has to be a simpler recommended way using the inspector
I have a Group of GameObjects as shown below
▼ UICanvas //Canvas
▼ Active Character //Empty Game Object
▼ Panel // UI Panel
CharacterName //UI Text
CharacterImage //UI Image
CharacterClassImage //UI Image
CharacterWeaponName //UI Text
I have a UIManager monobehavior attached to my UI Canvas that I can store a reference to during the Start() of my game Manager
_uiManager = GameObject.Find ("UICanvas").GetComponent<UIManager>();
similarly I have a way of getting a reference to the GameManager from the start method of my UIManager
_gameManager= GameObject.Find ("GameManager").GetComponent<GameManager>();
and in my GameManager's update function I make a call to
_uiManager.ActiveCharacterChanged();
Here is what my UIManager class looks like with 1 panel and zero interactivity which I can only imagine will get exponentially worse and unmanagable
public class UIManager : MonoBehaviour {
private GameManager _gameManager;
private Transform _activeCharacterPanel;
private Text _activeCharacterName;
private Image _activeCharacterImage;
private Image _activeCharacterClassImage;
private Text _activeCharacterWeaponName;
// Use this for initialization
void Start ()
{
_activeCharacterPanel = this.transform.FindChild("Active Character").FindChild("Active Character Panel");
_activeCharacterName = _activeCharacterPanel.FindChild("CharacterName").GetComponent<Text>();
_activeCharacterImage = _activeCharacterPanel.FindChild("CharacterImage").GetComponent<Image>();
_activeCharacterClassImage = _activeCharacterPanel.FindChild("CharacterClassImage").GetComponent<Image>();
_activeCharacterWeaponName = _activeCharacterPanel.FindChild("CharacterWeaponName").GetComponent<Text>();
if (_activeCharacterPanel == null)
{
Debug.LogError ("Could Not find active char panel");
}
_gameManager = GameObject.Find("GameManager").GetComponent<GameManager>();
if (_gameManager == null)
{
Debug.LogError ("GameManager is Null");
}
}
public void ActiveCharacterChanged()
{
if (_gameManager.ActiveCharacter != null)
{
_activeCharacterName.text = _gameManager.ActiveCharacter.DisplayName;
//_activeCharacterImage.texture = TODO;
//_activeCharacterClassImage.texture = TODO;
_activeCharacterWeaponName.text = _gameManager.ActiveCharacter.Weapon.Name;
}
}
}
I came up with the following, http://forum.unity3d.com/threads/simplifying-ui-data-exchange-method-presented-feedback-requested.288195/#post-1902199. I suspect it's not as automated or robust as you would like, but it HAS certainly reduced (if not eli$$anonymous$$ated) the need to write new code for UI elements.
Thanks I'll take a look, its extremely odd theres no supported way to bind and update values with ease without code behind.
I feel I have kind of retread covered ground I've looked at data binding with NotifyPropertyChanged as similar to what I need since there should only be a call to Update the UI when there is a meaningful value change.
Currently I have set it so that I have a publicly accessible property that when set executes a method on the UI $$anonymous$$anager to update the respective value that I have 'bound', I think that there should be a way to call a single method from any value and have it updated by passing in a parameter but admittedly I'm not sure how to go about this there may be someone more familiar with this stuff than me that has an idea
public Character ActiveCharacter
{
get
{
return _activeCharacter;
}
set
{
if (_activeCharacter != value)
{
_activeCharacter = value;
_ui$$anonymous$$anager.ActiveCharacterChanged();
}
}
}
Heres the link to NotifyPropertyChanged for Windows Presentation foundation that I'm looking at and bastardizing
http://msdn.microsoft.com/en-us/library/ms743695%28v=vs.110%29.aspx
Definately looks like something the unity $$anonymous$$m needs to addressed, it's a little puzzling and I can't seem to find a way around needing to write code
there may be a more complex up front solution ala an interface implementation notifying the unity ui system theres an updated property, getting the component it's attached to and re-getting the value for display, I'm not as proficient with c# to actually try and do that though someone might know if that is or is not possible.
Answer by Oliver1135 · Jan 05, 2015 at 03:09 AM
I'm not marking this as an accepted answer as as far as i'm concerned it's still a ways off from being a robust solution much like Glurths, it works and it is what I will be using but im convinced there has to be a way to structure this in a way that manages its own UI value updating.
I have a way to do this with only very little code and little setup, the downside being you need to know when the value you want to display changes as you set it via dictionary in a singleton. Code below
Example of a call to the UIManager to update the text being displayed
UIManager.Instance.UITextBindings["ActiveCharacterName"].text = _activeCharacter.DisplayName;
Here is the UIManager class (I think its called a singleton)
using UnityEngine; using UnityEngine.UI; using System.Collections.Generic;
public class UIManager
{
private static UIManager _instance;
public static UIManager Instance
{
get
{
if (_instance == null)
{
_instance = new UIManager();
}
return _instance;
}
set
{
_instance = value;
}
}
public Dictionary<string, Text> UITextBindings = new Dictionary<string, Text>();
}
Here is the behavior you attach to a gameobject with a text component on it. You give it a binding name which in the start function adds it to the dictionary and you can set the text initial value as well. It only takes strings of course so you need to do a little formatting or .ToString() calls for numbers you need to display
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using System;
public class TextBindingBehavior : MonoBehaviour
{
public string BindingName;
public string TextInitialValue;
public Text TextComponent;
public string TextValue
{
get
{
if (TextComponent == null)
{
TextComponent = this.gameObject.GetComponent<Text>();
}
return TextComponent.text;
}
set
{
if (TextComponent == null)
{
TextComponent = this.gameObject.GetComponent<Text>();
}
TextComponent.text = value;
}
}
// Use this for initialization
void Start ()
{
TextValue = TextInitialValue;
UIManager.Instance.UITextBindings.Add(BindingName, TextComponent);
}
}
Is there any particular reason you are defining your UImanager, as a non-static class, (I ask since it looks like you only want one instance of the class)? If you define it as a static class, you can eli$$anonymous$$ate that _instance variable (as well as the need to instantiate UImanager). e.g. usage: UI$$anonymous$$anager.UITextBindings.Add(BindingName, TextComponent);
Also, the link I posted to in my first comment has an additional post now, one of which mentions a way to bind the values to variables. It uses c# delegates and events, which I'm still learning.
Edit: ok, I updated it to include "subscriptions". please take a look- http://forum.unity3d.com/threads/simplifying-ui-data-exchange-method-presented-feedback-requested.288195/#post-1906038
Ok Thanks for the comment, the reason I have it set up this way is i'm trying the singleton pattern, right now I really dont see any difference really what so ever other than being able to clear the instance every time I load a new scene (still not finding a great global way to cover re-entering scenes where they have keys added that are already defined previously in the dictionary)
bit of inspiration from http://en.wikipedia.org/wiki/Singleton_pattern
as I only want one per scene, but I will want different "instances" of the UI$$anonymous$$anager per scene as the behaviors add keys to the dictionary On Start()
I'll definately take a look at the c# delegates and events thread to see if I can make sense of it, part of the problem is not fully understanding all the c# syntax and really "what" I can do with it.