Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

dynamika i kolizja w grach 2D

Ostatnio zmodyfikowano 2018-03-19 19:33
Autor Wiadomość
latajacaryba
Temat założony przez niniejszego użytkownika
dynamika i kolizja w grach 2D
» 2018-02-08 17:33:27
Witam. Mam klasę bullet, po której dziedziczą Rocket, StandardBullet oraz Laser. Są to - jak nazwa wskazuje - różne typy pocisków. Mają swoje obrażenie, teksture lotu oraz prędkość. Mam jednak problem:

Potrzebna mi możliwość pobierania obrażeń i tu zaczynają się schody. Standard bullet zwraca po prostu obrażenie, bo to pocisk jak każdy, uderzy to uderzy, więc getDamage() jest bez argumentów.
Ale:
Rocket wybucha i obrażenie zależą od pozycji przeciwnika - im bliżej wybuchu tym mocniejsze, więc to powinno być getDamage(const vector & Position)
Ale to jeszcze nie wszystko :D

Laser, jak to laser, bije cały czas, a jego obrażenie to damage per second, więc: getDamage(const Time & deltaTime)

No i klops. Bo jeśli chce wszystkie te pociski przechowywać w wektorze: std::vector<Bullet*> Bullets to niestety, nie mam jak wywoływać metody getDamage.

Ktoś mógłby powiedzieć
To niech każdy pocisk, niezależnie od rodzaju zwraca damage, a klasa np. Engine czy Game sobie ogarnie już zarządzanie (pomnoży przez deltaTime itd.)
No nie, bo przecież klasa nie wie, co kryje się pod Bullet*. To może być laser ale i rocket.

To zwracaj strukturę, która zawiera zmienną damage oraz enuma, np. eLaserDamage, eRocketDamage i w zależności od tego niech klasa Engine sie z tym bawi

No nie po raz drugi. To pogwałcenie zasady(nie pamiętam jak sie nazywała) która mówi, że nie powinna być wymagana znajomość typu obiektu klasy dziedziczącej (na tym polega w końcu dziedziczenie, żeby wywoływać tą samą metodę dla różnych obiektów klasy podstawowej). Jeśli dodam kiedyś klasę dziedziczącą po pocisk będę musiał tworzyć kolejnego enuma i jego obsługę.

Także proszę o jakiś pomysł.

edit:
@Saran (na dole :D)
Zapomniałem, że taki dział w ogóle istnieje.
P-169317
Saran
» 2018-02-08 18:07:47
O właśnie takie tematy jak te. One powinny być w dziale Tworzenia Gier, a nie jakieś pytania dot. instalacji bibliotek jakie tam są teraz. Według mnie przynajmniej.
P-169318
jankowalski25
» 2018-02-08 18:48:43
Niech każdy pocisk sam oblicza zadane obrażenia.
  • StandardBullet po prostu zwróci obrażenia.
  • Rocket niech w momencie wybuchu pobiera pozycję przeciwnika i oblicza wynik.
  • Laser niech podczas rażenia zapamiętuje, ile czasu to trwa i oblicza wynik.

Dopisano:
Potrzebna mi możliwość pobierania obrażeń
Jak to obsługujesz? Co klatkę pytasz każdy pocisk, czy i jakie zadał obrażenia? Skrajny przypadek: jeśli 1000 pocisków samonaprowadzających będzie sobie fruwało przez minutę za jakimś wrogiem, to w każdej klatce będziesz pytał każdy z nich o zadane obrażenia? Może lepiej niech pocisk sam o tym powiadamia. Wtedy masz kontener z "ciekawymi" zdarzeniami, które po kolei obsługujesz.
P-169319
michal11
» 2018-02-08 21:15:09
Odwróć logikę, niech to każdy "Bullet" odpowiednio implementuje zadawanie obrażeń a nie cel sobie pobiera damage z tego co go trafiło.

StandardBullet niech na zwykłym callbacku kolizyjnym zadaje te swoje obrażenia.
Laser niech robi to na swoim ticku (czy tam funkcji update), po trafieniu.
Rocket - tu masz dwa podejścia, albo bardziej rzeczywiste czyli rozszerzanie się wybuchu w czasie albo w jednej klatce zbierasz okolicznych przeciwników i zadajesz im damage.
W pierwszym przypadku robisz to na ticku rocketa (ew. na callbacku kolizji) i po prostu sprawdzasz odległość od centrum trafienia.
W drugim przypadku po prostu na trafieniu zbierasz okolicznych aktorów z systemu kolizji i wyliczasz odległość.
P-169329
latajacaryba
Temat założony przez niniejszego użytkownika
» 2018-02-08 22:24:00
Jak to obsługujesz?
Tu właśnie jeszcze nie mam pomysłu, jak sam zauważyłeś, niezbyt wydajnie jest sprawdzać każdy pocisk, czy koliduje z każdym przeciwnikiem. Miałem pomysł na coś w tym stylu:

C/C++
for( auto & i: wektorPrzeciwnikow )
{
    if( koliduje( pocisk, i ) )
         i.zadajObrazenia( pocisk.obrazenia() );
   
}

@michal11

Czyli coś w ten deseń:

C/C++
if( kolizja( pocisk, przeciwnik[ j ] ) )
{
    pocisk.zadajObrazenia( przeciwnik[ j ] );
}

?
Wydaje mi sie, że dobrym rozwiązaniem będzie połączenie Waszych metod - przy Update laser zapamiętuje deltaTime i wykorzystuje go przy ewentualnym zadawaniu obrażeń :))

PS. jankowalski myślałem, o podziale ekranu na części, np 20 i co update ruchu każdej postaci sprawdzałbym, w którym polu sie znajduje, tylko jest jeden problem: Dajmy na to pocisk znajduje sie
w polu o numerze 1 (rysunek poglądowy proszę!), więc sprawdza kolizje tylko dla postaci znajdujących się w polu 1, natomiast - jak wynika z załączonego obrazka - formalnie postać (jej środek) znajduje sie na polu nr. 2. Nie wiem, czy będzie to miało ogromny wpływ, ale proszę spojrzeć, jeszcze gorzej! Drugi przeciwnik jest na krzyżowaniu 4 pól, choć formalnie jego miejsce to pole 14. To oznacza, że jego obszar kolizji zmniejsza sie do zamalowanego na zielono fragmentu.
P-169332
Saran
» 2018-02-08 23:00:20
Może quadtree
P-169334
latajacaryba
Temat założony przez niniejszego użytkownika
» 2018-02-08 23:32:47
Zerknąłem na quadtree (ten film), tylko nie bardzo wiem, jak to ma wydajnie działać. W końcu jeśli podzielimy sobie okno na 4 części, to nadal musimy wiedzieć, ile obiektów znajduje sie w poszczególnych częściach (gałęziach) okna, po to by wiedzieć czy nadal mamy je dzielić, czy sprawdzać kolizje. A nie da sie tego policzyć inaczej, niż sprawdzić, czy obiekty kolidują z wydzielonym obszarem. Czy może gdzieś się mylę?
P-169336
michal11
» 2018-02-08 23:46:34
Jeżeli to jest projekt prywatny w którym chcesz się jak najwięcej nauczyć to proponuję żebyś zrobił cos bardziej systemowego a nie "skrojonego na miarę". Jeżeli ja bym pisał taki system pocisków to chciałbym móc zrobić tak
C/C++
void SimpleBullet::Init()
{
    CollisionObject.RegisterForCollision( this, & SimpleBullet::OnCollision );
}

void SimpleBullet::OnCollision( GameObject * HitObject, const Vector & HitLocation )
{
    if( HitObject )
    {
        HitObject->ApplyDamage( getBulletDamage(), GetBulletDamageType() );
    }
}

//....

void Laser::Tick( float DeltaTime )
{
    if( LastHitObjectIsValid() )
    {
        LastHitObject->ApplyDamage( getBulletDamage() * DeltaTime, GetBulletDamageType() );
    }
}

void Laser::OnCollision( GameObject * HitObject, const Vector & HitLocation )
{
    if( HitObject )
    {
        LastHitObject = HitObject;
    }
}

i wtedy musisz oczywiście odpowiednio zadbać o to aby CollisionObject rejestrował się do systemu kolizji, dla uproszczenia możesz założyć tylko kolizje BoxToBox. No i najlepiej zrobić też coś na zasadzie CollisionStart i CollisionEnd ale to już powinno być stosunkowo łatwe.

To o czym piszesz w PS nazywa się quadtree, możesz poczytać o tym, powinno pomóc w optymalizacji, aczkolwiek nawet jeżeli będziesz miała setki obiektów które mogą ze sobą kolidować i w każdym ticku będziesz sprawdzał kolizję każdy z każdym to nie powinien to być problem, pewnie nawet tysiące obiektów na raz nie będą dużym problemem,
P-169337
« 1 » 2 3 4
  Strona 1 z 4 Następna strona