- Home /
How to build a line drawing method called in Update, similar to Debug.DrawLine ()
I want to create a method that will draw lines and other shapes.
The method should be called e.g. in Update () and act like the Debug.DrawLine () method.
I don't want to use OnDrawGizmos (), OnRenderObject () etc.
I have no idea how to write it down.
Answer by Bunny83 · Sep 12, 2020 at 11:05 PM
You can not draw anything from Update since Update runs before the rendering even starts. Debug.DrawLine also does not draw any lines, it just queues the line information to be drawn during rendering. You can do something similar yourself with a singleton monobehaviour using OnRenderObject to draw all the queued lines. There's no way around that. When the rendering actually starts, the first actions is usually to clear the screen. So even when you could render something directly in Update, it would be erased when the actual scene renders. You can directly draw to the screen from inside OnGUI when the event type is "Repaint". This is usually a good point to draw such lines since that happens after the normal scene rendering.
What approach might be the best depends on what you actually need this for. If you really just want to draw / redraw the line(s) every frame you could just use the GL class to draw all the queued lines on the fly. If you want to draw them permanentally or for a larger period of time, creating a Mesh with topology "lines" might be a better approach. In any case you would use a list of queue to accumulate all the line information during the update cycle in order to draw them during the rendering phase. As I said you have to use either OnRenderObject, OnRenderImage, OnGUI or a coroutine that waits for WaitForEndOfFrame.
My old simulation to show how FixedUpdate works is entirely created in OnGUI. All lines are drawn using the GL class.
edit
I quickly created a simple example script:
using System.Collections.Generic;
using UnityEngine;
public class Lines : MonoBehaviour
{
public class Line
{
public Vector3 startPos;
public Vector3 endPos;
public Color color;
public float timeout;
}
private static Lines m_Instance = null;
private static Lines GetInstance()
{
if (m_Instance == null)
{
m_Instance = new GameObject("Lines").AddComponent<Lines>();
m_Instance.hideFlags = HideFlags.DontSave;
DontDestroyOnLoad(m_Instance.gameObject);
}
return m_Instance;
}
private List<Line> m_Lines = new List<Line>();
public Material lineMaterial;
private void Awake()
{
lineMaterial = new Material(Shader.Find("Legacy Shaders/Particles/Alpha Blended"));
}
private void OnRenderObject()
{
GL.PushMatrix();
GL.LoadIdentity();
var cam = Camera.current;
var matrix = cam.projectionMatrix * cam.worldToCameraMatrix;
GL.LoadProjectionMatrix(matrix);
GL.modelview = Matrix4x4.identity;
GL.Begin(GL.LINES);
lineMaterial.SetPass(0);
float currentTime = Time.time;
for(int i = m_Lines.Count - 1; i >= 0; i--)
{
var line = m_Lines[i];
GL.Color(line.color);
GL.Vertex(line.startPos);
GL.Vertex(line.endPos);
if (currentTime > line.timeout)
m_Lines.RemoveAt(i);
}
GL.End();
GL.PopMatrix();
}
public static void Add(Vector3 aStart, Vector3 aEnd, Color aColor, float aDuration = 0)
{
var line = new Line
{
startPos = aStart,
endPos = aEnd,
color = aColor,
timeout = 0
};
if (aDuration > 0)
line.timeout = Time.time + aDuration;
GetInstance().m_Lines.Add(line);
}
}
This is a singleton class that will instantiate itself. It creates a material with the legacy alpha blended particle shader since that shader is one of the few that actually uses vertex colors. You can use Lines.Add(start, end, color);
from anywhere to queue a single line for rendering. This line will be drawn only once and will be removed the next frame. You can also add a duration value which will keep the line for the specified in-game time.
Here's a simple example usage:
using UnityEngine;
public class TestScript : MonoBehaviour
{
void Update()
{
var p = transform.position;
float radius = 10f;
Lines.Add(p+Random.insideUnitSphere * radius, p+Random.insideUnitSphere * radius, new Color(1,0,0,0.5f), 5);
}
}
This will create a new random line every frame within a 10 unit radius around the gameobject this script is attached to. Each line will last for 5 seconds. At a framerate of 60 fps this would result in about 300 active lines after 5 seconds.
Since we use OnRenderObject and since we use Camera.current we will also see those lines in the scene view. If you use a different drawing method (for example OnGUI) you may only see them in the game view.
Thank you. So that it is easy to pass parameters to the visualization method.
In the meantime, I managed to make such a class.
Answer by davidcox70 · Sep 12, 2020 at 06:25 PM
Use Line Renderer:
https://docs.unity3d.com/Manual/class-LineRenderer.html
Basically add a LineRenderer component to a game object. You can then add points to it either in the editor or via script. There are also a lot of other controls to graduate line colors, thicknesses etc.
Hope that helps. DC
I know how Linerenderer works. I want to draw lines with the Handles, Gizmos, and GL classes, but call them from the Update () method. Like Debug.DrawLine ()
Answer by unityBerserker · Sep 13, 2020 at 05:10 PM
Here is solution for drawing gizmos from everywhere. Below is a base class for drawing gizmos. We derive from this class to make an appropriate script. Script place anywhere (except Editor folder)
using UnityEngine;
using UnityEngine.Events;
[DefaultExecutionOrder(-5000)]
[ExecuteInEditMode]
public class GizmosFromEveryWhereManager : MonoBehaviour
{
public static DrawingEvent gizmosDrawingEvents = new DrawingEvent();
void OnDrawGizmos() => gizmosDrawingEvents?.Invoke();
void OnDisable() => RemoveAllListeners();
void OnApplicationQuit() => RemoveAllListeners();
void FixedUpdate() => gizmosDrawingEvents.gizmoF.RemoveAllListeners();
void Update() => DrawingEvent.gizmoU.RemoveAllListeners();
static void RemoveAllListeners() => gizmosDrawingEvents.RemoveAllListeners();
private static GizmosFromEveryWhereManager _instance;
static void CreateInstanceIfClassInstanceNotExist()
{
if (_instance == null)
{
_instance = FindObjectOfType<GizmosFromEveryWhereManager>();
if (_instance == null)
{
GameObject gO = new GameObject();
gO.name = $"[{typeof(GizmosFromEveryWhereManager).Name}]";;
_instance = gO.AddComponent<GizmosFromEveryWhereManager>();
}
}
}
public class DrawingEvent : UnityEvent
{
public UnityEvent gizmoF = new UnityEvent();
public static UnityEvent gizmoU = new UnityEvent();
public void Invoke()
{
gizmoF.Invoke();
gizmoU.Invoke();
}
public void AddListener(UnityAction call)
{
CreateInstanceIfClassInstanceNotExist();
if (Time.inFixedTimeStep)
{
gizmoF.AddListener(call);
}
else
{
gizmoU.AddListener(call);
}
}
public void RemoveAllListeners()
{
gizmoF.RemoveAllListeners();
gizmoU.RemoveAllListeners();
}
}
}
Now we define derived class and we define methods to draw gizmos.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DebugExtentions : GizmosFromEveryWhereManager
{
public static void DrawGreenCube()
{
gizmosDrawingEvents.AddListener(() =>
{
Gizmos.color = Color.green;
Gizmos.DrawCube(Vector3.zero, new Vector3(2, 2, 2));
});
}
public static void DrawRedCube()
{
gizmosDrawingEvents.AddListener(() =>
{
Gizmos.color = Color.red;
Gizmos.DrawCube(Vector3.zero, new Vector3(4, 2, 4));
});
}
}
Now we can draw gizmos from everywhere. From Update(), FixedUpdate() e.c.t.. Below example script.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
public class Example : MonoBehaviour
{
void Update() => DebugExtentions.DrawGreenCube();
void FixedUpdate() => DebugExtentions.DrawRedCube();
void OnDrawGizmos()
{
// Draw yellow cube
Gizmos.color = Color.yellow;
Gizmos.DrawCube(Vector3.up*3+Vector3.right*Mathf.Repeat(Time.realtimeSinceStartup,1), new Vector3(1, 1, 1));
}
}
Your answer
Follow this Question
Related Questions
Force Render 0 Answers
Render Line Trailing Player 3 Answers
Creating a arrow string that can dynamically re size itself. 0 Answers
using openGL to render simple line 1 Answer