When developing a game with Unity, gizmo drawing functions such as Gizmos.DrawWireSphere and Gizmos.DrawLine are indispensable in order to visually check the character's enemy search range, AI patrol route, physical collision detection, etc. However, have you ever encountered the following problems? ``Although the gizmo line is clearly visible in the Scene view, nothing is displayed when you play the game and view it in the Game view'' ``The range line for debugging does not appear at all on an actual machine built for a PC or smartphone''. This is not a bug, but due to the strict specifications of Unity's drawing system. In this article, we will provide a step-by-step thorough explanation of the internal mechanism that causes this phenomenon, an easy-to-understand example for beginners, and a specific implementation method to correctly draw debug lines in the Game view and on the actual device after building.
1. Understanding with an analogy: "Auxiliary lines in an atelier" and "theater audience seats"
In order to understand the gap between the Scene view and Game view/actual machine drawing, let's use the analogy of a painting atelier (atelier) and a "museum (exhibition room) open to the public".
Suppose you are a teacher who teaches how to draw or a creator. When I draw a sketch on a canvas with paint, I attach a ``wooden frame guide that shows the golden ratio'' around the canvas, and I work (debug) by stringing many ``red water threads'' around the studio to keep the perspective from going awry. These working guides and threads are essential for completing your painting accurately. This is what we call ``Gizmos in the Scene view'' in Unity.
However, when showing the completed painting to the general audience (players) in the exhibition room, do you bring the wooden frame and red thread for perspective that you used in the atelier into the exhibition room? Naturally, all of this gets thrown away because it doesn't look good. Only pure "completed paintings" will be displayed in the exhibition room. This is the state of “Game view and built actual device”.
The Unity editor ``selfishly cleans up Gizmos,'' which is a work guide for developers, for the screen where players play the game (Game view) and the final product (actual build). Therefore, if you want to show the guide on the game screen or on the actual device, you must intentionally draw a special line that cannot be cleaned up (drawn as an object in the game).
2. Root cause: Gizmos class operation scope
Technically speaking, the Gizmos and Handles classes are functions closely tied to the Unity editor-specific assembly (UnityEditor). These are drawn only in the Scene view (and the editor-only debug display in the Game view), which is managed internally by the editor. During the Unity build process, lifecycle events such as OnDrawGizmos() and OnDrawGizmosSelected() are stripped from the binary at compile time. This improves the execution speed on the actual game machine and reduces unnecessary drawing load. Therefore, even if Gizmos processing remains in the product code, it will be completely ignored on the actual device and nothing will be displayed.
3. 4 solution approaches that are useful in practice
Choose the optimal solution method depending on the debugging context and execution environment (in the editor or actual build).
| Approach | Displayable environment | Benefits and recommended use cases |
|---|---|---|
| A. Gizmos toggle in Game view | Game view in editor only | No code changes required. If you want to easily check the enemy search range while playing the game in the editor. |
| B. Utilizing Debug.DrawLine | Both Scene/Game in the editor | Easily draw lines for one frame only. Ideal for checking ray cast (ray) collisions. |
| C. Direct drawing with GL class | In the editor + All builds on real devices | Works on real devices as well. It is possible to draw a large number of debug lines with fast script writing without contaminating components. |
| D. Using LineRenderer | In the editor + All actual builds | The safest and standard as they exist as physical GameObjects. Easy to adjust design. |
Solution approach A: Turn on the "Gizmos" button in the Game view (only in the editor)
If you just want to check the gizmos in the Game view while in play mode in the editor, this is the easiest method. Click the toggle button labeled "Gizmos" in the top right corner of the Game view window to turn it on. This will overlay the same gizmo lines in the Game view as in the Scene view. However, please keep in mind that this is a debugging feature of the editor and will never be reflected in the actual build.
Solution approach B: Use Debug.DrawLine / Debug.DrawRay
Using Debug.DrawLine or Debug.DrawRay, you can draw a line between arbitrary coordinates from a script. These are automatically drawn in the Game view when playing in the editor (the "Gizmos" button in the Game view must be ON).
using UnityEngine;
public class RaycastDebugger : MonoBehaviour
{
void Update()
{
Vector3 forward = transform.TransformDirection(Vector3.forward) * 10f;
// 1st argument: start position, 2nd argument: end position, 3rd argument: color, 4th argument: display duration (seconds)
Debug.DrawLine(transform.position, transform.position + forward, Color.red);
}
}
Solution Approach C: Low-level drawing for real devices using GL classes (recommended)
If you want to programmably draw lines and wireframes for debugging without adding any components even on real device builds, it is most effective to use the GL class, which sends drawing instructions directly to the graphics device. Write it within the lifecycle method OnPostRender or OnRenderObject that you call immediately after the camera is drawn.
using UnityEngine;
public class RuntimeLineDrawer : MonoBehaviour
{
public Color lineColor = Color.green;
private Material lineMaterial;
void CreateLineMaterial()
{
if (lineMaterial == null)
{
// Dynamically generate the simplest shader (one that draws vertex colors)
Shader shader = Shader.Find("Hidden/Internal-Colored");
lineMaterial = new Material(shader);
lineMaterial.hideFlags = HideFlags.HideAndDontSave;
// Setting rendering state (depth test settings, etc.)
lineMaterial.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
lineMaterial.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
lineMaterial.SetInt("_Cull", (int)UnityEngine.Rendering.CullMode.Off);
lineMaterial.SetInt("_ZWrite", 0); // Turn off depth writing
}
}
// Called after all objects have been rendered
void OnRenderObject()
{
CreateLineMaterial();
lineMaterial.SetPass(0);
GL.PushMatrix();
// Matrix settings for drawing directly in the world coordinate system
GL.MultMatrix(transform.localToWorldMatrix);
GL.Begin(GL.LINES); // Start line drawing mode
GL.Color(lineColor);
// Draw a 10 meter line from the origin to the front (Z+)
GL.Vertex3(0, 0, 0);
GL.Vertex3(0, 0, 10f);
GL.End();
GL.PopMatrix();
}
void OnDestroy()
{
if (lineMaterial != null)
{
DestroyImmediate(lineMaterial);
}
}
}
In the Universal Render Pipeline (URP), the execution timing of
OnRenderObject is different from the standard pipeline, so it may not render correctly. To safely implement a real device debug line in a URP environment, the best approach is to use LineRenderer described below, or create a custom Render Feature to integrate the drawing paths.
Solution approach D: Using the LineRenderer component (most reliable)
In actual builds, the method that works most robustly regardless of the drawing engine (URP/HDRP) or platform is to use Unity's standard component LineRenderer. By dynamically updating the number and coordinates of vertices from the program, you can draw smooth curves and arbitrary trajectories.
using UnityEngine;
[RequireComponent(typeof(LineRenderer))]
public class CircleRangeVisualizer : MonoBehaviour
{
public float radius = 5.0f; // Radius of the circle
public int segments = 50; // Number of line segments that make up the circle
private LineRenderer lineRenderer;
void Start()
{\
lineRenderer = GetComponent<LineRenderer>();
lineRenderer.useWorldSpace = false; // Draw in local coordinates
DrawCircle();
}
void DrawCircle()
{
lineRenderer.positionCount = segments + 1;
float angle = 0f;
for (int i = 0; i <= segments; i++)
{
float x = Mathf.Sin(Mathf.Deg2Rad * angle) * radius;
float z = Mathf.Cos(Mathf.Deg2Rad * angle) * radius;
lineRenderer.SetPosition(i, new Vector3(x, 0, z));
angle += (360f / segments);
}
}
// Dynamically update preview when radius changes in inspector
void OnValidate()
{
if (lineRenderer == null) lineRenderer = GetComponent();
DrawCircle();
}
}
4. Useful debugging object management checklist in practice
As development progresses, there are many accidents in which products are released with debugging code and components remaining. Please incorporate the following measures into your workflow.
- Assigning the Conditional attribute: By assigning the
[System.Diagnostics.Conditional("UNITY_EDITOR")]attribute to a method that performs drawing processing for debugging, the calling code for that method itself can be automatically deleted from the compilation results when building the product. - Separate serialization code: Always place classes for inspector extensions in the
Editorfolder and separate assemblies. - Debug component cleanup script: Creates a build preprocessor (a class that inherits from
IPreprocessBuildWithReport) and introduces a system to automatically destroy visualization game objects tagged "Debug" in the scene.