Як правильно намалювати лінію в Unity


27

Я працюю над грою, яка вимагає від мене намалювати кілька рядків з однієї точки, що є більш формально сказаною

Дано точку A з координатами x, y я малюю n прямих, де i-та лінія має координати, названі як xi, yi. Враховуючи можливості LineRenderer всередині Unity3D, я не зміг намалювати більше однієї лінії з певної точки, оскільки вона видає лише полілінії.

На даний момент я використовую метод Debug.DrawLine (), який успішно передає рядок. Я спробував використовувати GL.Begin (), як це показано в прикладі Unity але я не можу бачити, що мої лінії намальовані.

Моє запитання: чи існують інші методи для цього? Якщо ні, чи можете ви сказати мені, як я можу показати лінію, яка проводиться за допомогою програми Debug.DrawLine () у режимі відтворення? Я бачив, що можу використовувати Gizmos.DrawLine (), але не зовсім зрозумів, що це використання.

Відповіді:


48

Використання ліній GL:

Я б рекомендував використовувати GL API для малювання ліній. Товщина лінії завжди буде 1px на екрані, і немає можливості її змінити. Тіні також не буде.

Виклики методу GL виконуються негайно, тому вам потрібно переконатися, що вони дзвонять після того, як камера вже надіслана.

Додавання сценарію до камери та використання Camera.OnPostRender () добре працює для візуалізації у вікні гри. Щоб показати їх у редакторі, ви можете використовувати MonoBehaviour.OnDrawGizmos () .

Ось код barebones, щоб намалювати лінію з API 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();
}

Ось повний сценарій, який приєднує всі задані пункти до основного. У коментарях до коду є деякі вказівки, щоб його правильно налаштувати і про те, що відбувається.

Якщо у вас виникають проблеми із зміною кольору з'єднувальних ліній, обов’язково використовуйте шейдер на матеріалі лінії, який враховує колір вершин, наприклад 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();
    }
}

Подальша примітка щодо тіней: Я дослідив, використовуючи шейдер для геометрії, щоб зробити тіні, але оскільки виклики GL запускаються негайно, вони не перебувають у звичайному конвеєрному рендерінгу AutoLight.cgincі Lighting.cgincне приймуть ShadowCasterпропуск.


Лінії з тінями та радіусом

Якщо вам потрібно змінити товщину лінії і хочете мати реалістичні тіні. Просто використовуйте сітку циліндра і масштабуйте висоту.

Ось сценарій, який зробить циліндр для підключення кожної точки до головної точки. Розмістіть його на порожньому ігровому об’єкті та заповніть параметри. Він буде містити всі додаткові об'єкти, що з’єднуються.

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


2
Booo, лінії не мають тіней. : p
MichaelHouse

Це чудова відповідь. Я перевіряю, що ти там для мене. Велике спасибі за деталі, я зробив щось подібне до цього без жодного успіху. Я дуже добре розберуся у вашому прикладі, щоб побачити, де я помилився там. Дякую!
Крісто,

Чи можу це зробити, не використовуючи OnPostRender?
Крісто

Оскільки мені потрібно це зробити у власному класі, який не випливає з моно поведінки, тому у мене немає методу OnPostRender.
Крісто

1
Здається, не має значення, який колір я вкладаю в GL.Color (), чи я його взагалі називаю - колір ліній залишається кольором матеріалу. В даний час в моєму рядковому матеріалі використовується шейдер Unlit / Color.
Ерханніс

5

Лінії з тінями та радіусом через Куб

Виходячи з відповіді @ MadLittleMod , ось ще одна версія, що використовує лінії на основі куба ( tris: 12 ) замість ліній на основі циліндра ( 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);
        }
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.