Arkanoid 3D krok po kroku III

02.08.2010 - Olgierd Humeńczuk
TrudnośćTrudność

Aktualizacja pozycji aktorów

Na początek zaimplementujemy poruszanie się rakietki oraz kuli. Ruch rakietki zależeć będzie od tego, jak mocno gracz poruszy myszką. Kula natomiast poruszać się będzie - na razie - z jednostajną prędkością. Przyjrzyjmy się funktorowi, który obsługiwał będzie aktualizację pozycji aktorów.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
 * @struct sceneUpdater
 * @brief Funktor odpowiedzialny za aktualizacje sceny
 */
struct sceneUpdater
{
    /**
     * @brief Przeciazony operator funkcyjny
     * @param object obiekt ktorego pozycja ma zostac zaktualizowana
     */
    void operator()( arkanoidSceneManager::sceneObjectsContainer::value_type& object )
    {
        baseSceneObject& obj = object.second;
        switch( obj.objectType )
        {
            case OBJECT_TYPE_BALL:
                obj.position += obj.dir * obj.velocity;
                break;
            case OBJECT_TYPE_PADDLE:
                obj.position.x = transformPos( static_cast< GLfloat >( mousePosition.x ),
                    static_cast< GLfloat >( 800 )
                  , -1 * ( defObj::planeWidth ) * 0.5f + defObj::paddleWidth * .5f
                  , ( defObj::planeWidth ) * 0.5f - defObj::paddleWidth * .5f );
                break;
        }
    }
};

Jak widać funktor aktualizujący pozycję aktorów zbudowany jest bardzo prosto. Zależnie od typu aktora, modyfikuje jego pozycję. Dla kuli przesuwa aktualną pozycję w kierunku, w którym wskazuje wektor dir o odległość określoną w zmiennej velocity. Do aktualizacji pozycji rakietki użyliśmy kodu ze starej funkcji main.

Niestety taki sposób obliczania aktualnej pozycji nie jest fizycznie poprawny. Nie bierze pod uwagę zmiany czasu. Ulepszaniem tej implementacji zajmiemy się jednak w następnych artykułach, ponieważ został nam do omówienia bardzo ważny dla naszej gry temat wykrywania kolizji.

Wykrywanie kolizji

Podstawowym rodzajem kolizji, jaki będziemy musieli obsługiwać, jest kolizja kuli z różnymi figurami geometrycznymi. Dzięki temu, że nasza kula porusza się w jednej, stałej płaszczyźnie $ XZ $, możemy sprowadzić obliczenia związane z kolizją do tej właśnie płaszczyzny. To oznacza, że będziemy mogli rozpatrywać kolizje w przestrzeni dwuwymiarowej.

Aby stwierdzić, czy nastąpiła kolizja kuli z jakimś obiektem, wystarczy sprawdzić, czy okrąg o promieniu kuli przecina się z którąkolwiek krawędzią testowanego obiektu. Wyprowadzimy zatem wzór, który pozwoli nam wyliczyć, czy takie przecięcie nastąpiło.


Rys. 6. Wykrywanie kolizji okręgu z odcinkiem.

Wzór przedstawiony na Rys. 6 pozwoli nam na obliczenie długości odcinka pomiędzy punktami $ v_d $ i $ P $, gdzie $ v_d $ jest punktem położonym najbliżej środka okręgu, leżącym na odcinku z którym sprawdzamy kolizję.

Wzór ten rzutuje wektor $ \vec{o} $ na wektor $ \vec{v} $ dzięki czemu możemy obliczyć pozycję punktu $ v_d $ i tym samym szukaną długość odcinka łączącego punkty $ v_d $ i $ P $.

Musimy jednak pamiętać, że nie zawsze punkt $ v_d $ znajdzie się pomiędzy krańcami odcinka $ \vec{v} $. Kiedy okrąg zbliżać się będzie do odcinka pod ostrym kątem, punkt $ v_d $ będzie leżał na prostej przechodzącej przez punkty $ v_p $ oraz $ v_k $, jednak znajdował się będzie poza odcinkiem łączącym te punkty. W takim wypadku będziemy musieli użyć odpowiednio: początku odcinka, czyli punktu $ v_p $ jeżeli punkt $ v_d $ leży przed $ v_p $ oraz analogicznie punktu $ v_k $ jeżeli punkt $ v_d $ leży za $ v_k $.


Zaimplementujmy zatem funkcję realizującą te obliczenia.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
bool testSphereSegInter(
  const vector3f& sphereCenter, const float sphereRadius
, const vector3f& segBeg, const vector3f& segEnd
, vector3f& closest, vector3f& offset )
{
    vector3f seg = segEnd - segBeg;
    vector3f pt = sphereCenter - segBeg;
 
    float projLen = pt.dot( seg.normalize() );
 
    vector3f proj = seg.normalize() * projLen;
    closest = segBeg + proj;
 
    if( projLen <= 0 ) closest = segBeg;
    if( projLen >= seg.length() ) closest = segEnd;
 
    vector3f dist = ( sphereCenter - closest );
 
    float distLen = dist.length();
    offset = dist.normalize() * ( sphereRadius - distLen );
 
    return distLen <= sphereRadius;
}

Aby łatwiej zrozumieć działania wykonane w tej funkcji, przypiszmy jej argumentom oznaczenia wprowadzone na Rys. 6.

  • sphereCenter - odpowiada punktowi $ P $
  • segBeg - odpowiada punktowi $ v_p $
  • segEnd - odpowiada punktowi $ v_k $
  • closest - to punkt oznaczony $ v_d $
Funkcja ta, oprócz tego że realizuje omówione przez nas wcześniej obliczenia, zwraca wartość true jeżeli nastąpiła kolizja lub wartość false jeżeli jej nie było. Dodatkowo, poprzez referencję do zmiennej closest, zwraca współrzędne punktu $ v_d $, a w zmiennej offset zwraca przesunięcie środka okręgu, po zastosowaniu którego okrąg będzie stykał się odcinkiem w prawdziwym punkcie kolizji.

Korzystając z tej funkcji, łatwo zaimplementujemy funktor testujący kolizje kuli z poszczególnymi elementami sceny. Ponieważ jest to dobre ćwiczenie, postarajmy się to zrobić samodzielnie. W odnośniku na końcu artykułu znajduje się zwyczajowo archiwum z kompletną implementacją materiału przedstawionego w tym artykule, którą można się posiłkować w razie problemów. Powodzenia !

Zachęcam do przedstawienia swoich rozwiązań na forum, oraz do zadawania pytań dotyczących treści artykułów o tworzeniu naszej gry.

5
Twoja ocena: Brak Ocena: 5 (2 ocen)

Copyright © 2008-2010 Wrocławski Portal Informatyczny

design: rafalpolito.com