Wie man in Unity eine Linie korrekt zeichnet

27

Ich arbeite an einem Spiel, bei dem ich ein paar Linien von einem einzigen Punkt ziehen muss, der formeller formuliert ist

Bei gegebenem Punkt A mit den Koordinaten x, y zeichne ich n Linien, wobei die i-te Linie die Koordinaten xi, yi hat. Aufgrund der LineRenderer-Funktionen in Unity3D konnte ich nicht mehr als eine Linie von einem bestimmten Punkt aus zeichnen, da nur Polylinien gerendert werden.

Ich verwende derzeit die Debug.DrawLine () -Methode, die die Zeile erfolgreich rendert. Ich habe versucht, GL.Begin () zu verwenden, wie im Unity-Beispiel gezeigt, aber ich kann nicht sehen, dass meine Linien gezeichnet werden.

Meine Frage ist: Gibt es andere Methoden, um dies zu tun? Wenn nicht, können Sie mir sagen, wie ich die Linie, die mit Debug.DrawLine () gezeichnet wird, im Wiedergabemodus anzeigen kann? Ich habe gesehen, dass ich Gizmos.DrawLine () verwenden könnte, aber ich habe die Verwendung nicht ganz verstanden.

Christo
quelle

Antworten:

48

Verwendung von FIBU-Zeilen:

Ich würde empfehlen, die GL- API zum Zeichnen von Linien zu verwenden. Die Linienstärke wird auf dem Bildschirm immer 1px betragen und es gibt keine Möglichkeit, sie zu ändern. Es wird auch keine Schatten geben.

Die GL-Methodenaufrufe werden sofort ausgeführt, sodass Sie sicherstellen müssen, dass Sie sie aufrufen, nachdem die Kamera bereits gerendert hat.

Das Anhängen des Skripts an die Kamera und die Verwendung von Camera.OnPostRender () eignen sich gut zum Rendern im Spielfenster . Um sie im Editor anzuzeigen , können Sie MonoBehaviour.OnDrawGizmos () verwenden .

Hier ist der Barebones-Code zum Zeichnen einer Linie mit der GL-API:

public Material lineMat = new Material("Shader \"Lines/Colored Blended\" {" + "SubShader { Pass { " + "    Blend SrcAlpha OneMinusSrcAlpha " + "    ZWrite Off Cull Off Fog { Mode Off } " + "    BindChannels {" + "      Bind \"vertex\", vertex Bind \"color\", color }" + "} } }");

void OnPostRender() {
    GL.Begin(GL.LINES);
    lineMat.SetPass(0);
    GL.Color(new Color(0f, 0f, 0f, 1f));
    GL.Vertex3(0f, 0f, 0f);
    GL.Vertex3(1f, 1f, 1f);
    GL.End();
}

Hier ist ein vollständiges Skript, das alle angegebenen Punkte an den Hauptpunkt anfügt. In den Kommentaren des Codes finden Sie einige Anweisungen, um ihn richtig einzurichten und darüber, was gerade passiert.

Wenn Sie Probleme haben, die Farbe der Verbindungslinien zu ändern, stellen Sie sicher, dass Sie auf Ihrem Linienmaterial einen Shader verwenden, der die Scheitelpunktfarbe berücksichtigt, z Unlit/Color.

using UnityEngine;
using System.Collections;

// Put this script on a Camera
public class DrawLines : MonoBehaviour {

    // Fill/drag these in from the editor

    // Choose the Unlit/Color shader in the Material Settings
    // You can change that color, to change the color of the connecting lines
    public Material lineMat;

    public GameObject mainPoint;
    public GameObject[] points;

    // Connect all of the `points` to the `mainPoint`
    void DrawConnectingLines() {
        if(mainPoint && points.Length > 0) {
            // Loop through each point to connect to the mainPoint
            foreach(GameObject point in points) {
                Vector3 mainPointPos = mainPoint.transform.position;
                Vector3 pointPos = point.transform.position;

                GL.Begin(GL.LINES);
                lineMat.SetPass(0);
                GL.Color(new Color(lineMat.color.r, lineMat.color.g, lineMat.color.b, lineMat.color.a));
                GL.Vertex3(mainPointPos.x, mainPointPos.y, mainPointPos.z);
                GL.Vertex3(pointPos.x, pointPos.y, pointPos.z);
                GL.End();
            }
        }
    }

    // To show the lines in the game window whne it is running
    void OnPostRender() {
        DrawConnectingLines();
    }

    // To show the lines in the editor
    void OnDrawGizmos() {
        DrawConnectingLines();
    }
}

Weitere Anmerkung über Schatten: Ich erforschte ein Geometrie - Shader mit Schatten zu machen , aber da die GL Anrufe sofort ausgeführt wird , sind sie nicht in der normalen Rendering - Pipeline und AutoLight.cgincund Lighting.cgincnicht den abholen ShadowCasterPass.


Linien mit Schatten und Radius

Wenn Sie die Linienstärke ändern müssen und realistische Schatten haben möchten. Verwenden Sie einfach ein Zylindernetz und skalieren Sie die Höhe.

Hier ist ein Skript, das einen Zylinder erstellt, um jeden Punkt mit dem Hauptpunkt zu verbinden. Legen Sie es auf ein leeres Spielobjekt und geben Sie die Parameter ein. Es enthält alle zusätzlichen Verbindungsobjekte.

using UnityEngine;
using System.Collections;

public class ConnectPointsWithCylinderMesh : MonoBehaviour {

    // Material used for the connecting lines
    public Material lineMat;

    public float radius = 0.05f;

    // Connect all of the `points` to the `mainPoint`
    public GameObject mainPoint;
    public GameObject[] points;

    // Fill in this with the default Unity Cylinder mesh
    // We will account for the cylinder pivot/origin being in the middle.
    public Mesh cylinderMesh;


    GameObject[] ringGameObjects;

    // Use this for initialization
    void Start () {
        this.ringGameObjects = new GameObject[points.Length];
        //this.connectingRings = new ProceduralRing[points.Length];
        for(int i = 0; i < points.Length; i++) {
            // Make a gameobject that we will put the ring on
            // And then put it as a child on the gameobject that has this Command and Control script
            this.ringGameObjects[i] = new GameObject();
            this.ringGameObjects[i].name = "Connecting ring #" + i;
            this.ringGameObjects[i].transform.parent = this.gameObject.transform;

            // We make a offset gameobject to counteract the default cylindermesh pivot/origin being in the middle
            GameObject ringOffsetCylinderMeshObject = new GameObject();
            ringOffsetCylinderMeshObject.transform.parent = this.ringGameObjects[i].transform;

            // Offset the cylinder so that the pivot/origin is at the bottom in relation to the outer ring gameobject.
            ringOffsetCylinderMeshObject.transform.localPosition = new Vector3(0f, 1f, 0f);
            // Set the radius
            ringOffsetCylinderMeshObject.transform.localScale = new Vector3(radius, 1f, radius);

            // Create the the Mesh and renderer to show the connecting ring
            MeshFilter ringMesh = ringOffsetCylinderMeshObject.AddComponent<MeshFilter>();
            ringMesh.mesh = this.cylinderMesh;

            MeshRenderer ringRenderer = ringOffsetCylinderMeshObject.AddComponent<MeshRenderer>();
            ringRenderer.material = lineMat;

        }
    }

    // Update is called once per frame
    void Update () {
        for(int i = 0; i < points.Length; i++) {
            // Move the ring to the point
            this.ringGameObjects[i].transform.position = this.points[i].transform.position;

            // Match the scale to the distance
            float cylinderDistance = 0.5f*Vector3.Distance(this.points[i].transform.position, this.mainPoint.transform.position);
            this.ringGameObjects[i].transform.localScale = new Vector3(this.ringGameObjects[i].transform.localScale.x, cylinderDistance, this.ringGameObjects[i].transform.localScale.z);

            // Make the cylinder look at the main point.
            // Since the cylinder is pointing up(y) and the forward is z, we need to offset by 90 degrees.
            this.ringGameObjects[i].transform.LookAt(this.mainPoint.transform, Vector3.up);
            this.ringGameObjects[i].transform.rotation *= Quaternion.Euler(90, 0, 0);
        }
    }
}

MLM
quelle
2
Booo, die Linien haben keine Schatten. : p
MichaelHouse
Das ist eine großartige Antwort. Ich überprüfe, was du für mich da hast. Vielen Dank für das Detail, ich hatte so etwas ohne Erfolg gemacht. Ich werde mir Ihr Beispiel genau ansehen, um zu sehen, wo ich da falsch gelegen habe. Danke!
Christo
Kann ich das ohne OnPostRender machen?
Christo
Da dies in einer benutzerdefinierten Klasse erfolgen muss, die nicht vom Monoverhalten abgeleitet ist, ist die OnPostRender-Methode nicht verfügbar.
Christo
1
Es scheint egal zu sein, welche Farbe ich in GL.Color () eingestellt habe oder ob ich es überhaupt nenne - die Farbe der Linien bleibt die Farbe des Materials. Mein Linienmaterial verwendet derzeit den Shader Unlit / Color.
Erhannis
5

Linien mit Schatten und Radius über Würfel

Ausgehend von der Antwort von @ MadLittleMod , hier eine andere Version, die auf Würfeln basierende Linien ( Tris: 12 ) anstelle von auf Zylindern basierenden Linien ( Tris: 80 ) verwendet:

using UnityEngine;
using System.Collections;

public class ConnectPointsWithCubeMesh : MonoBehaviour 
{

    // Material used for the connecting lines
    public Material lineMat;

    public float radius = 0.05f;

    // Connect all of the `points` to the `mainPoint`
    public GameObject mainPoint;
    public GameObject[] points;

    // Fill in this with the default Unity Cube mesh
    // We will account for the cube pivot/origin being in the middle.
    public Mesh cubeMesh;


    GameObject[] ringGameObjects;

    // Use this for initialization
    void Start() 
    {
        this.ringGameObjects = new GameObject[points.Length];
        //this.connectingRings = new ProceduralRing[points.Length];
        for(int i = 0; i < points.Length; i++) {
            // Make a gameobject that we will put the ring on
            // And then put it as a child on the gameobject that has this Command and Control script
            this.ringGameObjects[i] = new GameObject();
            this.ringGameObjects[i].name = "Connecting ring #" + i;
            this.ringGameObjects[i].transform.parent = this.gameObject.transform;

            // We make a offset gameobject to counteract the default cubemesh pivot/origin being in the middle
            GameObject ringOffsetCubeMeshObject = new GameObject();
            ringOffsetCubeMeshObject.transform.parent = this.ringGameObjects[i].transform;

            // Offset the cube so that the pivot/origin is at the bottom in relation to the outer ring     gameobject.
            ringOffsetCubeMeshObject.transform.localPosition = new Vector3(0f, 1f, 0f);
            // Set the radius
            ringOffsetCubeMeshObject.transform.localScale = new Vector3(radius, 1f, radius);

            // Create the the Mesh and renderer to show the connecting ring
            MeshFilter ringMesh = ringOffsetCubeMeshObject.AddComponent<MeshFilter>();
            ringMesh.mesh = this.cubeMesh;

            MeshRenderer ringRenderer = ringOffsetCubeMeshObject.AddComponent<MeshRenderer>();
            ringRenderer.material = lineMat;

        }
    }

    // Update is called once per frame
    void Update() 
    {
        for(int i = 0; i < points.Length; i++) {
            // Move the ring to the point
            this.ringGameObjects[i].transform.position = this.points[i].transform.position;

            this.ringGameObjects[i].transform.position = 0.5f * (this.points[i].transform.position + this.mainPoint.transform.position);
            var delta = this.points[i].transform.position - this.mainPoint.transform.position;
            this.ringGameObjects[i].transform.position += delta;

            // Match the scale to the distance
            float cubeDistance = Vector3.Distance(this.points[i].transform.position, this.mainPoint.transform.position);
            this.ringGameObjects[i].transform.localScale = new Vector3(this.ringGameObjects[i].transform.localScale.x, cubeDistance, this.ringGameObjects[i].transform.localScale.z);

            // Make the cube look at the main point.
            // Since the cube is pointing up(y) and the forward is z, we need to offset by 90 degrees.
            this.ringGameObjects[i].transform.LookAt(this.mainPoint.transform, Vector3.up);
            this.ringGameObjects[i].transform.rotation *= Quaternion.Euler(90, 0, 0);
        }
    }
}
Purga
quelle