Gra 2D, część 9: Efektowne przejścia między stanami

25.08.2010 - Marcin Milewski
TrudnośćTrudność

Implementacja

Omówiliśmy przed chwilą kilka kwestii związanych z implementacją rozwiązania. Ze względu na fakt, że w przyszłości planujemy zintegrować naszą aplikacją z jakimś językiem skryptowym (najprawdopodobniej będzie to Lua), to nasza implementacja będzie dosyć prosta.

Zacznijmy od klasy reprezentującej efekt przejścia. Jej zachowanie do przesady przypomina dowolny ze stanów aplikacji - inicjalizacja, po czym wykonywanie cyklu obsłuż zdarzenia-uaktualnij-narysuj. Cechą charakterystyczną klasy efektu będzie to, że każda jej instancja będzie przechowywała poprzedni oraz następny stan aplikacji. W momencie zakończenia wykonywania kodu efektu sterowanie zostanie przekazane do tego drugiego, a aplikacja będzie działała tak, jakby tego efektu nigdy tam nie było.

Opakowywanie stanów w klasie efektu.

Tak więc klasa TransitionEffect dziedziczy po AppState, oto jej ogólny schemat:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// plik TransitionEffect.h
class TransitionEffect : public AppState {
    TransitionEffectType::Type m_effect_type;     // typ efektu
public:
    TransitionEffect(TransitionEffectType::Type effect_type);
    ~TransitionEffect();
    void Start();
    void Init();
    void Draw();
    bool Update(double dt);
    void ProcessEvents(const SDL_Event& event);
    AppStatePtr NextAppState() const;
};
  

Typ efektu określa, który efekt będzie reprezentowała konkretna instancja tej klasy. Możliwa jest aktualnie jedna z trzech wartości - FadeIn, FadeOut oraz PinWheelOut. Wyliczenie TransitionEffectType definiujemy w pliku Types.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
// plik Types.h
namespace TransitionEffectType {
    enum Type {
        UNKNOWN,
 
        FadeIn,
        FadeOut,
        PinWheelOut,
 
        COUNT
    };
}
  

Efekty przejścia będą najczęściej tworzone przez stany gry takie jak menu główne, hall of fame czy ekran wyboru poziomu. Dlatego do deklaracji każdej z klas potomnych dołączamy kod pozwalający pobrać inteligentny wskaźnik na this, co oznacza zmianę klas dziedziczących po AppState według schematu:

1
2
3
4
5
6
// deklarację klasy w odpowiednim pliku nagłówkowym (tu: MainMenu.h) zamieniamy z 
class MainMenu : public AppState {
 
// na
class MainMenu : public AppState, public boost::enable_shared_from_this<MainMenu> {
  

Jak działa efekt fade-out?

Zanim przejdziemy do szczegółów konstrukcji klasy efektów przejść, rzućmy okiem na sposób, w jaki możemy zaimplementować efekt fade-out (znany też jako fade to black lub bardziej ogólnie: fade to color)

Przede wszystkim potrzebujemy przechowywać dwa stany: pierwszy, który będzie znikał oraz drugi, który zostanie uruchomiony po zakończeniu się efektu. Na potrzeby tego przykładu załóżmy, że efekt zawsze trwa 2 sekundy. Schemat działania wyglądałby mniej więcej tak (pseudokod):

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
// pseudokod implementacji efektu fade-out
class Effect {
    State from_state;           // znikający stan (np. hall of fame)
    State to_state;             // stan po zakończeniu efektu (np. menu główne)
    double current_alpha;       // aktualna wartość przezroczystości
    double timer;               // czas, który upłynął od początku wyświetlania efektu
 
public:
    Effect(State from_state, State to_state)
        : from_state(from_state), to_state(to_state),
          current_alpha(0), timer(0) {
    }
 
    Update(double dt) {
        current_alpha += dt/2  // 2 to liczba sekund przez które będzie wyświetlany efekt
        timer += dt
        if timer > 2 {
            return to_state
        }
    }
 
    Draw() {
        ClearScreen()
        from_state->Draw()
        DrawBlackQuadWithAlpha(current_alpha)
        SwapBuffers()
    }
};
  

Jak widać, implementacja jest równie prosta co opisywany efekt. Dlatego zanim przejdziemy dalej, polecamy każdemu spróbować go zaimplementować.

Ilustracja przepływu sterowania, gdy używamy efektu przejścia.

Zauważmy, że napisaliśmy tutaj dokładnie jeden efekt, którego w żaden sposób nie da się modyfikować. A gdyby tak dodać możliwość definiowania czasu trwania efektu, a następnie dodać możliwość ustawienia początkowej i końcowej wartości przezroczystości? Wtedy automatycznie otrzymalibyśmy efekt fade-in. Te dwa efekty różnią się głównie tym, czy zwiększamy aktualną wartość przezroczystości czy ją zmniejszamy. Poza tym w fade-out chcemy rysować from_state, a w fade-in - to_state, ale o takie szczegóły umiemy szybko zadbać. Ponadto, dzięki możliwości manipulowania początkową oraz końcową przezroczystością, możemy tworzyć efekty częściowego zaciemnienia a następnie rozjaśnienia obrazu. Przykładową implementację zobaczymy niebawem.

0
Twoja ocena: Brak

Copyright © 2008-2010 Wrocławski Portal Informatyczny

design: rafalpolito.com