Hi,
running on iOS, setting useGUILayout to false and using GUI.depth broke the depth sorting. All is working fine (i.e. GUI widget are sorted correctly according to GUI.depth vlaues) if i enable useGUILayout again. It seems that this is a bug already discovered by others, anyone came out with a solution?
It’s not really a bug, just not documented proberly. Unity uses the Layout event for several things. One is to determine the drawing order. If you disable the layout event Unity can’t determine in which order it should call the OnGUI methods. The order of the layour events is always the same. It’s based on some internal order in which the instances have have been created.
For every event that is handled by OnGUI there will be a layout event right before the actual event. Of course only if those are enabled with useGUILayout = true. When you disable the layout event several things that depend on that event won’t work anymore.
edit
Here’s an example how you could manually distribute an OnGUI event the way you want:
// GUIManager.cs
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public interface ICustomGUI
{
int GUIDepth{get;}
void CustomOnGUI();
}
public class GUIManager : MonoBehaviour
{
#region singleton
private static GUIManager m_Instance;
public static GUIManager Instance
{
get{
if (m_Instance == null)
{
m_Instance = (GUIManager)FindObjectOfType(typeof(GUIManager));
if (m_Instance == null)
m_Instance = (new GameObject("GUIManager")).AddComponent<GUIManager>();
DontDestroyOnLoad(m_Instance.gameObject);
}
return m_Instance;
}
}
#endregion singleton
void Awake()
{
// This class only works when useGUILayout is false
useGUILayout = false;
m_Instance = this;
}
List<ICustomGUI> m_List = new List<ICustomGUI>();
// Use this method to "register" a new instance
public static void Add(ICustomGUI aInst)
{
Instance.m_List.Add(aInst);
}
private int Compare(ICustomGUI a, ICustomGUI b)
{
return b.GUIDepth.CompareTo(a.GUIDepth);
}
private void Prepare()
{
// remove items which are null
for(int i = m_List.Count-1; i >=0; i--)
{
// the "else if" is for detecting Unity's "null" trick when an object is destroyed
if (m_List *== null)*
-
m_List.RemoveAt(i);*
else if (m_List*.Equals(null))
m_List.RemoveAt(i);
_ }_
_ // sort the list according to each “depth” value*_
* // The highest value will be the first one*
* m_List.Sort(Compare);
_ }*_
* private void OnGUI()*
* {*
* // if there’s no item in the list, just leave*
* if(m_List.Count == 0)
_ return;_
_ Prepare();_
_ Event e = Event.current;_
_ if (e.type == EventType.Layout)_
_ {_
_ Debug.LogError(“The GUIManager only works without layout event”);_
_ }_
_ else if (e.type == EventType.Repaint)_
_ {_
_ // draw them from the highest depth to the lowest*_
* for(int i = 0; i < m_List.Count; i++)
m_List.CustomOnGUI();
_ }
else*
* {
// other events are send from lowest depth to highest*
* for(int i = m_List.Count-1; i>=0; i–)*
* {_
m_List.CustomOnGUI();
_ // stop if the event has been eaten:
if (e.type == EventType.Ignore)
break;
}
}
}
}
And here’s a sample script that uses the manager:
//GUISampleScript.cs*
using UnityEngine;
using System.Collections;_
public class GUISampleScript : MonoBehaviour, ICustomGUI
{
* public int GUIDepth { get; set; }*
* public int StartingGUIDepth;*
* void Awake()*
* {*
* // important! This will register this instance so the manager knows about it.*
* GUIManager.Add(this);*
* }*
* // Use “CustomOnGUI” instead of OnGUI*
* public void CustomOnGUI ()*
* {*
* GUIDepth = StartingGUIDepth;*
* Vector3 p = transform.position;*
* if (GUI.Button(new Rect(p.x,p.y,100,50),gameObject.name))*
* {*
* Debug.Log(gameObject.name+" has been clicked");*
* }*
* }*
}
So a class that wants to use our CustomOnGUI mechanism has to implement the ICustomGUI interface. That means your class need to have those things:
- a public “int” property GUIDepth
- a public method called CustomOnGUI
- the class need to call “GUIManager.Add(this)” somewhere in Awake / Start in order to have the CustomOnGUI method run.
Note: You don’t need to attach the GUIManager somewhere. It will create an instance automatically which will be marked with DontDestroyOnLoad. So it’s better to not manually attach the GUIManager script somewhere. If you do, make sure the scene where you placed the script in is only loaded once. If you load the scene again you will end up with two instances which would mess everything up.