Jak zaimplementować miękkie obszary krawędzi z cząstkami

13

Moja gra jest tworzona przy użyciu Phasera, ale samo pytanie jest niezależne od silnika.

W mojej grze mam kilka środowisk, głównie wielokątne obszary, do których mogą się poruszać postacie graczy. Na przykład lód, ogień, trucizna itp. Elementem graficznym tych obszarów jest sam obszar wielokąta wypełniony kolorem oraz cząstki odpowiedniego typu (w tym przykładzie odłamki lodu). Tak właśnie to obecnie wdrażam - z maską wielokątów pokrywającą kafelek wzorem cząstek:

wprowadź opis zdjęcia tutaj

Twarda krawędź wygląda źle. Chciałbym poprawić, wykonując dwie rzeczy: 1. Spraw, aby obszar wypełnienia wielokąta miał miękką krawędź i wtopił się w tło. 2. Niech niektóre odłamki wychodzą z obszaru wielokąta, aby nie zostały wycięte na środku i obszar nie miał linii prostej

na przykład (makieta):

wprowadź opis zdjęcia tutaj

Myślę, że 1 można osiągnąć poprzez rozmycie wielokąta, ale nie jestem pewien, jak poradzić sobie z 2.

Jak poszedłbyś na wdrożenie tego?

OpherV
źródło
2
To wygląda niesamowicie. Czy mogę prosić o link do Twojej gry ? :)
ashes999
@GameAlchemist Oto obraz kafelka i.imgur.com/y54CeQ9.png
OpherV
@ ashes999 To wciąż praca w toku. Na pewno opublikuję tutaj link, gdy go uruchomię :)
OpherV
Zauważ, że jest to przezroczysty plik PNG z białymi obiektami. Jeśli zobaczysz go na białym tle (tak jak często podczas otwierania zdjęcia w przeglądarce) nic nie zobaczysz
OpherV
@OpherV, byłoby świetnie. Po prostu edytuj swoje pytanie i opublikuj link - to prawdopodobnie najbardziej
dyskusyjny sposób na reklamę

Odpowiedzi:

2

1.Jeśli chcesz czegoś zbliżonego do makiety, użyłbym cząstek (nie musi to być w pełni rozdmuchany układ cząstek).

Renderuj swoje cząstki w postaci wielokąta na RenderTexture. Upewnij się, że używasz mieszania dodatków do cząstek. Cząsteczki wewnątrz wielokąta płynnie się ze sobą łączą, a cząsteczki na zewnątrz zapewniają miękką krawędź, której chcesz. (Przykład efektu można obejrzeć w tym filmie na youtube: Wideo cząstek addycyjnych Teraz wyrenderuj RenderTexture na ekranie głównym i gotowe. RenderTexture jest konieczny, aby cząstki nie zlewały się z tłem.

Możesz spróbować umieścić trójkąty bezpośrednio na teksturze cząstek i zobaczyć, jak to działa. W przeciwnym razie renderuj je na „zupce cząsteczkowej” jako osobną warstwę.

Utworzono szybką makietę w zaktualizowanym jsfiddle, który wygląda tak: Próbny Zaktualizowaną wersję demo można znaleźć tutaj

2. Każda cząstka ma prędkość i pochodzenie. Kiedy twój gracz dotknie wielokąta, zmieniasz prędkość każdej cząstki proporcjonalnie do prędkości gracza. Im dalej cząstka znajduje się od gracza, tym mniej na nią wpływa prędkość gracza.

Wzór na obliczenie prędkości cząstek byłby mniej więcej taki:

//player.velocity and particle.velocity are vectors 
//k is a factor to enhance or weaken the influence of players velocity
var distanceToPlayer = (player.position - particle.position).length();
particle.velocity = particle.velocity + ((k * player.velocity) + particle.velocity) * (1/distanceToPlayer);

Aby obliczyć położenie cząstki, umieść to w metodzie aktualizacji:

var speedY = -(springConstant * (particle.position.y - particle.origin.y)) - (dampingFactor * particle.velocity.y);
var speedX = -(springConstant * (particle.position.x - particle.origin.x)) - (dampingFactor * particle.velocity.x);

particle.position.y = particle.position.y + speedY;
particle.position.x = particle.position.x + speedX;

particle.velocity.x = particle.velocity.x + speedX;
particle.velocity.y = particle.velocity.y + speedY;

To powinno dać ci „płyn”, w którym każda cząstka obraca się wokół swojego źródła, gdy gracz miesza płyn. SpringConant zmienia, o ile cząstka odchyla się od swojego źródła, oraz tłumienie Czynnik, jak szybko cząstka zatrzymuje się. Być może będziesz musiał poprawić kod, ponieważ jest to zmodyfikowana wersja symulacji 1d, której używam w swojej grze.

Teraz z wersją demonstracyjną: Demo Ulepsz 3 stałe na górze, aż płyn zachowa się tak, jak chcesz.

zilluss
źródło
To naprawdę ciekawy pomysł do rozwiązania # 1! Lubię to. Jak sugerowałbyś implementację ruchu / odstępów trójkąta?
OpherV
1
Zredagowałem oryginalny post, aby odpowiedzieć na pytanie nr 2.
zilluss
dodano małe demo efektu cząsteczkowego. Myślę, że można uzyskać pożądany efekt przy odrobinie poprawy.
zilluss
Skończyło się na twoim rozwiązaniu, jest to najbliższy efekt, który chciałem osiągnąć. Dzięki! Jedno pytanie - w jaki sposób zlikwidowałbym sprężystość w twoich przykładach, aby naprawdę niewielkie ruchy nie spowodowały, że cząstki będą skakać na tak duże odległości?
OpherV
Możesz bawić się z tłumikiem i sprężyną. Wysokie tłumienie przyspieszy zatrzymanie cząstek. Niższa stała sprężyny obniża prędkość cząstek. Niestety obie wartości sprawiają, że zupa cząsteczkowa wydaje się bardziej lepka. Zamiast tego, gdy odtwarzacz dotyka cząstki, możesz zablokować maksymalną prędkość, którą gracz dodaje do cząstki, w następujący sposób: ) + cząstek.velocity.x) * (1 / distanceToPlayer)); zacisk patrz: stackoverflow.com/a/11409944
zilluss
4

Moje przemyślenia na temat tych dwóch punktów:

  1. Możesz użyć rozmycia cieniowania, ale będzie to bardzo kosztowne. Zamiast tego narysowałbym dodatkową granicę trójkątów, które zanikają od półprzezroczystego w środku do przezroczystego na krawędziach, aby zasymulować „rozmycie”. Zrobiłem to w mojej grze i działa całkiem dobrze. Oto dwa zrzuty ekranu przypominającego tęczowy wzmacniacz w mojej grze.

    wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj

    Zewnętrzna granica ma gradient. W mojej sytuacji granica nie zaczyna się od tego samego zmętnienia co część wewnętrzna, ale jeśli ustawisz te nieprzezroczystości na równe, będziesz miał przyjemny efekt zanikania.

  2. Zaprogramowałbym układ cząstek i sprawiłby, by cząstki podążały za wielokątem. Dzięki temu nie musisz się martwić o to, co stanie się na krawędziach. Dynamika układu cząstek zapewni, że cząstki będą wewnątrz wielokąta i równomiernie rozmieszczone. Możesz spróbować zmusić najbliższe cząstki do odpychania się, ale z dużą ilością „masy”, abyś miał bezwładność i wyglądał gładko. Aby to zrobić szybko, istnieją pewne możliwości, ale dużym problemem będzie złożoność czasowa wzajemnego popychania mechanizmu. Jeśli sprawisz, że każda cząstka pcha każdą inną cząsteczkę, będziesz miał O (n ^ 2), co nie jest dobre, jeśli masz na przykład 100 cząstek w twoim systemie. Dobrym poczytaniem o tym, jak możesz to zoptymalizować, jest ta prezentacja PixelJunk:http://fumufumu.q-games.com/gdc2010/shooterGDC.pdf

    Prostszym podejściem byłoby połączenie każdej cząstki z trzema lub czterema innymi cząsteczkami podczas konstruowania układu cząstek. Teraz każda cząstka będzie musiała przepchnąć tylko cztery inne cząstki, z którymi jest połączona. Takie podejście uniemożliwia jednak zawirowania w układzie cząstek. Nie wydaje się to jednak problemem, ponieważ twoja obecna sytuacja używa statycznej tekstury. Podejście to będzie O (n), co jest świetne.

Martijn Courteaux
źródło
Dzięki! 1. Trudno mi to sobie wyobrazić. Czy możesz zamieścić makietę tego, jak to wygląda \ działa w twojej grze? 2. Ciekawe! przeczyta o tym
OpherV
Dzięki za zdjęcia. Widzę, jak to by działało w kształcie tuby lub linii, ale jak to będzie działać z wielokątem? Czy gradienty trójkątów rzutowane z każdej strony nie będą ładnie układać się ...?
OpherV
Dopasuj je ładnie. To jest pomysł. Obejmuje to matematyczne i obliczeniowe punkty przecięcia itp. Możesz także podzielić granicę i wygładzić ją. Mnóstwo możliwości.
Martijn Courteaux
3

Pomysł, który miałem podczas czytania twojego postu był następujący:
• zbuduj zestaw kafelków, których będziesz używać w swoich obszarach.
• renderuj wielokąt powierzchniowy na małym tymczasowym kanwie w rozdzielczości kafelków (np. Jeśli kafelki mają format 16X16, renderuj w niższej rozdzielczości (16X, 16X)).
• użyj tego tymczasowego płótna, aby zdecydować, czy renderować kafelki, czy też nie na głównym kanwie:
jeśli punkt jest ustawiony na kanwie o niskiej rozdzielczości, narysuj „losową” płytkę na głównym kanwie. jeśli punkt jest tylko sąsiadem punktu ustawienia, narysuj „losową” płytkę o mniejszym stopniu krycia (w celu przejścia) na głównym obszarze roboczym.

Bałem się, że obniżenie rozdzielczości spowoduje efekt blokowy, ale nawet przy płytkach 20X20 wygląda całkiem dobrze:

wprowadź opis zdjęcia tutaj

Kroki w tym celu: weź swój wielokąt:
wprowadź opis zdjęcia tutaj

Narysuj wielokąt w rozdzielczości kafelków: wprowadź opis zdjęcia tutaj(!! tak, to czerwona rzecz w centrum handlowym).

Następnie użyj imageData płótna o niskiej rozdzielczości, aby zdecydować, czy narysować płytkę lub notatkę.
Jeśli piksel jest ustawiony na kanwie o niskiej rozdzielczości, oznacza to, że powinniśmy narysować kafelek: wybierz „losowy” indeks kafelków, aby wybrać kafelek do narysowania. Ten losowy indeks powinien być zawsze taki sam dla danego obszaru / płytki.
Jeśli punkt jest pusty, ale znajduje się obok wypełnionego punktu, narysuj również płytkę, ale z półprzezroczystością.

wprowadź opis zdjęcia tutaj

Narysujmy więc płytki:

wprowadź opis zdjęcia tutaj

W przypadku wielokąta rysuję tylko kilkukrotnie wielokąt, zmniejszając go i zwiększając krycie przy każdym losowaniu (można również użyć funkcji globalCompositeOperation „jaśniej”).

wprowadź opis zdjęcia tutaj

Po dodaniu wszystkiego daje:

wprowadź opis zdjęcia tutaj

skrzypce jest tutaj:

http://jsfiddle.net/gamealchemist/T7b4Y/

daj mi znać, jeśli to pomoże.

GameAlchemist
źródło
Dziękujemy za opracowanie rozwiązania i dostarczenie kodu! Co rozumiesz przez „Następnie użyj imageData płótna o niskiej rozdzielczości, aby zdecydować, czy narysować kafelek lub notatkę”. Jak korzystasz z danych małego obrazu, aby zdecydować, gdzie \ narysować duży obraz? I używając tej metody, jak mogę uniknąć nakładania się trójkątów, aby uzyskać odstępy podobne do mojego makiety?
OpherV
Mam na myśli kompletny test boolowski: Czy muszę wypełnić ten kafelek? , prawda lub fałsz, podaje test testu niskiej rozdzielczości na imageData [(x + y * szerokość) * 4]! = 0, to znaczy == 'czy narysowałem coś na kanwie niższej rozdzielczości w to miejsce na kafelki? ”. Następnie, jeśli tak, używam opracowanej formuły wypełnionej liczbą pierwszą (getRandomNumber), aby przejść od (x, y) do „losowego” indeksu kafelków, który zawsze zapewni ten sam numer dla danego obszaru + (x, y). Nie rozumiem, jak rzeczy mogą się nakładać, jeśli poprawnie zdefiniujesz swój wielokąt, więc nie dostaję drugiego pytania.
GameAlchemist
1

Tylko pomysł:

W kawałku „kawałki” mają maksymalnie około 80 pikseli szerokości. Sugerowałbym, tworząc 2 obszary. Rysujesz oryginalny wielokąt i wykonujesz jego makietę w odległości około 80 pikseli od każdej krawędzi. Następnie rysujesz płytki w większym wielokącie.

Następnie wszystkie „kawałki”, które mają piksel poza wewnętrznym wielokątem i bez przecięcia z wewnętrznym wielokątem, możesz zalać je wypełnieniem przezroczystością.

To jest bardzo wysoki poziom, ale pomyślałem, że podzielę się nim z tobą. Jest tu wiele szczegółów do ustalenia.

Surowy obraz do zademonstrowania:

wprowadź opis zdjęcia tutaj

Gdyby wewnętrzny czarny wielokąt był oryginałem, skasowałbyś wszystkie wielokąty czerwoną kropką.

użytkownik46560
źródło
Jak byś sprawdził przecięcie elementów osadzonych w obrazie płytki z wewnętrznym wielokątem?
OpherV
@OpherV Uhmm, możesz ustalić, czy punkt X jest w wielokącie, prawda? Możesz zapętlić wszystkie piksele w zewnętrznym wielokącie, a dla każdego białego piksela sprawdź zalanie wszystkich pikseli i sprawdź, czy nie znajdują się w wewnętrznej / zewnętrznej części. Nie brzmi to jednak szczególnie wydajnie, ale kiedy już coś masz, możesz pomyśleć o sposobach optymalizacji?
user46560
To przetwarzanie końcowe na CPU. Trudno samo zoptymalizować. Twój pomysł może działać dobrze, jeśli wewnętrzny wielokąt i zewnętrzny zostaną połączone trójkątnym paskiem. OP może dodać informacje o atrybucie wierzchołka i oznaczyć każdy wierzchołek jako wewnątrz lub na zewnątrz. Następnie w module cieniującym wierzchołek nieprzezroczystość / alfa można interpolować liniowo z wartości mniej przezroczystej do wartości całkowicie nieprzezroczystej.
teodron
@teodron To jest punkt, który próbowałem zrobić w odpowiedzi. To pomysł na wysokim poziomie i tak naprawdę nie jestem twórcą gier. Właśnie miałem pomysł.
user46560
@ user46560 tak, ale w ten sposób nie musisz obliczać, jakie piksele są w tym paśmie między wielokątami .. ani na GPU, ani na CPU. Ponieważ, jak mówisz, nie jesteś przyzwyczajony do sposobów tworzenia gier, zasługujesz na +1 za swój pomysł :).
teodron