Comment tracer correctement une ligne dans Unity

27

Je travaille sur un jeu qui m'oblige à tracer quelques lignes à partir d'un seul point qui est plus formellement dit

Étant donné le point A avec les coordonnées x, y, je trace n lignes où la i-ème ligne a les coordonnées nommées xi, yi. Étant donné les capacités de LineRenderer à l'intérieur d'Unity3D, je n'ai pas pu dessiner plus d'une ligne à partir d'un point particulier avec elle car elle ne rend que les polylignes.

J'utilise actuellement la méthode Debug.DrawLine () qui rend avec succès la ligne. J'ai essayé d'utiliser GL.Begin () comme il est montré dans l'exemple Unity mais je ne vois pas mes lignes tracées.

Ma question est: existe-t-il d'autres méthodes pour ce faire? Sinon, pouvez-vous me dire comment puis-je afficher la ligne qui est tracée avec Debug.DrawLine () en mode de lecture? J'ai vu que je pouvais utiliser Gizmos.DrawLine () mais je ne comprenais pas très bien son utilisation.

Christo
la source

Réponses:

48

Utilisation de GL Lines:

Je recommanderais d'utiliser l' API GL pour tracer des lignes. L'épaisseur du trait sera toujours de 1px à l'écran et il n'y a pas d'option pour la changer. Il n'y aura pas non plus d'ombres.

Les appels de méthode GL sont exécutés immédiatement, vous devez donc vous assurer de les appeler une fois que la caméra est déjà restituée.

Attacher le script à la caméra et utiliser Camera.OnPostRender () fonctionne bien pour le rendu dans la fenêtre de jeu. Pour les faire apparaître dans l'éditeur, vous pouvez utiliser MonoBehaviour.OnDrawGizmos () .

Voici le code barebones pour tracer une ligne avec l'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();
}

Voici un script complet qui attache tous les points donnés au point principal. Il y a quelques instructions dans les commentaires du code pour le configurer correctement et sur ce qui se passe.

Si vous rencontrez des problèmes pour changer la couleur des lignes de connexion, assurez-vous d'utiliser un ombrage sur votre matériau de ligne qui prend en compte la couleur du sommet, par exemple 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();
    }
}

Remarque supplémentaire sur les ombres: j'ai exploré l'utilisation d'un ombrage de géométrie pour créer des ombres, mais comme les appels GL s'exécutent immédiatement, ils ne sont pas dans le pipeline de rendu normal AutoLight.cgincet Lighting.cgincne captent pas la ShadowCasterpasse.


Lignes avec ombres et rayon

Si vous devez modifier l'épaisseur du trait et que vous souhaitez avoir des ombres réalistes. Utilisez simplement un maillage cylindrique et modifiez la hauteur.

Voici un script qui fera un cylindre pour relier chaque point au point principal. Placez-le sur un objet de jeu vide et remplissez les paramètres. Il contiendra tous les objets de connexion supplémentaires.

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
la source
2
Booo, les lignes n'ont pas d'ombres. : p
MichaelHouse
Voilà une excellente réponse. J'inspecte ce que tu as là pour moi. Merci beaucoup pour le détail, j'avais fait quelque chose de similaire sans aucun succès. Je vais très bien regarder votre exemple pour voir où je me suis trompé là-bas. Merci!
Christo
Puis-je le faire sans utiliser OnPostRender?
Christo
Parce que je dois le faire dans une classe personnalisée qui n'est pas dérivée du comportement mono, je n'ai donc pas la méthode OnPostRender.
Christo
1
Peu importe la couleur que je mets dans GL.Color (), ou si je l'appelle du tout - la couleur des lignes reste la couleur du matériau. Mon matériau de ligne utilise actuellement le shader Unlit / Color.
Erhannis
5

Lignes avec ombres et rayon via le cube

En partant de la réponse de @ MadLittleMod , voici une autre version utilisant des lignes à base de cube ( tris: 12 ) au lieu de lignes à base de cylindre ( 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
la source