Jak narysować kontur w Unity3d?

13

Próbuję podkreślić prostokąt o dowolnej wysokości. Pomyślałem, że najłatwiej to zrobić, tworząc osobny obiekt „box”, który obrysuje prostokąt.

Próbowałem zarówno z MeshRenderer + Transparent Texture, jak i LineRenderer, aby obrysować cztery punkty prostokąta. Żadne z nich nie są bardzo zadowalające.

wprowadź opis zdjęcia tutaj

(Renderowanie linii włączone na środku, wyskalowany sześcian po prawej stronie)

Jak właściwie to zrobić? Próbuję uzyskać coś w rodzaju lewego prostokąta - prosty obwód o stałej szerokości przez cztery wybrane przeze mnie punkty.

Raven Dreamer
źródło

Odpowiedzi:

8

Zastosowanie GUI.Box().

Jeśli potrzebujesz tylko prostokąta 2D, najlepszym rozwiązaniem jest GUI. Utwórz nowy GUIStyle, używając prostego prostokąta jako tekstury (wnętrze prostokąta powinno być oczywiście przezroczyste), ustaw jego Borderwartość, aby nie była rozciągnięta, i wywołaj GUI.Box(new Rect(...),"",myGuiStyle);.

Możesz użyć Camera.WorldToScreenPointmetody, jeśli chcesz zarysować coś we współrzędnych świata (tj. 3D), pamiętaj tylko, że we współrzędnych świata Unity y przebiega od dołu do góry, aw GUI y od góry do dołu.

Przykład kodu:

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);
}
Nieważne
źródło
O ile się nie mylę, GUI.Box () zawsze będzie rysowany na dowolnych obiektach w scenie? Czy można mieć element GUI za lub częściowo za obiektem gry?
Raven Dreamer
Tak, GUI jest zawsze rysowane po innych obiektach. Jest pewien hack, którego można użyć do renderowania osobnej kamery na GUI, ale to trochę niewygodne.
Nevermind
Ta odpowiedź dała mi to, czego potrzebowałem, ale na razie pozostaję otwarty, w nadziei, że jest lepszy sposób na zrobienie tego w ogólnym przypadku.
Raven Dreamer
Zaakceptowałem twój przykład dodawania kodu edycji i naprawiłem błąd GUI-y-up-down.
Nevermind,
1

Poniżej znajduje się podejście inne niż shader.

Pomyśl o swoim polu 2d jako o niczym więcej niż czterech liniach, gdzie każda linia jest rozciągnięta tylko w jednym wymiarze (pozostałe dwa wymiary są przekrojem krawędzi). To tak, jakbyś budował pudełko w prawdziwym życiu, gdzie składasz ze sobą różne długości drewna, które mają taki sam rozmiar przekroju.

Mając to na uwadze, możesz opracować Komponent, powiedzmy BoxBuilder, który po podłączeniu do GameObject tworzy i zarządza czterema potomnymi GameObjectami. Każdy obiekt gry podrzędnej jest jedną z krawędzi pudełka i może być po prostu sześcianem 3d rozciągniętym tylko w jednym wymiarze. Z widthi heightokreślonym BoxBuilderpoziomie, można obliczyć niezbędną pozycjonowanie i niejednorodnej skalę czterech krawędziach potomnych. To będzie dużo pos.x=w/2, pos.y=h/2, ..., scale.x=h, scale.y=w, itd rodzaj kodu.

Chociaż uważam, że prosisz tylko o 2D, zauważ, że ten sam pomysł można zastosować w razie potrzeby do ramek 3d, gdzie BoxBuilderteraz musi utworzyć i zarządzać 12 krawędziami podrzędnymi, ale ponownie skalować tylko każdą krawędź w jednym lokalnym wymiarze.

DuckMaestro
źródło
Wykonalne, ale jest zbyt skomplikowane.
Nevermind
Gdybym musiał to zrobić w ten sposób, po prostu użyłbym rendererów 4 linii ...
Raven Dreamer
To jednak poprawnie obsługuje głębokość.
DuckMaestro
1

Prostym sposobem jest użycie modułu cieniującego z dwoma przejściami: pierwsze przejście wykorzystuje moduł cieniujący wierzchołek do nieznacznego skalowania obiektu, a moduł cieniujący piksele do pokolorowania go jednolitym kolorem dopasowanym do koloru, który ma mieć kontur, oraz wtedy drugie przejście wykonuje normalne renderowanie.

UBSophung
źródło
Czy możesz podać przykładowy kod, aby lepiej wyjaśnić, co masz na myśli?
Raven Dreamer
Działa to tylko w przypadku obiektów wypukłych (takich jak prostokąt), których początek znajduje się w środku geometrycznym.
rootlocus
1

Miałem ten sam problem z tworzeniem konspektu, tyle że musiałem stworzyć „obrys” dla kostki 3d i znalazłem nowy sposób na zrobienie tego, czego nigdzie indziej nie widziałem online.

Na poniższym obrazku są dwa kształty z konturami. Ten po prawej to sześcian zbudowany z LineRenderer, który tworzy płaskie ściany, które zawsze zwracają się w stronę użytkownika. Uważam tę metodę za super glitchy, z pojawiającymi się przypadkowymi „pociągnięciami”, które naśladują trójkąty tworzące twarz.

Ten po lewej to moja „innowacja”, w której 12 oddzielnych, chudych kostek tworzy coś, co wygląda jak kontur. Aby zmienić rozmiar „obrysu” w konspekcie, muszę zwiększyć / zmniejszyć rozmiary dwóch boków każdej z 12 chudych kostek. Działa to również w przypadku konturów 2d. Po prostu zastosuj materiał, aby zmienić kolor i voila!

dwa różne kontury

Na tym obrazie widać szczegół struktury tego sześcianu. Wszystko to można było utworzyć w czasie wykonywania, ale zrobiłem to ręcznie i użyłem go jako prefabrykatu.

Szczegół

ow3n
źródło
0

Obecnie mam do czynienia z tym samym problemem, a moje rozwiązanie jest dokładnie tym, co sugerują DuckMaestro i Raven Dreamer - Mają skrypt, który tworzy w czasie wykonywania 4 obiekty potomne, z których każdy reprezentuje bok granicy i dołącza do każdego z nich renderery linii.

W moim przypadku musiałem ciągle zmieniać rozmiar ramki, aby utrzymać ją wokół mojego obiektu (siatka tekstowa [która korzysta z renderera siatki] dla niestandardowego pola tekstowego), więc przy każdej aktualizacji robiłem to:


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() po prostu uzyskuje dostęp do odpowiedniego mechanizmu renderującego linie (określonego przez pierwszy parametr) i ustawia odpowiednio jego początek i koniec na pierwszy i drugi wektor.

Zauważ, że użyłem rendererjako odniesienia dla rozmiaru, ale oczywiście możesz użyć dowolnego prostokąta, o ile x, y jest lewym górnym rogiem.

Z tego, co mogę powiedzieć, działa to naprawdę dobrze, wygląda świetnie w grze, ponieważ mogę z łatwością przesuwać graniczy obiekt we wszystkich 3 osiach (nawet go obracać, a ponieważ renderery linii zawsze są skierowane w stronę kamery, nie wygląda to dziwnie) i jest nie trudne do wdrożenia.

rzymski
źródło