Programuję prostą grę asteroid w Javie. I znajdź ten problem (nie jest to problem, moje rozwiązanie działa idealnie, ale wygląda brzydko). Chcę tylko wiedzieć, czy istnieje sposób na uproszczenie mojego kodu.
W mojej grze jest kilka obiektów. Statek, Meteor, Pocisk i Moneta. Wszystkie są podklasą GameObject . Aby radzić sobie z kolizjami, w klasie MyGame mam te funkcje.
private void ship_hit_meteor(Ship ship, Meteor meteor){}
private void bullet_hit_meteor(Bullet bullet, Meteor meteor){}
private void ship_hit_coin(Ship ship, Coin coin){}
//...and so on
Aby sprawdzić kolizję, umieściłem wszystkie obiekty w jednym dużym objArray . Następnie iteruję tablicę, aby sprawdzić kolizje. Oto jak to robię: ale wyobraź sobie, że mam 100 rodzajów obiektów.
//inside the collision loop
GameObject a = objArray[i];
GameObject b = objArray[j];
if(a instanceof Ship && b instanceof Meteor ){
ship_hit_meteor((Ship)a, (Meteor)b);
}
else if(a instanceof Meteor && b instanceof Ship){
ship_hit_meteor((Ship)b, (Meteor)a);
}
else if(a instanceof Bullet && b instanceof Meteor){
bullet_hit_meteor((Bullet)a, (Meteor)b);
}
else if(a instanceof Meteor && b instanceof Bullet){
bullet_hit_meteor((Bullet)b, (Meteor)a);
}
//... end of the loop
Czy jest jakiś sposób, aby to uprościć?
Dzięki.
4 odpowiedzi
Może uda ci się stworzyć interfejs, powiedzmy „SpaceObject”, a wszystkie klasy oddziałujące na nią to implementują. Interfejs ma metodę handleCollision () i klasy powinny ją przesłonić. Tam umieszczasz logikę kolizji, uszkodzenia itp. Do HandlerService (miejsca, w którym żyje twój fragment) po prostu otrzymujesz listę SpaceObjects i wywołujesz ich metodę handleCollision.
Chociaż nie mam pojęcia o Javie, ale myślę, że twój kod wymaga zastanowienia się nad organizacją podprogramów. Wolałbym raczej zdefiniować dla każdego obiektu 1 własną procedurę „uderzenia”. W głównym przepływie sprawdź pierwszy obiekt z poprawną listą nazw obiektów, po czym wywołaj odpowiednie "object.hitting" z parametrem 2. Sprawdzanie poprawności parametru wypisuje pojedynczą liczbę lub 0, a możesz tutaj " przełączanie ”ponownie.
Jeśli masz 100 typów obiektów, napiszesz 5050 metod, jeśli napiszesz oddzielną metodę dla każdej kombinacji typów obiektów. (formuła jest metodami n(n-1)+n
, jeśli istnieje n typów). I to nawet nie bierze pod uwagę, jak wysłać do właściwego, z czym tutaj się zmagasz.
Lepszym podejściem byłoby posiadanie ogólnej metody obsługi kolizji, która wie, jak radzić sobie ze wszystkimi kolizjami w oparciu o właściwości kolidujących obiektów. Takie właściwości można następnie wyrazić jako metody, razem z metodami dodatkowymi, które pozwalają logice kolizji manipulować zderzającymi się obiektami. na przykład
public interface Collides {
// properties
int damageDealtOnImpact();
boolean isReward();
boolean isIndestructible();
boolean isDestroyedOnImpact();
// impact methods
void damage(int damage);
void destroy();
void split();
void changeCourse(Vector impactVector);
// ... and so on, whatever you need
}
W ten sposób dodawanie typów obiektów powinno być stosunkowo bezbolesne. Chociaż czasami mogą być potrzebne nowe właściwości dla nowych typów obiektów. W takim przypadku wpłynie to na ogólną metodę obsługi kolizji i może być konieczne zaimplementowanie dodatkowych właściwości w istniejących obiektach. Ale to przebija dodawanie 101 nowych metod i wysyłanie do nich.
Najpierw sprawdzasz kolizje za pomocą jakiejś biblioteki z czterema drzewami bez odniesienia do typu obiektu.
Zobacz tę odpowiedź Quadtree do wykrywania kolizji 2D oraz Sprawna (i dobrze wyjaśniona) implementacja Quadtree do wykrywania kolizji 2D
Wtedy… możesz samodzielnie filtrować kolizje.
https://www.iforce2d.net/b2dtut/collision-filtering
Więc skonfiguruj identyfikatory obiektów
public static final short xUSER_SHIP = 0x0001;
public static final short xUSER_BULLET = 0x0002;
I maski kategorii kolizji tworzone przez logiczne LUB wszystkiego, co koliduje
public static final short USER_SHIP_MASK = xPICKUP | xWALL | xENEMY_SHIP;
(pamiętając, że musisz stwierdzić, że A uderza B ORAZ, że B uderza A.
A następnie wdrażasz, przechodząc przez listę kolizji
bool collide =
(filterA.maskBits & filterB.categoryBits) != 0 &&
(filterA.categoryBits & filterB.maskBits) != 0;
Jednak zrobienie tego wszystkiego wymaga dużo pracy. Możesz preferować ziarnistą siatkę kursu dla każdego kwadratu, która jest większa niż jakikolwiek pojedynczy obiekt (moneta, asteroida itp.). Każdy obiekt aktualizuje swoją pozycję x,y na tej głównej siatce. Dla każdego obiektu, sprawdź otaczające go kwadraty na siatce kursu (może zachodzić na swojego bezpośredniego właściciela, ale nie może ominąć otaczające komórki) w celu sprawdzenia kolizji „wykrywanie kolizji w oparciu o siatkę” lub „wykrywanie kolizji w oparciu o kafelki”. To będzie najłatwiejsze i możesz wdrożyć samodzielnie, jak chcesz. jeśli traktujesz wszystko jako kolidujące kręgi, jest jeszcze łatwiej.
Podobne pytania
Powiązane pytania
Nowe pytania
java
Java to język programowania wysokiego poziomu. Użyj tego tagu, jeśli masz problemy z używaniem lub zrozumieniem samego języka. Ten tag jest rzadko używany samodzielnie i jest najczęściej używany w połączeniu z [spring], [spring-boot], [jakarta-ee], [android], [javafx], [hadoop], [gradle] i [maven].
Bullet
trafieniemMeteor
aMeteor
trafieniemBullet
?Ship.collision(GameObject a)
możesz mieć instrukcję switch dlaGameObject
, ale zaimplementował kilka prostych metodhit(GameObject object)
w każdej z twoich pojedynczych klas. Wtedy możesz je przeciążyć. Załóżmy, że w swojej klasieShip
możesz zaimplementowaćhit(Meteor meteor){...}
, a możehit(Bullet bullet)
. Potem decydujesz, czy zniszczysz statek, czy zredukujesz jego punkty życia, czy cokolwiek. Możesz go po prostu wyzwolić, wywołująca.hit(b)
bez faktycznego "wiedzenia" w czasie wykonywania, jaka jest klasaa
lubb
.