to get another class hold a reference to a float in another component

Hi,

I am trying to write a class which holds a list of vector2 as a way of storing vertices for a graph which I can call onGUI method and then do some GL drawing onto a render texture and show an oscilloscope type graph

My basic idea is for the Graph class to hold a reference to the the variable passed in and be able to get it’s value.

I’ve sort of got it working with ‘hard’ code but I want to make it more generic.

My poor code at the moment is…

public enum GraphType
{
    Oscilloscope,
    Line,
    Bar
};

public class Graph
{
    public GraphType GraphType;
    public List<Vector2> Vertices;
    public Color Colour;
    public float ValueToFollow;

    public Graph(GraphType graphType, float valueToFollow, Color colour, List<Vector2> initialValues = null)
    {
        GraphType = graphType;
        ValueToFollow = valueToFollow;
        Colour = colour;
        if (initialValues != null)
        {
            Vertices = new List<Vector2>(initialValues);
        }
        else
        {
            Vertices = new List<Vector2>();
        }
    }

    public void Update()
    {
        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);
        }
      
    }

}

and then I would like to 'initialise it by getting a reference to the component and then the variable I want to monitor where GraphWindowTestValues is another component attached to the gameObject.

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 want the Graph class to store a reference to the GraphWindowTestValue.somevalue and ‘automagically’ Update it when it’s Update method is called…

public class GraphWindow : MonoBehaviour
{


//....other stuff
// In the inspector drop the public variable you want to follow into this
    GraphWindowTestValues graphWindowTestValues;

// In Awake
private void Awake()
    {
graphWindowTestValues = GetComponent<GraphWindowTestValues>();

        ListOfGraphs.Add(new Graph(GraphType.Oscilloscope, graphWindowTestValues.SomeValue, Color.red));

// More

// And in FixedUpdate
 private void FixedUpdate()
    {
        if (Time.time >= TimeNextUpdate)
        {
            TimeNextUpdate = Time.time + f_timeInterval;
            foreach(Graph graph in ListOfGraphs)
            {
                graph.AddVertex(graphWindowTestValues.SomeValue); // So this works
                //graph.Update(); // Can I get this to work?
            }
        }
    }

// etc...
}

If you see what I mean?

I apologise in advance for any idiocy on my part but I’m stuck…

Michael

Sorry, I’m having a little trouble following.
Graph doesn’t inherit from MonBehaviour, nor does it have a base class that inherits from Monobehaviour, which means it isn’t going to be attached to a gameobject, which thus means Update is not called by Unity automatically. In this particular case, Update is just a normal method that you can call yourself. Now, you can make it serializable if you want the values to be visible in the inspector, but this still isn’t going to let Update be run by Unity as part of it’s Update loop.

No, You’re right. Graph is a class I use to keep hold of information about a graph I want to draw. Sorry. I know my code is not that clear. The two monobehaviours are GraphWindow and GraphWindowTestValues. The Update in Graph class, I shouldn;t have named it that as it leads to confusion. Give me ten minutes and Ill just upload the whole monolith script.

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

Ah. I see

[c# - Store a reference to a value type? - Stack Overflow](http://c# - Store a reference to a value type? - Stack Overflow)

I think that answers my question.

“You cannot store a reference to a variable in a field or array. The CLR requires that a reference to a variable be in (1) a formal parameter, (2) a local, or (3) the return type of a method. C# supports (1) but not the other two.”