Duszki stają się rozmyte z prędkością

9

Po dodaniu prędkości do mojej gry, czuję, że moje tekstury drżą. Myślałem, że to tylko moje oczy, aż w końcu uchwyciłem to na zrzucie ekranu:

wprowadź opis zdjęcia tutaj

Ten po lewej jest tym, co renderuje się w mojej grze; ten po prawej to oryginalny duszek, wklejony. (To jest zrzut ekranu z Photoshopa, powiększony 6x.)

Zauważ, że krawędzie są aliasingowe - wygląda prawie jak renderowanie subpikseli. W rzeczywistości, gdybym nie zmusił moich duszków (które mają pozycję i prędkość jako liczby całkowite) do rysowania przy użyciu wartości całkowitych, przysięgałbym, że MonoGame rysuje z wartościami zmiennoprzecinkowymi. Ale tak nie jest.

Co może być przyczyną rozmycia tych rzeczy? Nie dzieje się to bez zastosowania prędkości.

Mówiąc ściślej, moja SpriteComponentklasa ma Vector2 Positionpole. Kiedy dzwonię Draw, zasadniczo używam new Vector2((int)Math.Round(this.Position.X), (int)Math.Round(this.Position.Y))stanowiska.

Miałem wcześniej błąd, w którym drgałyby nawet obiekty stacjonarne - to dlatego, że użyłem prostego Positionwektora i nie zaokrąglałem wartości ints. Jeśli użyję Floor/ Ceilingzamiast okrągłej, duszek tonie / unosi się (różnica o jeden piksel w jedną stronę), ale nadal rysuje się rozmazany.

ashes999
źródło
1
Czy można to powiązać z zastosowanym filtrowaniem tekstur?
OriginalDaemon,
Czy masz kod swojego shadera? Niektóre osoby dodają pół piksela na obu osiach, aby wyśrodkować piksele. Nie jestem jednak pewien, myślę, że to jedyna rzecz związana z DirectX.
William Mariager,
3
Dezaktywacja filtrowania jest obejściem, a nie rozwiązaniem.
Archy,
1
Renderowanie nic nie wie o twojej prędkości, więc ani pozycja, rozmiar, ani nic innego, co przechodzisz do SpriteBatch.Draw jest inny lub błąd występował przed dodaniem prędkości. Jak dokładnie wywołać SpriteBatch.Draw?
Archy,
1
@ ashes999 debugowałeś to połączenie i sprawdzałeś to X, this.Y i this.origin? Co to jest ustawiony this.origin? Zasadniczo nie ma innego sposobu, aby wynik renderowania był inny, gdy to wywołanie Draw jest takie samo.
Archy,

Odpowiedzi:

3

Cóż, to jest zawstydzające.

Okazuje się, że nie rysowałem przy użyciu pozycji liczb całkowitych, ale pozycji zmiennoprzecinkowych. Archy wskazał mi właściwą drogę swoim komentarzem@ashes999 did you debug this call and checked this.X, this.Y and this.origin?

Gdy tylko dodałem instrukcję śledzenia, zauważyłem, że nic nie wytropiło. Okazuje się, że moja SpriteComponentklasa poprawnie używała wartości całkowitych, ale moje SpriteSheetComponentnadal używały wartości zmiennoprzecinkowych z surowegoVector2 Position .

Chociaż nie próbowałem filtrować i blokować tekstur, podejrzewałem, że to nie jest poprawna zmiana, ponieważ rysowałem obraz 2D o tej samej szerokości i wysokości co obraz źródłowy (bez skalowania).

ashes999
źródło
1
Gotowe, cieszę się, że to znalazłeś!
Archy
2

XNA używa DirectX9, który wykorzystuje środek piksela jako swoją lokalizację. Jest to zauważalne przy użyciu przeciążeń opartych na Vector2 w klasie rysowania. Uważam, że odjęcie (.5, .5) powinno rozwiązać twój problem.

Więcej informacji tutaj.

ClassicThunder
źródło
Nie mogę tego technicznie zrobić, ponieważ przekazuję Vector2 int, intjako argumenty. Poza tym wszystko wygląda idealnie, gdy nie mam żadnych obiektów w ruchu.
ashes999
Co rozumiesz przez obiekt w ruchu? XNA nie ma pojęcia o ruchu, rysuje rzeczy tam, gdzie to mówisz. Jest to seria stacjonarnych migawek. Spróbuj także użyć przeciążenia prostokąta, ponieważ i tak zaokrąglasz do prostokąta.
ClassicThunder,
Mam własną implementację prędkości. SpriteComponentma Vector2pozycję, która zwiększa się jako liczba zmienna w zależności od prędkości; kiedy rysuję, tworzę nowe Vector2wersje positionX i Y z liczbami całkowitymi (zaokrąglonymi). Nie o to chodzi, ponieważ obiekty nieruchome bez prędkości wyglądają dobrze.
ashes999
Więc mówiąc, że masz wektor pozycji p i wektor prędkości v, dodaj je, takie jak p + = v, a następnie utwórz kopię p za pomocą zaokrąglonych wartości. I że to daje różne wartości, a więc ma pozycję, która jest taka sama jak poprzednie p + = v, pomimo że wywołania remisu są identyczne? Ponieważ to nie ma sensu.
ClassicThunder,
Tak, to też mówi @Archy. Pozwól mi debugować i sprawdzić ponownie. Dodam p += vpodczas Updatei renderuję za pomocą new Vector2((int)Math.Round(p.X), (int)Math.Round(p.Y))).
ashes999
2

Renderowanie nic nie wie o twojej prędkości, więc ani pozycja, rozmiar, ani nic innego, co przechodzisz do SpriteBatch.Draw jest inny lub błąd występował przed dodaniem prędkości. Jak dokładnie wywołać SpriteBatch.Draw?

Czy debugowałeś to połączenie i sprawdziłeś to X, this.Y i this.origin? Co to jest ustawiony this.origin? Zasadniczo nie ma możliwości, aby wynik renderowania z prędkością był inny, gdy to wywołanie Draw jest takie samo.

Archia
źródło
1

Problem prawdopodobnie polega na włączeniu filtrowania tekstur. Jeśli nie masz pewności, co to oznacza, rozważ sytuację, w której masz obraz o szerokości 2 pikseli: pierwszy piksel jest czarny, drugi piksel jest biały. Jeśli powiększysz tę teksturę, jeśli włączone jest filtrowanie tekstury, zobaczysz, że rozmazuje się i że obszar między pikselem czarnym i białym ma gradientowy szary kolor.

Klasycznym przykładem gier z lub bez filtrowania są Super Mario 64, które miały filtrowanie, podczas gdy Doom nie.

Kiedy nie używasz prędkości, Twój Sprite jest prawdopodobnie ustawiony w taki sposób, że środek tekstury znajduje się w punkcie próbki, w którym XNA (lub jakikolwiek bazowy API jest używany) chwyta kolor. Kiedy poruszasz się z prędkością zmiennoprzecinkową, wówczas zmienia się pozycja twojego duszka, dlatego punkt próbkowania może nie pokrywać się bezpośrednio ze środkiem piksela, więc końcowym wynikiem jest kolor będący średnią najbliższych pikseli używane do renderowania.

Możesz sprawdzić, czy filtrowanie jest włączone, po prostu renderując swojego Duszka naprawdę duży na ekranie. Jeśli jest rozmazany, to jest problem.

Jeśli musisz mieć idealną dokładność renderowania w pikselach, będziesz chciał mieć gdzieś zapisaną wartość zmiennoprzecinkową pozycji, ale użyj wartości całkowitych dla pozycji renderowanego obiektu ... lub oczywiście możesz po prostu wyłączyć filtrowanie jeśli twoja gra tego nie potrzebuje.

Możesz także przeczytać to .

Victor Chelaru
źródło
Jak wspomniałem w moim pytaniu, już używam liczb całkowitych dla mojej lokalizacji i oryginalnego rozmiaru obrazu, kiedy dzwonię Draw. Nie jestem więc pewien, jak to możliwe - spróbuję renderować na dużą skalę i sprawdzać, czy wyłączenie filtrowania tekstur pomoże.
ashes999
1

Spróbuj ustawić SamplerState na SamplerState.PointClamp w wywołaniu SpriteBatch.Begin.

gry rzemieślnicze
źródło