using System.Collections.Generic;
using UnityEngine;
public enum GraphType
{
Oscilloscope,
Line,
Bar
};
public class Graph
{
public GraphType GraphType;
public List<Vector2> Vertices;
public Color Colour;
public Component component;
public Graph(GraphType graphType, Color colour, List<Vector2> initialValues = null)
{
GraphType = graphType;
Colour = colour;
if (initialValues != null)
{
Vertices = new List<Vector2>(initialValues);
}
else
{
Vertices = new List<Vector2>();
}
}
public void Update(float valueToFollow)
{
AddVertex(valueToFollow);
Debug.Log(valueToFollow);
}
public void AddVertex(float x, float y)
{
Vertices.Add(new Vector2(x, y));
}
public void AddVertex(Vector2 vertex)
{
Vertices.Add(vertex);
}
public void AddVertex(float value)
{
AddVertex(-1, value);
if (Vertices.Count > 512)
{
Vertices.RemoveAt(0);
}
}
}
// Attach this as a component to the object you want to show a value of
public class GraphWindow : MonoBehaviour
{
private Rect windowRect = new Rect(400, 300, 512, 512);
[SerializeField]
public List<Graph> ListOfGraphs = new();
// Rendering
private Material lineMaterial;
private Texture2D _texture;
private RenderTexture _renderTexture;
// Graph Type
private int graphTypeSelection;
private string[] graphTypeStrings = { "Oscilloscope", "Line", "Bar" };
// Graph Options
private bool drawAxis;
private bool drawGrid;
// Texture size
private bool showTextureSizeOptions;
private Vector2Int[] textureSizeOptions = { new Vector2Int(256, 256),
new Vector2Int(512, 512),
new Vector2Int(300, 200),
new Vector2Int(600, 300)
};
private int textureSizeSelection = 1;
private int oldTextureSizeSelection = 1;
private string[] textureSizeStrings = { "256, 256", "512, 512", "300, 200", "600, 300" };
private bool showUpdateInterval;
// In the inspector drop the public variable you want to follow into this
GraphWindowTestValues graphWindowTestValues;
float followthis;
private float f_timeInterval;
private string s_timeInterval = "0.01";
private float TimeNextUpdate;
private List<Vector2> Values = new();
private List<float> Oscilloscope0 = new();
private List<float> Oscilloscope1 = new();
private List<float> Oscilloscope2 = new();
Color myGray = new Color(0.1f, 0.1f, 0.1f, 0.5f);
private float xFactor;
private float yFactor;
private void Awake()
{
lineMaterial = new Material(Shader.Find("Hidden/Internal-Colored"));
lineMaterial.hideFlags = HideFlags.HideAndDontSave;
// Turn on alpha blending
lineMaterial.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
lineMaterial.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
// Turn backface culling off
lineMaterial.SetInt("_Cull", (int)UnityEngine.Rendering.CullMode.Off);
// Turn off depth writes
lineMaterial.SetInt("_ZWrite", 0);
CreateTexture2D(512, 512);
CreateRenderTexture(512, 512);
Values.Add(new Vector2(0, 0));
Values.Add(new Vector2(100, 100));
Values.Add(new Vector2(200, 100));
Values.Add(new Vector2(200, 200));
Values.Add(new Vector2(1000, 100));
// At the moment have to hard code which values you want to follow
// In Oscilloscope mode you can follow 3 values
// must be a way of doing this somehow
graphWindowTestValues = GetComponent<GraphWindowTestValues>();
followthis = GetComponent<GraphWindowTestValues>().SomeValue;
ListOfGraphs.Add(new Graph(GraphType.Oscilloscope, Color.red));
}
private void FixedUpdate()
{
if (Time.time >= TimeNextUpdate)
{
TimeNextUpdate = Time.time + f_timeInterval;
foreach(Graph graph in ListOfGraphs)
{
graph.AddVertex(graphWindowTestValues.SomeValue);
//graph.Update(followthis);
}
}
}
public void CreateTexture2D(int width, int height)
{
if (_texture != null)
{
Destroy(_texture);
}
_texture = new Texture2D(width, height, TextureFormat.ARGB32, false);
_texture.hideFlags = HideFlags.DontSave;
}
public void CreateRenderTexture(int width, int height)
{
if (_renderTexture != null)
{
_renderTexture.Release();
Destroy(_renderTexture);
}
_renderTexture = new RenderTexture(width, height, 32, RenderTextureFormat.ARGB32);
_renderTexture.hideFlags = HideFlags.DontSave;
_renderTexture.Create();
}
private void OnGUI()
{
// Screen size can change
xFactor = (float)Screen.width / _texture.width;
yFactor = (float)Screen.height / _texture.height;
windowRect = GUI.Window(GetHashCode(), windowRect, WindowFunc, "Graph of " + gameObject.name);
}
private void WindowFunc(int WindowID)
{
GUILayout.BeginHorizontal();
showTextureSizeOptions = GUILayout.Toggle(showTextureSizeOptions, "Texture Size Options");
if (showTextureSizeOptions)
{
textureSizeSelection = GUILayout.SelectionGrid(textureSizeSelection, textureSizeStrings, 2);
if (textureSizeSelection != oldTextureSizeSelection)
{
oldTextureSizeSelection = textureSizeSelection;
CreateTexture2D(textureSizeOptions[textureSizeSelection].x, textureSizeOptions[textureSizeSelection].y);
CreateRenderTexture(textureSizeOptions[textureSizeSelection].x, textureSizeOptions[textureSizeSelection].y);
}
}
drawAxis = GUILayout.Toggle(drawAxis, "Draw Axis");
drawGrid = GUILayout.Toggle(drawGrid, "Draw Grid");
GUILayout.EndHorizontal();
showUpdateInterval = GUILayout.Toggle(showUpdateInterval, "Update Interval");
if (showUpdateInterval)
{
GUILayout.BeginHorizontal();
s_timeInterval = GUILayout.TextField(s_timeInterval, 8);
float.TryParse(s_timeInterval, out f_timeInterval);
GUILayout.Label("Update Interval (s) : " + f_timeInterval.ToString());
GUILayout.EndHorizontal();
}
graphTypeSelection = GUILayout.SelectionGrid(graphTypeSelection, graphTypeStrings, graphTypeStrings.Length);
// GUILayout.Label("RenderTexture : " + _renderTexture.width + " , " + _renderTexture.height);
// GUILayout.Label("Texture : " + _texture.width + " , " + _texture.height);
// GUILayout.Label("x and y Factors: " + xFactor + ", " + yFactor);
GUILayout.Box("", GUILayout.ExpandHeight(true), GUILayout.ExpandWidth(true));
GUI.DrawTexture(GUILayoutUtility.GetLastRect(), _texture, ScaleMode.StretchToFill);
if (Event.current.type == EventType.Repaint)
{
// We do the actual GL graph drawing during this event
DoDrawing();
}
// Resize the window with the right mouse button
Event e = Event.current;
if (e.button == 1 && GUILayoutUtility.GetLastRect().Contains(e.mousePosition))
{
if (windowRect.width < 200) { windowRect.width = 200; }
if (windowRect.height < 200) { windowRect.height = 200; }
windowRect = new Rect( windowRect.x, windowRect.y,
windowRect.width + Event.current.delta.x,
windowRect.height + Event.current.delta.y);
}
else
{
GUI.DragWindow();
}
}
// Draw into a render texture and then copy over the result to our texture....
void DoDrawing()
{
// Remember currently active render texture
RenderTexture currentActiveRT = RenderTexture.active;
// Set the supplied RenderTexture as the active one
RenderTexture.active = _renderTexture;
GL.Clear(true, true, myGray);
// Draw the graph to the render texture
GL.PushMatrix();
lineMaterial.SetPass(0);
GL.LoadPixelMatrix();
// Draw the value lines of the graph
// Draw a debug cross in the middle of the texture...
//GL.Begin(GL.LINES);
//GL.Color(Color.red);
//GL.Vertex3(_renderTexture.width / 2 * xFactor, 0, 0);
//GL.Vertex3(_renderTexture.width / 2 * xFactor, _renderTexture.height * yFactor, 0);
//GL.Vertex3(0, _renderTexture.height / 2 * yFactor, 0);
//GL.Vertex3(_renderTexture.width * xFactor, _renderTexture.height / 2 * yFactor , 0);
//GL.End();
if (drawGrid)
{
GL.Begin(GL.LINES);
GL.Color(myGray);
for (int i = 0; i < _renderTexture.width; i += 20)
{
GL.Vertex3(i * xFactor, 0, 0);
GL.Vertex3(i * xFactor, _renderTexture.height * yFactor, 0);
}
for (int i = 0; i < _renderTexture.height; i += 20)
{
GL.Vertex3(0, i * yFactor, 0);
GL.Vertex3(_renderTexture.width * xFactor, i * yFactor, 0);
}
GL.End();
}
if (drawAxis)
{
GLDrawLine(8, 8, _renderTexture.width, 8, Color.white);
GLDrawLine(8, 8, 8, _renderTexture.height, Color.white);
}
foreach(Graph graph in ListOfGraphs)
{
GLDrawLineList(graph.Vertices, graph.Colour);
}
if (graphTypeSelection == (int)GraphType.Oscilloscope)
{
GLDrawLineList(Oscilloscope0, Color.cyan);
GLDrawLineList(Oscilloscope1, Color.green);
GLDrawLineList(Oscilloscope2, Color.blue);
}
else
{
GL.Begin(GL.LINES);
for (int i = 0; i < Values.Count; i++)
{
if (i + 1 == Values.Count)
{
// Got to the end of the list
break;
}
GL.Color(Color.green);
GL.Vertex3(Values[i].x * xFactor, Values[i].y * yFactor, 0);
GL.Vertex3(Values[i + 1].x * xFactor, Values[i + 1].y * yFactor, 0);
}
GL.End();
}
GL.PopMatrix();
// Graphics.CopyTexture is GPU based and faster
// Make sure the texture and the renderTexture are the same format and miplevels (see creation)
Graphics.CopyTexture(_renderTexture, _texture);
// Restore previously active render texture
RenderTexture.active = currentActiveRT;
}
void GLDrawLine(Vector3 _start, Vector3 _end, Color _colour)
{
GL.Begin(GL.LINES);
GL.Color(_colour);
GL.Vertex3(_start.x * xFactor, _start.y * yFactor, 0);
GL.Vertex3(_end.x * xFactor, _end.y * yFactor, 0);
GL.End();
}
void GLDrawLine(float x1, float y1, float x2, float y2, Color _colour)
{
GL.Begin(GL.LINES);
GL.Color(_colour);
GL.Vertex3(x1 * xFactor, y1 * yFactor, 0);
GL.Vertex3(x2 * xFactor, y2 * yFactor, 0);
GL.End();
}
void GLDrawLineList(List<float> _list, Color _colour)
{
GL.Begin(GL.LINE_STRIP);
GL.Color(_colour);
for (int i = _list.Count - 1; i > 0; i--)
{
GL.Vertex3(i * xFactor, _list[i] * yFactor, 0);
}
GL.End();
}
void GLDrawLineList(List<Vector2> _list, Color _colour)
{
GL.Begin(GL.LINE_STRIP);
GL.Color(_colour);
for (int i = _list.Count - 1; i > 0; i--)
{
GL.Vertex3(i * xFactor, _list[i].y * yFactor, 0);
}
GL.End();
}
}
and then GraphwindowTestValues
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GraphWindowTestValues : MonoBehaviour
{
public float SomeValue = 1.0f;
public float anotherValue = 2.0f;
public float aThirdValue = 3.0f;
// Update is called once per frame
void Update()
{
SomeValue = 100 * (1 + Mathf.Sin(Time.time / 2));
anotherValue = 200 + 100 * Mathf.Sin(Time.time / 3);
aThirdValue = 200 + 100 * Mathf.Cos(Time.time / 4);
}
}
I realise I shouldn;t have called Graph.Update() that but something else.
There is lot’s of zombie code in what I’ve posted as I’ve been trying various things. The main issue I want to try and solve is for each instance of Graph to hold a reference to the variable it is following so I don’t have to do it in the main GraphWindow class.
I hope you see what I mean. And thank you for replying initially!
Michael