Cara menggambar garis dengan benar di Unity

27

Saya mengerjakan game yang mengharuskan saya menggambar beberapa baris dari satu titik yang lebih formal

Diberikan titik A dengan koordinat x, y saya menggambar n garis di mana garis ke-i memiliki koordinat yang dinamai xi, yi. Mengingat kemampuan LineRenderer di dalam Unity3D, saya tidak dapat menarik lebih dari satu baris dari titik tertentu karena itu membuat hanya polyline.

Saat ini saya menggunakan metode Debug.DrawLine () yang berhasil membuat baris. Saya telah mencoba menggunakan GL.Begin () seperti yang ditunjukkan pada contoh Unity tapi saya tidak bisa melihat garis saya ditarik.

Pertanyaan saya adalah: apakah ada metode lain untuk melakukan ini? Jika tidak, dapatkah Anda memberi tahu saya cara menunjukkan garis yang sedang digambar dengan Debug.DrawLine () di dalam mode putar? Saya telah melihat bahwa saya bisa menggunakan Gizmos.DrawLine () tapi saya tidak begitu mengerti penggunaannya.

Christo
sumber

Jawaban:

48

Menggunakan GL Lines:

Saya akan merekomendasikan menggunakan API GL untuk menggambar garis. Ketebalan garis akan selalu 1px di layar dan tidak ada opsi untuk mengubahnya. Juga tidak akan ada bayangan.

Panggilan metode GL dieksekusi segera sehingga Anda harus memastikan untuk memanggil mereka setelah kamera telah diberikan.

Melampirkan skrip ke kamera dan menggunakan Camera.OnPostRender () berfungsi baik untuk rendering di jendela game. Untuk membuatnya ditampilkan di editor, Anda dapat menggunakan MonoBehaviour.OnDrawGizmos () .

Berikut adalah kode barebones untuk menggambar garis dengan API GL:

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();
}

Berikut ini adalah skrip lengkap yang melampirkan semua poin yang diberikan ke poin utama. Ada beberapa instruksi dalam komentar kode untuk mengaturnya dengan benar dan tentang apa yang sedang terjadi.

Jika Anda mengalami masalah dalam mengubah warna garis penghubung, pastikan untuk menggunakan shader pada bahan garis Anda yang memperhitungkan warna titik seperti 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();
    }
}

Catatan lebih lanjut tentang bayangan: Saya menjelajahi menggunakan geometri shader untuk membuat bayangan tetapi karena panggilan GL langsung berjalan, mereka tidak dalam pipa render normal dan AutoLight.cgincdan Lighting.cginctidak akan mengambil ShadowCasterpass.


Garis dengan Bayangan dan Radius

Jika Anda perlu mengubah ketebalan garis dan ingin memiliki bayangan yang realistis. Cukup gunakan silinder mesh dan skala tingginya.

Berikut ini adalah skrip yang akan membuat silinder untuk menghubungkan setiap titik ke titik utama. Letakkan di objek game yang kosong dan isi parameternya. Ini akan menampung semua objek penghubung tambahan.

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
sumber
2
Booo, garis tidak memiliki bayangan. : p
MichaelHouse
Itu jawaban yang bagus. Saya memeriksa apa yang Anda dapatkan di sana untuk saya. Terima kasih banyak untuk detailnya, saya telah melakukan sesuatu yang mirip dengan ini tanpa hasil apa pun. Saya akan melihat contoh Anda dengan sangat baik untuk melihat kesalahan saya di sana. Terima kasih!
Christo
Bisakah saya melakukan ini tanpa menggunakan OnPostRender?
Christo
Karena saya perlu melakukannya di kelas khusus yang tidak berasal dari perilaku mono, jadi saya tidak memiliki metode OnPostRender.
Christo
1
Tampaknya tidak masalah warna apa yang saya masukkan ke GL.Color (), atau apakah saya menyebutnya sama sekali - warna garis tetap warna bahan. Materi baris saya saat ini menggunakan shader Unlit / Color.
Erhannis
5

Baris dengan Shadows dan Radius via Cube

Pergi dari jawaban @ MadLittleMod ini , di sini versi lain menggunakan jalur berdasarkan Cube ( tris: 12 ) bukan garis berdasarkan Cylinder ( tris: 80 ):

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
sumber