Cách vẽ chính xác một dòng trong Unity


27

Tôi đang làm việc trong một trò chơi đòi hỏi tôi phải vẽ một vài dòng từ một điểm duy nhất được nói chính thức hơn

Cho điểm A có tọa độ x, y tôi vẽ n đường thẳng trong đó đường thẳng thứ i có tọa độ có tên là xi, yi. Với các khả năng LineRenderer bên trong Unity3D, tôi không thể vẽ nhiều hơn một dòng từ một điểm cụ thể với nó vì nó chỉ hiển thị các đường dẫn.

Tôi hiện đang sử dụng phương thức Debug.DrawLine () để kết xuất thành công dòng. Tôi đã thử sử dụng GL.Begin () vì nó được hiển thị trong ví dụ về Unity nhưng tôi không thể thấy các dòng của mình được vẽ.

Câu hỏi của tôi là: có phương pháp nào khác để làm điều này không? Nếu không, bạn có thể cho tôi biết làm thế nào tôi có thể hiển thị dòng đang được vẽ bằng Debug.DrawLine () trong chế độ chơi không? Tôi đã thấy rằng tôi có thể sử dụng Gizmos.DrawLine () nhưng tôi không hiểu lắm về cách sử dụng.

Câu trả lời:


48

Sử dụng GL Lines:

Tôi sẽ khuyên bạn nên sử dụng API GL để vẽ các đường. Độ dày của dòng sẽ luôn là 1px trên màn hình và không có tùy chọn để thay đổi nó. Cũng sẽ không có bóng.

Các cuộc gọi phương thức GL được thực hiện ngay lập tức, do đó bạn cần đảm bảo gọi chúng sau khi máy ảnh đã được kết xuất.

Đính kèm tập lệnh vào máy ảnh và sử dụng Camera.OnPostRender () hoạt động tốt để hiển thị trong cửa sổ trò chơi. Để làm cho chúng hiển thị trong trình chỉnh sửa, bạn có thể sử dụng MonoBehaviour.OnDrawGizmos () .

Đây là mã vạch để vẽ một dòng với 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();
}

Dưới đây là một kịch bản đầy đủ gắn tất cả các điểm đã cho vào điểm chính. Có một số hướng dẫn trong các ý kiến ​​của mã để thiết lập đúng và về những gì đang xảy ra.

Nếu bạn gặp vấn đề khi thay đổi màu của các đường kết nối, hãy đảm bảo sử dụng trình đổ bóng trên vật liệu đường có tính đến màu đỉnh như 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();
    }
}

Lưu ý thêm về bóng: Tôi đã khám phá bằng cách sử dụng trình tạo bóng hình học để tạo bóng nhưng vì các cuộc gọi GL chạy ngay lập tức, chúng không nằm trong đường dẫn kết xuất thông thường AutoLight.cgincLighting.cgincsẽ không nhận ShadowCasterđường chuyền.


Dòng có bóng và bán kính

Nếu bạn cần thay đổi độ dày của dòng và muốn có bóng thực tế. Chỉ cần sử dụng một lưới xi lanh và quy mô chiều cao.

Dưới đây là một kịch bản sẽ tạo ra một hình trụ để kết nối mỗi điểm với điểm chính. Đặt nó trên một đối tượng trò chơi trống và điền vào các tham số. Nó sẽ giữ tất cả các đối tượng kết nối thêm.

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, các dòng không có bóng. : p
MichaelHouse

Đó là một câu trả lời tuyệt vời. Tôi đang kiểm tra những gì bạn đã có cho tôi. Cảm ơn rất nhiều về chi tiết, tôi đã làm một cái gì đó tương tự như thế này mà không có bất kỳ thành công nào. Tôi sẽ xem xét ví dụ của bạn rất tốt để xem tôi đã sai ở đâu. Xin cảm ơn!
Christo

Tôi có thể làm điều này mà không cần sử dụng OnPostRender không?
Christo

Bởi vì tôi cần thực hiện nó trong một lớp tùy chỉnh không xuất phát từ hành vi đơn sắc, do đó tôi không có phương thức OnPostRender.
Christo

1
Nó dường như không quan trọng màu gì tôi đặt trong GL.Color (), hoặc liệu tôi có gọi nó không - màu của các đường kẻ vẫn giữ nguyên màu của vật liệu. Tài liệu dòng của tôi hiện đang sử dụng shader Unlit / Color.
Erhannis

5

Dòng với Shadows và Radius qua Cube

Thoát khỏi câu trả lời của @ MadLittleMod , ở đây một phiên bản khác sử dụng các dòng dựa trên Cube ( tris: 12 ) thay vì các dòng dựa trên Xi lanh ( 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);
        }
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.