Gra 2D, część 14: Lepszy respawn i kilka szczegółów

01.10.2012 - Marcin Milewski
TrudnośćTrudność

Punkty za czarodziejskie kule

W większości, o ile nie w każdej grze platformowej na planszy znajduje się element, za którego zebranie gracz otrzymuje punkty. Mogą to być monety, banknoty, gwiazdki, owoce, itp. W naszej grze będą to kule.

Zaczynamy od utworzenia klasy, która będzie reprezentować kulę. Ponieważ będzie ona bardzo mała (konstruktor + metoda), to aby uniknąć nadmiaru plików nagłówkowych, zdefiniujemy ją w pliku Misc.h. W tym miejscu będziemy umieszczać klasy, które są na tyle krótkie, że nie warto tworzyć dla nich osobnych plików. W tym kryterium mieszczą się m.in. bonusy. Oto zawartość pliku Misc.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#ifndef __MISC_H_INCLUDED__
#define __MISC_H_INCLUDED__
#include "StdAfx.h"
#include "Entity.h"
 
 
class Orb : public Entity {
public:
    Orb(double x, double y)
        : Entity(x,y, 0, 0) {
    }
 
    ET::EntityType GetType() const { return ET::Orb; }
};
 
 
#endif  

Następnie dodajemy typ Orb do wyliczenia ET::EntityType w pliku Types.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
// Plik: Types.h
    enum EntityType {
        UNKNOWN,
 
        Mush,
        PlayerBullet,
        SingleShot,
        TwinShot,
        Orb,            // NOWE
 
        COUNT
    };
}  

W pliku Types.cpp podajemy ciąg znaków, na który zostanie przetłumaczony typ ET::Orb podczas zapisu poziomu na dysk.

1
2
3
4
5
6
7
8
9
10
11
12
// Plik: Types.cpp
std::string EntityTypeAsString(ET::EntityType et) {
    switch (et) {
 
    // bonusy
    case ET::SingleShot   : return "singleshot_upgrade";
    case ET::TwinShot     : return "twinshot_upgrade";
    case ET::HigherJump   : return "higherjump_upgrade";
    case ET::Orb          : return "orb";                                // NOWE
 
    // (...)
}  

Odwrotną do powyższej operację należy przeprowadzić w fabryce encji. Jest tam tworzona jednostka na podstawie opisu wczytanego z pliku lub typu przekazanego jako argument funkcji. Zatem trzeba uaktualnić dwa miejsca:

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
28
29
30
31
32
// Plik: EntityFactory.cpp
#include "Misc.h"                // Orb
 
// (...)
 
EntityPtr
EntityFactory::CreateEntity(const std::string& name, double x, double y) {
    // (...)
    } else if (name=="twinshot_upgrade") {
        return CreateEntity(ET::TwinShot, x, y);
    } else if (name=="orb") {                                            // NOWE
        return CreateEntity(ET::Orb, x, y);                              // NOWE
    }
    // (...)
}
 
// (...)
 
EntityPtr
EntityFactory::CreateEntity(ET::EntityType type, double x, double y) {
    // (...)
    } else if (type == ET::TwinShot) {
        ptr.reset(new TwinShotUpgrade(x, y));
        SpritePtr sprite = GetSpriteByName("twinshot_upgrade");
        ptr->SetSprites(sprite, sprite, sprite);
    } else if (type == ET::Orb) {                                        // NOWE
        ptr.reset(new Orb(x, y));                                        // NOWE
        SpritePtr sprite = GetSpriteByName("orb");                       // NOWE
        ptr->SetSprites(sprite, sprite, sprite);                         // NOWE
    }
    // (...)
}  

Mamy już zdefiniowany nowy typ encji. Podaliśmy także odwzorowanie ciąg znaków <-> nazwa typu. Zostało nam jeszcze:

  • Sprawdzanie kolizji z bohaterem
  • Grafika i oznaczenie na atlasie

Pierwszy punkt poruszaliśmy w poprzednim rozdziale. Kod odpowiadający za sprawdzanie kolizji i reakcję znajduje się w pliku Game.cpp. Odpowiedź na zderzenie z encją typu ET::Orb jest bardzo prosta: zwiększamy bohaterowi liczbę punktów. Odbywa się to przez wywołanie metody AddScores z odpowiednią wartością.

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
28
29
30
31
// Plik: Game.cpp, metoda CheckPlayerEntitiesCollisions
    // (...)
    std::vector<EntityPtr>::iterator it;
    for ( it=m_entities.begin(); it != m_entities.end(); ++it) {
        EntityPtr entity = *it;
        const ET::EntityType entity_type = entity->GetType();
 
        const int score_for_bonus = 40;   // punkty za wzięcie bonusu
        const int score_for_orb = 130;    // punkty za wzięcie magicznej kuli
 
        if (entity_type == ET::PlayerBullet) {
            // postać nie koliduje ze swoimi pociskami
            continue;
        } else if (entity_type == ET::TwinShot) {
            // gracz wziął bonus "podwójne strzelanie"
            if (m_player->GetAabb().Collides(entity->GetAabb())) {
                m_player->AddScores(score_for_bonus);
                m_player->EnableTwinShot();
                entity->SetIsDead(true);
            }
            continue;
        } else if (entity_type == ET::Orb) {                             // NOWE
            // gracz wziął kulę (oznaczającą dodatkowe punkty)           // NOWE
            if (m_player->GetAabb().Collides(entity->GetAabb())) {       // NOWE
                m_player->AddScores(score_for_orb);                      // NOWE
                entity->SetIsDead(true);                                 // NOWE
            }                                                            // NOWE
            continue;                                                    // NOWE
        }
        // (...)
}  

Drugi z wymienionych punktów to grafika. Kulę można znaleźć w sieci lub narysować samemu, np. w programie Inkscape czy GIMP. W roboczej wersji można umieścić także kwadrat bądź trójkąt. Najważniejsze, żeby element dobrze wypełniał prostokąt, gdyż to właśnie na nich oparty jest nasz algorytm wykrywający kolizje.

Kiedy grafika, choćby robocza, jest gotowa, należy poinformować grę, w którym miesjcu atlasu (plik data/tex.png) ją umieściliśmy. Inaczej mówiąc, należy dodać dodać do konstruktora klasy SpriteConfig wpis podobny do poniższego:

1
2
3
4
5
6
7
8
  Insert("orb",
         SpriteConfigData(DL::Entity,       // warstwa wyświetlania
                          1,                // liczba klatek w animacji
                          1,                // czas wyświetlania klatki
                          4 * 32, 19 * 32,  // położenie w atlasie (w px)
                          32, 32,           // wymiary prostokąta
                          true,             // czy animacja się zapętla
                          false));          // czy wyświetlać przyciemnione

Teraz można już przetestować jak sprawdzają się magiczne kule z punktami w praktyce. Wystarczy umieścić w pliku data/1.ents na przykład taką zawartość:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Plik: data/1.ents
player 1 2
 
# kasa, kasa, kasa!
orb 2 8
orb 3 7
orb 4 7
 
# pozostałe obiekty
mush 2 1
mush 4 1
mush 16 4
mush 23 7
 
twinshot_upgrade 4 3  
0
Twoja ocena: Brak

Copyright © 2008-2010 Wrocławski Portal Informatyczny

design: rafalpolito.com