Wie kann ich in Unity3d einen Umriss zeichnen?

13

Ich versuche ein Rechteck beliebiger Höhe hervorzuheben. Ich dachte, der einfachste Weg, dies zu tun, wäre, ein separates "Box" -Spielobjekt zu erstellen, das das Rechteck umreißt.

Ich habe sowohl mit einem MeshRenderer + Transparent Texture als auch mit einem LineRenderer versucht, die vier Punkte des Rechtecks ​​zu umreißen. Weder sind sehr zufriedenstellend.

Bildbeschreibung hier eingeben

(Linienrenderer in der Mitte, skalierter Würfel rechts)

Was ist der richtige Weg, um dies zu tun? Ich versuche, so etwas wie das linke Rechteck zu erhalten - einen einfachen Umfang fester Breite durch vier Punkte meiner Wahl.

Raben-Träumer
quelle

Antworten:

8

Verwenden Sie GUI.Box().

Wenn Sie nur ein 2D-Rechteck benötigen, ist die grafische Benutzeroberfläche die richtige Wahl. Erstellen Sie einen neuen GUIStyle mit einem einfachen Rechteck als Textur (die Innenseite des Rechtecks ​​sollte natürlich transparent sein), richten Sie seinen BorderWert so ein, dass er nicht gestreckt wird, und rufen Sie auf GUI.Box(new Rect(...),"",myGuiStyle);.

Sie können die Camera.WorldToScreenPointMethode verwenden, wenn Sie etwas in Weltkoordinaten (z. B. 3D) skizzieren möchten. Denken Sie jedoch daran, dass in Unitys Weltkoordinaten y von unten nach oben und in der GUI von oben nach unten verläuft.

Code Beispiel:

void OnGUI()
{
    //top left point of rectangle
    Vector3 boxPosHiLeftWorld = new Vector3(0.5f, 12, 0);
    //bottom right point of rectangle
    Vector3 boxPosLowRightWorld = new Vector3(1.5f, 0, 0);

    Vector3 boxPosHiLeftCamera = Camera.main.WorldToScreenPoint(boxPosHiLeftWorld);
    Vector3 boxPosLowRightCamera = Camera.main.WorldToScreenPoint(boxPosLowRightWorld);

    float width = boxPosHiLeftCamera.x - boxPosLowRightCamera.x;
    float height = boxPosHiLeftCamera.y - boxPosLowRightCamera.y;


     GUI.Box(new Rect(boxPosHiLeftCamera.x, Screen.Height - boxPosHiLeftCamera.y, width, height),"", highlightBox);
}
Keine Ursache
quelle
Wenn ich mich nicht irre, wird GUI.Box () immer auf Objekte in der Szene gezeichnet. Ist es möglich, ein GUI-Element hinter oder teilweise hinter einem Spielobjekt zu haben?
Raven Dreamer
Ja, GUI wird immer nach anderen Objekten gezeichnet. Es gibt einen Hack, mit dem Sie eine separate Kamera über der Benutzeroberfläche rendern können, der jedoch etwas unhandlich ist.
Nevermind
Diese Antwort brachte mir das, was ich brauchte, aber ich halte sie vorerst offen in der Hoffnung, dass es im allgemeinen Fall einen besseren Weg gibt, dies zu tun.
Raven Dreamer
Ich habe Ihr Beispiel zum Hinzufügen von Code akzeptiert und den GUI-y-Upside-Down-Fehler darin behoben.
Nevermind
1

Nachfolgend sehen Sie einen Nicht-Shader-Ansatz.

Stellen Sie sich Ihre 2D-Box als nicht mehr als vier Linien vor, wobei jede Linie nur in einer Dimension gedehnt wird (die anderen beiden Dimensionen sind der Querschnitt der Kante). Dies ähnelt dem Aufbau einer Box im echten Leben, bei der Sie verschiedene Holzlängen zusammenfügen, die alle die gleiche Querschnittsgröße haben.

In diesem Sinne können Sie beispielsweise eine Komponente entwickeln BoxBuilder, die, wenn sie an ein GameObject angehängt ist, vier untergeordnete GameObjects erstellt und verwaltet. Jedes untergeordnete Spielobjekt ist eine der Kanten Ihrer Box und kann einfach ein 3D-Würfel sein, der nur in einer Dimension gespannt ist. Mit einer widthund heightdefinierte BoxBuilderEbene können Sie die notwendige Positionierung und ungleichmäßige Skala der vier Kinder Kanten berechnen. Es wird eine Menge sein pos.x=w/2, pos.y=h/2, ..., scale.x=h, scale.y=wusw. Art Code.

Obwohl ich glaube, dass Sie nur nach 2-D fragen, beachten Sie, dass dieselbe Idee bei Bedarf auf 3D-Boxen angewendet werden kann, bei denen BoxBuilderjetzt 12 untergeordnete Kanten erstellt und verwaltet werden müssen, aber auch hier nur jede Kante in einer lokalen Dimension skaliert werden muss.

DuckMaestro
quelle
Praktisch, aber viel zu kompliziert.
Nevermind
Wenn ich es so machen müsste, würde ich einfach 4 Linienrenderer verwenden ...
Raven Dreamer
Dies ist jedoch für die Tiefe ausreichend.
DuckMaestro
1

Eine einfache Möglichkeit besteht darin, einen Shader mit zwei Übergängen zu verwenden: Beim ersten Durchgang wird das Objekt mit dem Scheitelpunkt-Shader etwas vergrößert und mit dem Pixel-Shader so eingefärbt, dass es der Farbe entspricht, die die Kontur haben soll dann führt der zweite Durchgang das reguläre Rendern durch.

UBSophung
quelle
Könnten Sie ein Codebeispiel geben, um besser zu erklären, was Sie meinen?
Raven Dreamer
Dies funktioniert nur bei konvexen Objekten (wie einem Rechteck), deren Ursprung im geometrischen Zentrum liegt.
Rootlocus
1

Ich hatte das gleiche Problem beim Erstellen einer Gliederung, außer dass ich den "Strich" für einen 3D-Würfel erstellen musste, und fand eine neue Möglichkeit, dies zu tun, die ich online nirgendwo anders gesehen habe.

Im Bild unten gibt es zwei Formen mit Umrissen. Der eine auf der rechten Seite ist ein mit LineRenderer konstruierter Würfel, der flache Flächen bildet, die sich immer dem Benutzer zuwenden. Ich fand diese Methode super patzig, mit zufälligen "Strichen", die die Dreiecke imitierten, aus denen das Gesicht besteht.

Die eine auf der linken Seite ist meine „Innovation“ mit 12 separaten dünnen Würfeln, die einen Umriss bilden. Um die Größe des „Strichs“ in der Kontur zu ändern, muss ich zwei Seiten jedes der 12 dünnen Würfel vergrößern / verkleinern. Dies würde auch für 2D-Konturen funktionieren. Wenden Sie einfach ein Material an, um die Farbe und das Voila zu ändern!

zwei verschiedene Umrisse

In diesem Bild sehen Sie ein Detail der Struktur dieses Würfels. Dies könnte alles zur Laufzeit erstellt werden, aber ich habe es von Hand gemacht und als Fertighaus verwendet.

Detail

ow3n
quelle
0

Ich stehe derzeit vor dem gleichen Problem, und meine Lösung ist genau das, was DuckMaestro und Raven Dreamer vorgeschlagen haben - Erstellen Sie ein Skript, das zur Laufzeit 4 untergeordnete Objekte erstellt, von denen jedes eine Seite des Rahmens darstellt, und fügen Sie jedem einen Linienrenderer hinzu.

In meinem Fall musste ich die Größe des Rahmens ständig ändern, um ihn um mein Objekt (ein Textnetz [das einen Mesh-Renderer verwendet] für ein benutzerdefiniertes Textfeld) zu erhalten. Bei jedem Update habe ich Folgendes ausgeführt:


float width = Mathf.Max(renderer.bounds.size.x + paddingX * 2, minWidth);      
float x = renderer.bounds.center.x - width / 2;
float height = renderer.bounds.size.y + paddingY * 2;
float y = renderer.bounds.center.y - height / 2;

AlterBorder(0, new Vector3(x - thickness / 2, y, 0), new Vector3(x + width + thickness / 2, y, 0)); //Bottom edge going left to right
AlterBorder(1, new Vector3(x + width, y + thickness / 2, 0), new Vector3(x + width, y + height - thickness / 2, 0)); //Right edge going bottom to top
AlterBorder(2, new Vector3(x + width + thickness / 2, y + height, 0), new Vector3(x - thickness / 2, y + height, 0)); //Top edge going right to left
AlterBorder(3, new Vector3(x, y + height - thickness / 2, 0), new Vector3(x, y + thickness / 2, 0)); //Left edge going top to bottom

AlterBorder() greift einfach auf den entsprechenden Linienrenderer zu (angegeben durch den ersten Parameter) und setzt dessen Anfang und Ende auf den ersten bzw. zweiten Vektor.

Beachten Sie, dass ich rendererals Referenz für die Größe verwendet habe, aber Sie können natürlich jedes Rechteck verwenden, solange x, y die obere linke Ecke ist.

Soweit ich weiß, funktioniert dies sehr gut, sieht im Spiel großartig aus, da ich mein umrandetes Objekt leicht in allen drei Achsen bewegen kann (sogar drehen und da Linienrenderer immer zur Kamera zeigen, sieht es nicht sonderbar aus) und ist es auch nicht schwer zu implementieren.

römisch
quelle