วิธีการวาดเส้นใน Unity อย่างถูกต้อง


27

ฉันกำลังทำงานกับเกมที่ต้องให้ฉันวาดสองสามบรรทัดจากจุดเดียวที่พูดอย่างเป็นทางการมากกว่า

กำหนดจุด A พร้อมพิกัด x, y ฉันวาดเส้น n เส้นที่เส้น i-th มีพิกัดที่ชื่อว่า xi, yi เมื่อพิจารณาความสามารถของ LineRenderer ภายใน Unity3D ฉันไม่สามารถวาดมากกว่าหนึ่งบรรทัดจากจุดใดจุดหนึ่งด้วยเนื่องจากมีการแสดงเฉพาะโพลีน

ขณะนี้ฉันใช้วิธี Debug.DrawLine () ซึ่งทำให้การแสดงผลสำเร็จ ฉันลองใช้ GL.Begin () ตามที่แสดงในตัวอย่าง Unityแต่ฉันไม่เห็นเส้นของฉันถูกวาด

คำถามของฉันคือ: มีวิธีอื่นในการทำเช่นนี้หรือไม่? ถ้าไม่คุณสามารถบอกได้ว่าฉันจะแสดงบรรทัดที่วาดด้วย Debug.DrawLine () ในโหมดเล่นได้อย่างไร ฉันเห็นว่าฉันสามารถใช้ Gizmos.DrawLine () ได้ แต่ฉันไม่เข้าใจว่ามันใช้งานอย่างไร

คำตอบ:


48

ใช้สาย GL:

ฉันอยากจะแนะนำให้ใช้GL API สำหรับการวาดเส้น ความหนาของเส้นจะอยู่ที่ 1px บนหน้าจอเสมอและไม่มีตัวเลือกให้เปลี่ยน จะไม่มีเงาอีกด้วย

การเรียกใช้เมธอด GL จะดำเนินการทันทีดังนั้นคุณต้องแน่ใจว่าได้ทำการโทรหลังจากที่กล้องแสดงผลไปแล้ว

การแนบสคริปต์เข้ากับกล้องและใช้CameraOnPostRender ()ทำงานได้ดีสำหรับการแสดงผลในหน้าต่างเกม เพื่อให้พวกเขาแสดงให้เห็นในการแก้ไขคุณสามารถใช้MonoBehaviour.OnDrawGizmos ()

นี่คือรหัส barebones เพื่อวาดเส้นด้วย 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();
}

นี่คือสคริปต์เต็มรูปแบบที่แนบจุดที่กำหนดทั้งหมดไปยังจุดหลัก มีคำแนะนำในการแสดงความคิดเห็นของรหัสเพื่อให้มันถูกต้องและเกี่ยวกับสิ่งที่เกิดขึ้น

หากคุณกำลังมีปัญหาในการเปลี่ยนสีของเส้นเชื่อมต่อให้แน่ใจว่าจะใช้ Shader 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

นั่นเป็นคำตอบที่ยอดเยี่ยม ฉันกำลังตรวจสอบสิ่งที่คุณไปถึงที่นั่นเพื่อฉัน ขอบคุณมากสำหรับรายละเอียดฉันได้ทำสิ่งที่คล้ายกับสิ่งนี้โดยไม่ประสบความสำเร็จ แต่อย่างใด ฉันจะดูตัวอย่างของคุณเป็นอย่างดีเพื่อดูว่าฉันไปผิดที่นั่นขอบคุณ!
Christo

ฉันสามารถทำได้โดยไม่ใช้ OnPostRender หรือไม่
Christo

เพราะฉันต้องทำในคลาสที่กำหนดเองที่ไม่ได้มาจากพฤติกรรมโมโนดังนั้นฉันไม่มีเมธอด OnPostRender
Christo

1
ดูเหมือนจะไม่สำคัญว่าสีที่ฉันใส่ไว้ใน GL.Color () หรือว่าฉันจะเรียกมันว่าอะไรก็ตาม - สีของเส้นตรงจะเป็นสีของวัสดุ วัสดุที่ใช้ในบรรทัดของฉันกำลังใช้ shader Unlit / Color
Erhannis

5

เส้นที่มีเงาและรัศมีผ่าน Cube

ออกไปจากคำตอบของ@ MadLittleModนี่เป็นอีกเวอร์ชั่นที่ใช้ Cube based lines ( tris: 12 ) แทนที่จะเป็น 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);
        }
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.