resizing GUI elements through GUI matrix

I am currently looking into rescaling my GUI systems based on screen size (considering that in web-player with things like zoom factor, and such building to a resolution is rather difficult), but have been unlucky so far. the most promissing thing I have found was here, but I don’t know how to arrive at the position vector, and the quaternion rotation.

using this code:

using UnityEngine;
using System.Collections;

public class GUIMatrixTest : MonoBehaviour {

	private Rect box0 = new Rect(0,0,0,0);
	private int tWidth;
	private int tHeight;
	
	public string msg = 
		"Here is a text box for testing against." + '


+ “It has text in it, and allows us the” + ’

+ “see results.”;
// Use this for initialization
void Start () {
tWidth = Screen.width;
tHeight = Screen.height;
}

	// Update is called once per frame
	void Update () {
		if(tWidth != Screen.width || tHeight != Screen.height){
			Matrix4x4 mat = GUI.matrix;
			Vector4 tempV = Vector4.zero;
			
			Debug.Log("before: " + mat.ToString());
			
			Vector3 vec0 = Vector3.zero;
			tempV = mat.GetRow(0);
			vec0.x = tempV.x; vec0.y = tempV.y; vec0.z = tempV.z;
			
			Quaternion vec1 = Quaternion.identity;
			tempV = mat.GetRow(1);
			vec1.x = tempV.x; vec1.y = tempV.y; vec1.z = tempV.z; vec1.w = tempV.w;
			
			Vector3 vec2 = Vector3.zero;
			vec2.x = 800/Screen.width; vec2.y = 600/Screen.height; vec2.y = 1;
			mat.SetTRS(vec0, vec1, vec2);
			
			Debug.Log("after: " + mat.ToString());
			
			GUI.matrix = mat;
			
			tWidth = Screen.width;
			tHeight = Screen.height;
		}
	}
	
	public void OnGUI(){
		box0.width = 280; box0.height = 100;
		GUI.Box(box0, msg);
	}
}

I get the error “Ignoring invalid matrix assigned to GUI.matrix - the matrix needs to be invertible. Did you scale by 0 on z-axis?”

any help in either correcting this, or a different solution would be appreciated.

the best solution for this is to not modify the GUI.matrix directly, but instead to use GUIUtility.ScaleAroundPivot though this can look quite clunky when sitting in multiple places in you OnGUI(), and the scaling is always relative to the pivot

(for example if the pivot is screen center, and Vector is (2,2) then everything will be shifted/scaled by a factor of 2 from the screen center)

the best suggestion that I can make is do your scaling per item this way you can get cleaner results in terms of placement, and have you pivot point relative the rect you working with. since it is rather difficult to extend the GUI class it would be best to just have a static extension class do the work. though this might either require you to create sudo-overloads per GUI function, or to have this helper simply maintain the original Matrix, scale the matrix, and then be able to reset it.

the first implementation would look something like this:

    //...
	public static void Label(Rect rect, string text, Vector2 scale, TextAnchor pivot){
		Label(rect, new GUIContent(text), "label", scale, pivot);
	}
	public static void Label(Rect rect, string text, GUIStyle style, Vector2 scale, TextAnchor pivot){
		Label(rect, new GUIContent(text), style, scale, pivot);
	}
	
	public static void Label(Rect rect, Texture texture, Vector2 scale, TextAnchor pivot){
		Label(rect, new GUIContent(texture), "label", scale, pivot);
	}
	public static void Label(Rect rect, Texture texture, GUIStyle style, Vector2 scale, TextAnchor pivot){
		Label(rect, new GUIContent(texture), style, scale, pivot);
	}

	public static void Label(Rect rect, GUIContent content, Vector2 scale, TextAnchor pivot){
		Label(rect, content, "label", scale, pivot);
	}
	public static void Label(Rect rect, GUIContent content, GUIStyle style, Vector2 scale, TextAnchor pivot){
		Matrix4x4 backupMat = GUI.matrix;
		Vector2 pivotPoint = PivotVector(ref rect, pivot);
		GUIUtility.ScaleAroundPivot(scale, pivotPoint);
		GUI.Label(rect, content, style);
		GUI.matrix = backupMat;
	}
    //...
	public static Vector2 PivotVector(ref Rect rect, TextAnchor pivot){
		Vector2 pivotPoint = Vector2.zero;
		//set to the top left corner of the thing to be scaled
		pivotPoint.x = rect.x; pivotPoint.y = rect.y;
		switch(pivot){
		case TextAnchor.UpperCenter:	pivotPoint.x += rect.width/2;									break;
		case TextAnchor.UpperRight:		pivotPoint.x += rect.width;										break;
		case TextAnchor.MiddleLeft:										pivotPoint.y += rect.height/2;	break;
		case TextAnchor.MiddleCenter:	pivotPoint.x += rect.width/2;	pivotPoint.y += rect.height/2;	break;
		case TextAnchor.MiddleRight:	pivotPoint.x += rect.width;		pivotPoint.y += rect.height/2;	break;
		case TextAnchor.LowerLeft:										pivotPoint.y += rect.height;	break;
		case TextAnchor.LowerCenter:	pivotPoint.x += rect.width/2;	pivotPoint.y += rect.height;	break;
		case TextAnchor.LowerRight:		pivotPoint.x += rect.width;		pivotPoint.y += rect.height;	break;
		}
		return pivotPoint;
	}
    //...

I have not written the other implementation, but it should be readily practical to utilize.

the above code is functional, but there is a direct exception of not behaving between of a GUI.BeginGroup(), and GUI.EndGroup(), but this can be superseded by creating a BeginGroup(), but don’t expect to be able to use the ScaleAroundPivot effectively, but GUILayout is still a viable option inside of the group, but this uses full space filling, and you may have to play with padding directly.