| | using System.Collections.Generic; |
| | using System.Linq; |
| | using UnityEngine; |
| |
|
| | namespace Unity.MLAgents |
| | { |
| | |
| | |
| | |
| | |
| | public class Monitor : MonoBehaviour |
| | { |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | public enum DisplayType |
| | { |
| | Independent, |
| | Proportion |
| | } |
| |
|
| | |
| | |
| | |
| | public static float verticalOffset = 3f; |
| |
|
| | static bool s_IsInstantiated; |
| | static GameObject s_Canvas; |
| | static Dictionary<Transform, Dictionary<string, DisplayValue>> s_DisplayTransformValues; |
| |
|
| | |
| | |
| | |
| | |
| | static Dictionary<Transform, Camera> s_TransformCamera; |
| |
|
| | static Color[] s_BarColors; |
| |
|
| | struct DisplayValue |
| | { |
| | public float time; |
| | public string stringValue; |
| | public float floatValue; |
| | public float[] floatArrayValues; |
| |
|
| | public enum ValueType |
| | { |
| | Float, |
| | FloatarrayIndependent, |
| | FloatarrayProportion, |
| | String |
| | } |
| |
|
| | public ValueType valueType; |
| | } |
| |
|
| | static GUIStyle s_KeyStyle; |
| | static GUIStyle s_ValueStyle; |
| | static GUIStyle s_GreenStyle; |
| | static GUIStyle s_RedStyle; |
| | static GUIStyle[] s_ColorStyle; |
| | static bool s_Initialized; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | public static void Log( |
| | string key, |
| | string value, |
| | Transform target = null, |
| | Camera camera = null) |
| | { |
| | if (!s_IsInstantiated) |
| | { |
| | InstantiateCanvas(); |
| | s_IsInstantiated = true; |
| | } |
| | if (s_Canvas == null) |
| | { |
| | return; |
| | } |
| |
|
| | if (target == null) |
| | { |
| | target = s_Canvas.transform; |
| | } |
| |
|
| | s_TransformCamera[target] = camera; |
| |
|
| | if (!s_DisplayTransformValues.Keys.Contains(target)) |
| | { |
| | s_DisplayTransformValues[target] = |
| | new Dictionary<string, DisplayValue>(); |
| | } |
| |
|
| | var displayValues = |
| | s_DisplayTransformValues[target]; |
| |
|
| | if (value == null) |
| | { |
| | RemoveValue(target, key); |
| | return; |
| | } |
| |
|
| | if (!displayValues.ContainsKey(key)) |
| | { |
| | var dv = new DisplayValue(); |
| | dv.time = Time.timeSinceLevelLoad; |
| | dv.stringValue = value; |
| | dv.valueType = DisplayValue.ValueType.String; |
| | displayValues[key] = dv; |
| | while (displayValues.Count > 20) |
| | { |
| | var max = ( |
| | displayValues |
| | .Aggregate((l, r) => l.Value.time < r.Value.time ? l : r) |
| | .Key |
| | ); |
| | RemoveValue(target, max); |
| | } |
| | } |
| | else |
| | { |
| | var dv = displayValues[key]; |
| | dv.stringValue = value; |
| | dv.valueType = DisplayValue.ValueType.String; |
| | displayValues[key] = dv; |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | public static void Log( |
| | string key, |
| | float value, |
| | Transform target = null, |
| | Camera camera = null) |
| | { |
| | if (!s_IsInstantiated) |
| | { |
| | InstantiateCanvas(); |
| | s_IsInstantiated = true; |
| | } |
| |
|
| | if (target == null) |
| | { |
| | target = s_Canvas.transform; |
| | } |
| |
|
| | s_TransformCamera[target] = camera; |
| |
|
| | if (!s_DisplayTransformValues.Keys.Contains(target)) |
| | { |
| | s_DisplayTransformValues[target] = new Dictionary<string, DisplayValue>(); |
| | } |
| |
|
| | var displayValues = s_DisplayTransformValues[target]; |
| |
|
| | if (!displayValues.ContainsKey(key)) |
| | { |
| | var dv = new DisplayValue(); |
| | dv.time = Time.timeSinceLevelLoad; |
| | dv.floatValue = value; |
| | dv.valueType = DisplayValue.ValueType.Float; |
| | displayValues[key] = dv; |
| | while (displayValues.Count > 20) |
| | { |
| | var max = ( |
| | displayValues.Aggregate((l, r) => l.Value.time < r.Value.time ? l : r).Key); |
| | RemoveValue(target, max); |
| | } |
| | } |
| | else |
| | { |
| | var dv = displayValues[key]; |
| | dv.floatValue = value; |
| | dv.valueType = DisplayValue.ValueType.Float; |
| | displayValues[key] = dv; |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | public static void Log( |
| | string key, |
| | float[] value, |
| | Transform target = null, |
| | DisplayType displayType = DisplayType.Independent, |
| | Camera camera = null |
| | ) |
| | { |
| | if (!s_IsInstantiated) |
| | { |
| | InstantiateCanvas(); |
| | s_IsInstantiated = true; |
| | } |
| |
|
| | if (target == null) |
| | { |
| | target = s_Canvas.transform; |
| | } |
| |
|
| | s_TransformCamera[target] = camera; |
| |
|
| | if (!s_DisplayTransformValues.Keys.Contains(target)) |
| | { |
| | s_DisplayTransformValues[target] = new Dictionary<string, DisplayValue>(); |
| | } |
| |
|
| | var displayValues = s_DisplayTransformValues[target]; |
| |
|
| | if (!displayValues.ContainsKey(key)) |
| | { |
| | var dv = new DisplayValue(); |
| | dv.time = Time.timeSinceLevelLoad; |
| | dv.floatArrayValues = value; |
| | if (displayType == DisplayType.Independent) |
| | { |
| | dv.valueType = DisplayValue.ValueType.FloatarrayIndependent; |
| | } |
| | else |
| | { |
| | dv.valueType = DisplayValue.ValueType.FloatarrayProportion; |
| | } |
| |
|
| | displayValues[key] = dv; |
| | while (displayValues.Count > 20) |
| | { |
| | var max = ( |
| | displayValues.Aggregate((l, r) => l.Value.time < r.Value.time ? l : r).Key); |
| | RemoveValue(target, max); |
| | } |
| | } |
| | else |
| | { |
| | var dv = displayValues[key]; |
| | dv.floatArrayValues = value; |
| | if (displayType == DisplayType.Independent) |
| | { |
| | dv.valueType = DisplayValue.ValueType.FloatarrayIndependent; |
| | } |
| | else |
| | { |
| | dv.valueType = DisplayValue.ValueType.FloatarrayProportion; |
| | } |
| |
|
| | displayValues[key] = dv; |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | public static void RemoveValue(Transform target, string key) |
| | { |
| | if (target == null) |
| | { |
| | target = s_Canvas.transform; |
| | } |
| |
|
| | if (s_DisplayTransformValues.Keys.Contains(target)) |
| | { |
| | if (s_DisplayTransformValues[target].ContainsKey(key)) |
| | { |
| | s_DisplayTransformValues[target].Remove(key); |
| | if (s_DisplayTransformValues[target].Keys.Count == 0) |
| | { |
| | s_DisplayTransformValues.Remove(target); |
| | } |
| | } |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | public static void RemoveAllValues(Transform target) |
| | { |
| | if (target == null) |
| | { |
| | target = s_Canvas.transform; |
| | } |
| |
|
| | if (s_DisplayTransformValues.Keys.Contains(target)) |
| | { |
| | s_DisplayTransformValues.Remove(target); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | public static void SetActive(bool active) |
| | { |
| | if (!s_IsInstantiated) |
| | { |
| | InstantiateCanvas(); |
| | s_IsInstantiated = true; |
| | } |
| |
|
| | if (s_Canvas != null) |
| | { |
| | s_Canvas.SetActive(active); |
| | } |
| | } |
| |
|
| | |
| | static void InstantiateCanvas() |
| | { |
| | s_Canvas = GameObject.Find("AgentMonitorCanvas"); |
| | if (s_Canvas == null) |
| | { |
| | s_Canvas = new GameObject(); |
| | s_Canvas.name = "AgentMonitorCanvas"; |
| | s_Canvas.AddComponent<Monitor>(); |
| | } |
| |
|
| | s_DisplayTransformValues = new Dictionary<Transform, |
| | Dictionary<string, DisplayValue>>(); |
| |
|
| | s_TransformCamera = new Dictionary<Transform, Camera>(); |
| | } |
| |
|
| | void OnGUI() |
| | { |
| | if (!s_Initialized) |
| | { |
| | Initialize(); |
| | s_Initialized = true; |
| | } |
| |
|
| | var toIterate = s_DisplayTransformValues.Keys.ToList(); |
| | foreach (var target in toIterate) |
| | { |
| | if (target == null) |
| | { |
| | s_DisplayTransformValues.Remove(target); |
| | continue; |
| | } |
| |
|
| | |
| | var cam = s_TransformCamera[target]; |
| | if (cam == null) |
| | { |
| | cam = Camera.main; |
| | } |
| |
|
| | var widthScaler = (Screen.width / 1000f); |
| | var keyPixelWidth = 100 * widthScaler; |
| | var keyPixelHeight = 20 * widthScaler; |
| | var paddingWidth = 10 * widthScaler; |
| |
|
| | var scale = 1f; |
| | var origin = new Vector3( |
| | Screen.width / 2.0f - keyPixelWidth, Screen.height); |
| | if (!(target == s_Canvas.transform)) |
| | { |
| | var camTransform = cam.transform; |
| | var position = target.position; |
| | var cam2Obj = position - camTransform.position; |
| | scale = Mathf.Min( |
| | 1, |
| | 20f / (Vector3.Dot(cam2Obj, camTransform.forward))); |
| | var worldPosition = cam.WorldToScreenPoint( |
| | position + new Vector3(0, verticalOffset, 0)); |
| | origin = new Vector3( |
| | worldPosition.x - keyPixelWidth * scale, Screen.height - worldPosition.y); |
| | } |
| |
|
| | keyPixelWidth *= scale; |
| | keyPixelHeight *= scale; |
| | paddingWidth *= scale; |
| | s_KeyStyle.fontSize = (int)(keyPixelHeight * 0.8f); |
| | if (s_KeyStyle.fontSize < 2) |
| | { |
| | continue; |
| | } |
| |
|
| |
|
| | var displayValues = s_DisplayTransformValues[target]; |
| |
|
| | var index = 0; |
| | var orderedKeys = displayValues.Keys.OrderBy(x => -displayValues[x].time); |
| | foreach (var key in orderedKeys) |
| | { |
| | s_KeyStyle.alignment = TextAnchor.MiddleRight; |
| | GUI.Label( |
| | new Rect( |
| | origin.x, origin.y - (index + 1) * keyPixelHeight, |
| | keyPixelWidth, keyPixelHeight), |
| | key, |
| | s_KeyStyle); |
| | float[] vals; |
| | GUIStyle s; |
| | switch (displayValues[key].valueType) |
| | { |
| | case DisplayValue.ValueType.String: |
| | s_ValueStyle.alignment = TextAnchor.MiddleLeft; |
| | GUI.Label( |
| | new Rect( |
| | origin.x + paddingWidth + keyPixelWidth, |
| | origin.y - (index + 1) * keyPixelHeight, |
| | keyPixelWidth, keyPixelHeight), |
| | displayValues[key].stringValue, |
| | s_ValueStyle); |
| | break; |
| | case DisplayValue.ValueType.Float: |
| | var sliderValue = displayValues[key].floatValue; |
| | sliderValue = Mathf.Min(1f, sliderValue); |
| | s = s_GreenStyle; |
| | if (sliderValue < 0) |
| | { |
| | sliderValue = Mathf.Min(1f, -sliderValue); |
| | s = s_RedStyle; |
| | } |
| |
|
| | GUI.Box( |
| | new Rect( |
| | origin.x + paddingWidth + keyPixelWidth, |
| | origin.y - (index + 0.9f) * keyPixelHeight, |
| | keyPixelWidth * sliderValue, keyPixelHeight * 0.8f), |
| | GUIContent.none, |
| | s); |
| | break; |
| |
|
| | case DisplayValue.ValueType.FloatarrayIndependent: |
| | const float histWidth = 0.15f; |
| | vals = displayValues[key].floatArrayValues; |
| | for (var i = 0; i < vals.Length; i++) |
| | { |
| | var value = Mathf.Min(vals[i], 1); |
| | s = s_GreenStyle; |
| | if (value < 0) |
| | { |
| | value = Mathf.Min(1f, -value); |
| | s = s_RedStyle; |
| | } |
| |
|
| | GUI.Box( |
| | new Rect( |
| | origin.x + paddingWidth + keyPixelWidth + |
| | (keyPixelWidth * histWidth + paddingWidth / 2) * i, |
| | origin.y - (index + 0.1f) * keyPixelHeight, |
| | keyPixelWidth * histWidth, -keyPixelHeight * value), |
| | GUIContent.none, |
| | s); |
| | } |
| |
|
| | break; |
| |
|
| | case DisplayValue.ValueType.FloatarrayProportion: |
| | var valsSum = 0f; |
| | var valsCum = 0f; |
| | vals = displayValues[key].floatArrayValues; |
| | foreach (var f in vals) |
| | { |
| | valsSum += Mathf.Max(f, 0); |
| | } |
| |
|
| | if (valsSum < float.Epsilon) |
| | { |
| | Debug.LogError( |
| | $"The Monitor value for key {key} " + |
| | "must be a list or array of " + |
| | "positive values and cannot " + |
| | "be empty."); |
| | } |
| | else |
| | { |
| | for (var i = 0; i < vals.Length; i++) |
| | { |
| | var value = Mathf.Max(vals[i], 0) / valsSum; |
| | GUI.Box( |
| | new Rect( |
| | origin.x + paddingWidth + |
| | keyPixelWidth + keyPixelWidth * valsCum, |
| | origin.y - (index + 0.9f) * keyPixelHeight, |
| | keyPixelWidth * value, keyPixelHeight * 0.8f), |
| | GUIContent.none, |
| | s_ColorStyle[i % s_ColorStyle.Length]); |
| | valsCum += value; |
| | } |
| | } |
| |
|
| | break; |
| | } |
| |
|
| | index++; |
| | } |
| | } |
| | } |
| |
|
| | |
| | void Initialize() |
| | { |
| | s_KeyStyle = GUI.skin.label; |
| | s_ValueStyle = GUI.skin.label; |
| | s_ValueStyle.clipping = TextClipping.Overflow; |
| | s_ValueStyle.wordWrap = false; |
| | s_BarColors = new[] |
| | { |
| | Color.magenta, |
| | Color.blue, |
| | Color.cyan, |
| | Color.green, |
| | Color.yellow, |
| | Color.red |
| | }; |
| | s_ColorStyle = new GUIStyle[s_BarColors.Length]; |
| | for (var i = 0; i < s_BarColors.Length; i++) |
| | { |
| | var texture = new Texture2D(1, 1, TextureFormat.ARGB32, false); |
| | texture.SetPixel(0, 0, s_BarColors[i]); |
| | texture.Apply(); |
| | var staticRectStyle = new GUIStyle(); |
| | staticRectStyle.normal.background = texture; |
| | s_ColorStyle[i] = staticRectStyle; |
| | } |
| |
|
| | s_GreenStyle = s_ColorStyle[3]; |
| | s_RedStyle = s_ColorStyle[5]; |
| | } |
| | } |
| | } |
| |
|