Jump to content
Unity Insider Forum

Linie erzeugen


Kojote

Recommended Posts

Grüße!

Ich bin immer noch an der Konzeptphase meines neuen Projektes und habe da eine Idee und wollte euch mal fragen, ob und wie dies technisch machbar wäre.

Ich habe mehrere Spieleobjekte auf einem 3D Spielefeld. Wenn ich einen der beiden Punkte angeklickt habe, soll ein Ankerpunkt gesetzt werden, sprich ein Ursprungspunkt. Wenn ich das nächste Spieleobjekt angeklickt habe, soll eine Linie zwischen beiden Punkten erzeugt werden. Nun kann ich von Objekt zu Objekt eine Linie zeichnen. Jedoch muss ich auch abfragen können, ob sich Linien überschneiden.

Ist so etwas umsetzbar, wenn ja wie?

Des weiteren gleich eine Anschlussfrage, diese Linien möchte ich leuchten lassen, wäre dies auch möglich?

Vielen Dank und Grüße von

Kojote :)

Link zu diesem Kommentar
Auf anderen Seiten teilen

Linien überschneiden von 4 Objekten?  Also z.B. eine von oben nach unten und eine von links nach rechts?

Klar kann man das. Ist nur ne Frage der mathematischen Formel. Es ist schon einmal ganz klar, dass wenn die 2 Punkte innerhalb einer Achse im 2D Raum innerhalb der Endpunkte der anderen Linie sind, sie sich überschneiden würden. Beispiel Linie 1 starten bei  X=2,Y=0 und geht nach X=5,Y=5. Die andere Linie geht von X=1,Y=4 und endet bei X=9,Y=2.
Linie 2 Kreuzt eindeutig den X Bereich von Linie1 Und ist zusätzlich noch innerhalb der Maximalen Y Punkte, also Y=0 und Y=5.

Sowas ist einfach. Wenn jetzt aber 3 Dimensionen im Spiel sind oder aber eine Linie nur evtl. die andere Kreuzen würde, weil sie ganz knapp an einem Endpunkt der anderen Linie vorbei geht oder schneidet, dann musst du interpolieren, also alle möglichen Zwischenschritte abfragen.
Ich, der jetzt keine Formel dafür hat (die es aber bestimmt gibt), würde die Linien ersteinmal halbieren und schauen, ob der erste Endpunkt und der Mittelpunkt innerhalb einer Schnittmenge liegen würde. Tut er das nicht, dann kann diese Hälfte ignoriert werden. Jetzt würde ich die andere Hälfte wieder halbieren usw. oder aber das mit der ersten Hälfte machen, denn es kann immer nur eine Hälfte eines Abschnitts die andere Linie treffen. :) 
Es gibt aber ein Problem dabei, denn du musst im 3D Raum eine Dicke der Linie definieren. Eine Linie hat ja eigentlich keine Dicke und somit würdest du unendlich Teilen und nie rausfinden, ob sich etwas berührt bzw. schneidet.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich habe noch einmal eine Grafik beigefügt.

Ihr seht ja, die 8 Kästchen, jeder hat seinen Koordinaten (Vector 3). Ich möchte nun, wenn ich auf eines der Kästchen klicke, zum beispiel das links unten, die Ursprungskoordinate gespeichert wird. Sagen wir, es ist Vector3(3,3,0).

Danach klicke ich ein weiteres Kästchen an, das weiter rechts oben, dieses hat die die Koordinate Vector3(4,2,0).

Nun möchte ich, dass zwischen diesen beiden Punkten, nun eine Geradeerzeugt wird. Was in meinem Falle denke ich ein Line Renderer wäre.

Das wäre Schritt 1. Ich möchte aber gleichzeitig auch abfragen, ob sich zwei Linien überkreuzen, wenn ja, soll eine Verbindung zwischen zwei Punkten nicht zustande kommen.

Die Antwort, welche Koordinaten jeder Punkt hat, ich recht einfach gelöst, ich kann ja X,Y und Z abfragen. Kompliziert wird es für mich jedoch, wie ich diese Linie da hin bekomme, wo sie hin soll, im richtigen Winkel und in der richtigen Länge. Genau so, wie ich Abfrage, ob da schon eine Linie ist. Man könnte mit einem fest definierten Quader oder Zylinder arbeiten. In meinem Beispiel ist es jetzt schlecht ersichtlich, aber auf der Spielfläche kann es nur zwei bestimmte Distanzen geben.

Man müsste also X1, Y1 und Z1 nehmen, dazu X2, Y2 und Z2 und heraus bekommen, welchen Abstand beide haben und dann den Mittelpunkt zwischen beiden Punkten ermitteln und das Objekt an diese Stelle setzen und noch mit dem Satz des Pythagoras den Rest ausrechnen. Wenn nun zwei Objekte sich schneiden, wäre dies durch die Collision zu merken.

Wäre schon nen ziemlicher Aufwand, gibts da in Unity etwas, was mir da etwas weiter helfen kann oder seht ihr eine andere Lösung?

@yawahoo - Stimmt, mit Raycasting könnte ich Distanz ermitteln. Fehlen würde dann nur noch der Winkel, der müsste über Pythagoras raus zu bekommen sein, damit hätte ich Winkel und Distanz. 

Idee.jpg

Link zu diesem Kommentar
Auf anderen Seiten teilen

Wenn wir mal davon ausgehen das deine Zeichnung akkurat ist, dann bedeutet das deine Objekte liegen immer in einer Ebene?

Für diesen Fall: https://martin-thoma.com/how-to-check-if-two-line-segments-intersect/

 

Für den dreidimensionalen Fall gibt es aber genauso Lösungen: http://paulbourke.net/geometry/pointlineplane/

Beide Links nutzen aber einfach Lineare Algebra um das Problem zu lösen. So wie es scheint gibt es von Unity keine vorgefertigte Lösung.

Link zu diesem Kommentar
Auf anderen Seiten teilen

@Kojote Kannst du dich noch an die Koordinatensystem in der Schule erinnern? Wie hast du denn da die Mitten gefunden?

Genau, je Achse das kleinere vom Großen abziehen und dann durch 2 Teilen und dem kleineren wieder drauf tun!   ( Wir haben früher immer die Achse auf den 0 Punkt geschoben und dann einfach geteilt)
X1 = 5 , X2 =17 , also ist die X-Länge  X2-X1   Gerechnet 17-5=12, Und die Xmitte  dann Xlänge/2 also 12/2 = 6   Jetzt einfach wieder zurück schieben um den Punkt im Koordinatensystem zu haben. Also X1+Xmitte  5+6 =11.

Also wenn du es jetzt genau so machen willst, wie du es geschrieben hast (Mitte ermitteln, Collider-Objekt da hin setzen, ausrichten und länge anpassen dann hätte ich da was für dich.
Man braucht gar nichts selber errechnen sondern einfach die Transform- und Vector3- Befehle und Funktionen nutzen.

Zum Test brauchst du einfach einen Cube, der dieses Script bekommt. Dann ziehst du einfach 2 Objekte der Szene in die beiden Transformslots im Inspector. Startest und drückst A.

 public Transform startobjekt;
 public Transform zielobjekt;

	void Update () {
        if (Input.GetKeyDown(KeyCode.A)) {
            // kurz aufs Startobjekt legen
            transform.position = startobjekt.position;
            // damit ich mich zum Ziel hin ausrichen kann
            transform.LookAt(zielobjekt); 
            // jetzt schiebe ich mich in die Mitte zwischen die 2 Objekte
            transform.position = Vector3.MoveTowards(transform.position, zielobjekt.position, Vector3.Distance(startobjekt.position,zielobjekt.position)/2);
            //Jetzt ziehe ich mich in die länge
            transform.localScale = new Vector3(0.1f, 0.1f, Vector3.Distance(startobjekt.position, zielobjekt.position));
        }
	}

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ja, man kann das auch mathematisch lösen. Ich würde einfach einen Box-Collider jeweils um die Linien legen (mit entsprechenden Dimensionen) und einen Rayast in die Richtung der neuen Verbindung schicken. Wenn der Raycast kollidiert, dann ist die Verbindung nicht erlaubt.
Alternativ kann man auch ein Bounds-Objekt für jede Linie erzeugen und dann über Intersects überprüfen, ob die neue Verbindung mit einem bereits vorhandenen BB kollidiert.
https://docs.unity3d.com/ScriptReference/Bounds.html
https://docs.unity3d.com/ScriptReference/Bounds.Intersects.html
 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Da hab ich ja viel zum ausprobieren, vielen Dank, werd mich mal ran setzen! :)

So, hier mal mein Code:

    public GameObject verbindungsstueck;

    public Vector3 koordinate1;
    public Vector3 koordinate2;

    public bool koordinate1Gesetzt;
    public bool koordinate2Gesetzt;
    public bool verbindungsstueckPositionieren;



    private void Start() {
        koordinate1Gesetzt = false;
        koordinate2Gesetzt = false;
        verbindungsstueckPositionieren = false;
    }

    public void Update() {
        if (verbindungsstueckPositionieren == true) {
            // Positionierung des Verbindungsstücks
            verbindungsstueck.transform.position = Vector3.MoveTowards(verbindungsstueck.transform.position, koordinate2, Vector3.Distance(koordinate1, koordinate2) / 2);

            // Ausrichtung des Verbindungsstückes zum Ziel
            verbindungsstueck.transform.LookAt(koordinate2);

            //Jetzt ziehe ich mich in die länge
            verbindungsstueck.transform.localScale = new Vector3(0.1f, 0.1f, Vector3.Distance(koordinate1, koordinate2));

        }    
    }

Ich habe alles in ein Steuerstrcipt gepackt. Dieses soll irgendwann mal einen Prefab im Spiel laden, dieses Prefab soll das Verbindungsstück sein.

Im Moment gebe ich das Gameobjekt jedoch erst einmal vor.

Hätte da zwei Probleme:

1. Bei LookAt richet sich immer von der Ursprungskoordinate an, zum Zielobjekt aus, dadurch passt der Wenkel nicht.

2. Aus bisher unbekanntem Grund, wird das Verbindungsstück immer auf die Position der zweiten Koordinate gesetzt.

 

EDIT:

Hab meinen Denkfehler gefunden, so müsste meine abgeänderte Version funktionieren. :)

Klappt doch ganz gut, neues Objekt wird nun per Instantiate aus den Prefabs geladen und die Verbindungen funktionieren!

Jetzt mal sehen wie das mit dem Raycast ist. :)

public void Update() {
        if (verbindungsstueckPositionieren == true) {
            // Positionierung des Verbindungsstücks
            verbindungsstueck.transform.position = Vector3.MoveTowards(koordinate1, koordinate2, Vector3.Distance(koordinate1, koordinate2) / 2);

            // Ausrichtung des Verbindungsstückes zum Ziel
            verbindungsstueck.transform.LookAt(koordinate2);

            // Jetzt ziehe ich mich in die länge
            verbindungsstueck.transform.localScale = new Vector3(0.1f, 0.1f, Vector3.Distance(koordinate1, koordinate2));

            // Ende
            verbindungsstueckPositionieren = false;
        }
    }

EDIT 2:

Muss leider noch mal nachfragen, nutz das erste mal Raycasting.

Habe jetzt dieses Scriptschnipsel:

private void FixedUpdate() {
        if (raycastUeberpruefung == true) {
            if (Physics.Raycast(koordinate1, koordinate2)) { 
                if (hit.collider.tag == "Verbindungsstueck") {
                    print("Gefunden");
                    raycastUeberpruefung = false;
                }
            }
        }  
    }

Wenn ich es richtig verstanden habe gibt es ja verschiedene Möglichkeiten das Raycasting einzusetzen. Bei mir wäre es, ein Raycast von einem zum anderen Punkt und die Überprüfung des Rückgabewertes hit.

Nur kommt bei mir bei der Tag Abfrage eine Fehlermeldung.


EDIT 3:

Na endlich:

if (Physics.Raycast(koordinate1, koordinate2, out hit, Vector3.Distance(koordinate1, koordinate2))) {
	if (hit.collider.tag == "XYZ") {                
	}
}

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 2 weeks later...

Tja, dachte das Problem wäre gelöst, nun kommt es doch wieder hervor. Hatte vor einiger Zeit das Ray wieder ausgebaut, da ich dachte ich brauches es nicht mehr. Nun muss es doch wieder rein.

Das steht bei mir in FixedUpdate:

    // Raycasting für Verbindungsprüfung
        if (verbindungsueberpruefung == true) {
            Debug.Log("Neustart!");
            Physics.Raycast(koordinate1, koordinate2, out hit, Vector3.Distance(koordinate1, koordinate2));
            Debug.Log(hit.collider.tag);
            if (hit.collider.tag == "Verbindungsstueck") {
                // Doppelte Verbindung, keine Verbindung erlaubt
                verbindungsueberpruefung = false;
            } else {
                // Keine Verbindung vorhanden, Verbindung erzeugen erlaubt
                if (Vector3.Distance(koordinate1, koordinate2) < maximalAbstand) {
                    // Es ist keine doppelte Verbindung vorhanden 
                    verbindungsueberpruefung = false;
                    VerbindungErzeugen();
                } else {
                    verbindungsueberpruefung = false;
                }
            }
        }

Rein vom logischen müsste es gehn, er soll von Koordinate1 zu Koordinate2 ein Ray schicken, Distanz ist der Abstand zwischen beiden Koordinaten. Output ist hit.

Findet er einen Tag mit den Namen "Verbindungsstueck" sollte er abbrechen, wenn nicht Entfernung kontrollieren und dann entscheiden was passiert. Je nach dem was für ein Ergebnis kommt, soll die Überprüfung wieder deaktiviert werden.

Jedoch startet er immer und immer wieder neu. Von außen kann es eigentlich nicht sein, der Boolean "verbindungsueberpruefung" wird über eine Methode gestartet, die nur aktiv ist, wenn ich etwas klicke.

Trotzdem startet er mit dieser Fehlermeldung immer wieder neu:

NullReferenceException: Object reference not set to an instance of an object
Spielsteuerung.FixedUpdate () (at Assets/Scripts/Spielsteuerung.cs:140)

Er bezieh sich dabei auf diese Zeile:

 if (hit.collider.tag == "Verbindungsstueck") {

Wo sieht er denn hier ein Problem? Entweder hat der Tag den Namen oder nicht. Wenn er ihn nicht findet, dann halt false. *grübel*

Das funktioniert auch nicht:

hit.transform.gameObject.tag

Ich habe im übrigen auch mal getestet:

Debug.DrawLine(koordinate1, koordinate2, Color.red);

Der Ray ist richtig und geht von einer zur anderen Koordinate, wird aber immer und immer wieder gesendet.

Sieht ihr hier einen Fehler?

Link zu diesem Kommentar
Auf anderen Seiten teilen

Probier es mal damit, da waren mehrere Fehler im Code, zum einen übergibt man beim Raycast eine Richtung und keine 2. Koordinate und zum anderen sollte man prüfen, ob der Raycast etwas getroffen hat:

        // Raycasting für Verbindungsprüfung
        if (verbindungsueberpruefung == true)
        {
            Debug.Log("Neustart!");

            Vector3 direction = koordinate2 - koordinate1;

            // Ist die Verbindung vorhanden ?
            if (Physics.Raycast(koordinate1, direction, out hit, Vector3.Distance(koordinate1, koordinate2)))
            {
                Debug.Log(hit.collider.tag);
                if (hit.collider.CompareTag("Verbindungsstueck"))
                {
                    // Doppelte Verbindung, keine Verbindung erlaubt
                    verbindungsueberpruefung = false;
                }
            }
            else
            {
                // Keine Verbindung vorhanden, Verbindung erzeugen erlaubt
                if (Vector3.Distance(koordinate1, koordinate2) < maximalAbstand)
                {
                    // Es ist keine doppelte Verbindung vorhanden 
                    verbindungsueberpruefung = false;
                    VerbindungErzeugen();
                }
                else
                {
                    verbindungsueberpruefung = false;
                }
            }
        }

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Funktioniert! :)

Habe jetzt schon mehrfach im Internet bei anderen Fragenden das Ray von und zu einem Objekt gesehen. Kannst du mir verraten, was du mit der Variable diretion machst? Das wäre dann doch die Differenz zwischen den Vectoren oder?

Und abgefragt hatte ich ja, du nutzt dazu CompareTag, ich nur Tag, gibts da einen größeren Unterschied?

Link zu diesem Kommentar
Auf anderen Seiten teilen

"direction" ist der Richtungsvektor zwischen diesen beiden Punkten und in diesem Fall auch die Differenz. Dieser wird gebildet aus Zielpunkt - Startpunkt, man kann diesen auch noch zusätzlich "Normalisieren" (weil die Distanz bei einem Richtungsvektor keine Rolle spielt und einige Funktionen ggf. einen normalisierten Vektor benötigen), dann ist es nur noch der Richtungsvektor. Ich vermute aber Unity normalisiert den Richtungsvektor intern sowieso noch einmal, daher habe ich es weggelassen.
Der Unterschied betrifft die Performance, CompareTag ist schneller (da es intern schneller ausgewertet werden kann) und damit man es sich "angewöhnt" , habe ich es gleich mit korrigiert.
 

Link zu diesem Kommentar
Auf anderen Seiten teilen

So langsam komm ich zu Stuhle, ich habe nun mehrere LineRenderer in ein Array gepackt, die sich nun nach und nach ablösen.

Nun bin ich seit heute Morgen am Material und etwas am verzweifeln.

Mein Problem ist, zum einen wirkt die Line etwas zerhackt an den Kanten, zum anderen ist die Textur gestreckt. Auch das Ende sieht etwas suboptimal aus. :unsure:

Habt ihr ne Idee gegen die Texturstreckung und das Ende bzw. das zerhackte?

Was ich auch merkwürdig finde ist, dass er einen Alpha in Unity benutzt, obwohl ich gar keinen Alpha in der Grafik habe.

Ich nutze den Shadder Particles/Additive, die genutzte Textur ist im DDS (DXT1) Format.

Hier mal Textur und Ansicht im Spiel:

 

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Gegen die Streckung, könnte hier eine Umstellung auf Repeat ("Tile" oder die anderen beiden Modi) helfen:
https://docs.unity3d.com/ScriptReference/LineRenderer-textureMode.html
https://docs.unity3d.com/ScriptReference/LineTextureMode.html

Beim "zerkackt" habe ich keine richtige Idee, vielleicht muss du die "Linie" hier besser "abschließen".

Link zu diesem Kommentar
Auf anderen Seiten teilen

Beides hängt vermutlich zusätzlich vom Shader und von der Einstellung der Textur ab (Clamp / Repeat).

Und das hier musst du auch noch setzen:
https://docs.unity3d.com/ScriptReference/Material.SetTextureScale.html

Hier ist aber Beispielcode, welches einen Partikelrenderer verwendet. Den würde ich einfach mal so übernehmen und testen und dann den Mode auf "Tile" umstellen :
https://docs.unity3d.com/ScriptReference/LineRenderer-textureMode.html

Link zu diesem Kommentar
Auf anderen Seiten teilen

Wie gesagt fürs "Feintuning" der UVs der Linien ist dieser Mode zuständig. Aber wie die anderen beiden Modi "PerSegment" genau funktionieren und zu verwenden sind kann ich dir auch nicht sagen, aber könnte mir vorstellen, daß du damit das Problem bei der Rundung korrigieren kannst:
https://docs.unity3d.com/ScriptReference/LineTextureMode.html

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hab ich alles schon heute im laufe des Tages durchgetestet.. Muss ich weiter schauen, irgendwie muss es gehen, hab ich schon ohne Streckung gesehen. Vielleicht muss ich was neu berechnen.

Wegen RepeatPerSegment, ja eigentlich schon. Ich habs nun noch mal mit dem Corner Verticies versucht. So lange die Kurve aus einem Teil besteht ist alles gut, danach fangen die Probleme an. Wie es aussieht, versucht er mit RepeatPerSegment die gesamte 512x512 Textur in ein Teilstück zu pressen, aus dem eine Kurve besteht.

Also besteht das Kreissegment aus 3 Teilen packt er in jedes Teilsegment die Textur hinein, dadurch sieht es so zusammengepresst aus.

Rein im Uity Manual benutzt man im Tutorial "Particles/Additive" - werde mal andere testen, da es aber für Mobile sein muss, hab ich net viel Auswahl.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

Dieses Thema ist jetzt archiviert und für weitere Antworten gesperrt.

×
×
  • Neu erstellen...