Rozwijam grę z terenem podobnym do Minecrafta zbudowanym z bloków. Ponieważ podstawowe renderowanie i ładowanie porcji jest już zakończone, chcę zaimplementować wybieranie bloków.
Dlatego muszę dowiedzieć się, jaki blok stoi aparat pierwszej osoby. Słyszałem już o nieprojektowaniu całej sceny, ale zdecydowałem się tego nie robić, ponieważ brzmi to hack i nie jest dokładne. Może mógłbym jakoś rzucić promień w kierunku widoku, ale nie wiem, jak sprawdzić kolizję z blokiem w moich danych wokselowych. Oczywiście obliczeń należy dokonać na CPU, ponieważ potrzebuję wyników do wykonania operacji logiki gry.
Jak więc dowiedzieć się, który blok znajduje się przed kamerą? Jeśli jest to preferowane, jak mogę rzucić promień i sprawdzić kolizje?
camera
voxels
raycasting
selection
danijar
źródło
źródło
Odpowiedzi:
Kiedy miałem ten problem podczas pracy nad kostkami , znalazłem artykuł „A Fast Voxel Traversal Al Algorytm for Ray Tracing” autorstwa John Amanatides i Andrew Woo, 1987, który opisuje algorytm, który można zastosować do tego zadania; jest dokładny i wymaga tylko jednej iteracji pętli na przecięty woksel.
Napisałem implementację odpowiednich części algorytmu artykułu w JavaScript. Moja implementacja dodaje dwie funkcje: pozwala określić limit odległości raycasta (przydatne w celu uniknięcia problemów z wydajnością, a także zdefiniować ograniczony „zasięg”), a także oblicza, która twarz każdego woksela zawiera promień.
origin
Wektor wejściowy musi być skalowany w taki sposób, aby długość boku woksela wynosiła 1. Długośćdirection
wektora nie jest znacząca, ale może wpływać na dokładność numeryczną algorytmu.Algorytm ten działa za pomocą sparametryzowanego reprezentację promieniem
origin + t * direction
. Dla każdej osi współrzędnych, możemy śledzićt
wartości, które mamy, jeśli zrobiliśmy krok wystarczający do przekroczenia granicy woksela wzdłuż tej osi (czyli zmienić część całkowita współrzędnych) w zmiennychtMaxX
,tMaxY
oraztMaxZ
. Następnie wykonujemy krok (używając zmiennychstep
itDelta
) wzdłuż dowolnej osi, która ma najmniejszątMax
- tj. Która granica wokseli jest najbliższa.Stały link do tej wersji źródła na GitHub .
źródło
function intbounds(s,ds) { return (ds > 0? Math.ceil(s)-s: s-Math.floor(s)) / Math.abs(ds); }
. PonieważInfinity
jest większa niż wszystkie liczby, nie sądzę, żebyś musiał się wystrzegać, czy ds będzie tam 0.1/ds
powoduje, że jedna z pozostałych osi jest zamiast tego zwiększana. Poprawka polega na napisaniu,intfloor
aby sprawdzić, czy obads
są ujemne i czys
są liczbą całkowitą (mod zwraca 0), i zwracają w tym przypadku 0,0.Być może zajrzyj do algorytmu linii Bresenhama , szczególnie jeśli pracujesz z blokami jednostek (jak ma to miejsce w większości gier Minecraft).
Zasadniczo zajmuje to dwa dowolne punkty i kreśli nieprzerwaną linię między nimi. Jeśli rzucisz wektor na gracza na maksymalną odległość, możesz go użyć, a gracze ustawią się jako punkty.
Mam tutaj implementację 3D w python: bresenham3d.py .
źródło
Aby znaleźć pierwszy blok przed kamerą, utwórz pętlę for, która zapętla od 0 do pewnej maksymalnej odległości. Następnie pomnóż wektor do przodu kamery przez licznik i sprawdź, czy blok w tej pozycji jest pełny. Jeśli tak, zapisz pozycję bloku do późniejszego użycia i przestań zapętlać.
Jeśli chcesz także umieścić klocki, wybieranie twarzy nie jest trudniejsze. Po prostu zapętl się z powrotem od bloku i znajdź pierwszy pusty blok.
źródło
Napisałem post na Reddicie z moją implementacją , która korzysta z algorytmu liniowego Bresenhama. Oto przykład, w jaki sposób chcesz go użyć:
Oto sama implementacja:
źródło