Gra 2D, część 13: Edytor poziomów cz. 3

02.06.2011 - Marcin Milewski
TrudnośćTrudność

Wywoływanie metod multipędzla w edytorze

Definiując podklasę MultiBrush dopisaliśmy metody StartAt, MoveTo, FinishAt. Do tej pory nigdzie ich nie wywoływaliśmy. Łatwo się domyślić, że właściwym ku temu miejscem jest obsługa zdarzeń przychodzących do klasy Editor. Dodamy zatem bliźniacze dla ActionAtCoords, prywatne metody MoveToCoords oraz ReleaseAtCoords, odpowiadające za reakcję na przesuniecie kursora oraz zwolnienia klawisza myszy. Odpowiedni kod w pliku Editor.h wygląda następująco:

1
2
3
4
5
6
7
// Plik: editor/Editor.h
    Editor* SetBrush(BrushPtr brush) { m_brush = brush; return this; }
 
    void ReleaseAtCoords(double x, double y);  // Wsp.świata
    void MoveToCoords(double x, double y);     // Wsp.świata
    void ActionAtCoords(double x, double y);   // Wsp.świata
  

Definicja obu nowych metod jest bardzo podobna. Polega na upewnieniu się czy używanym pędzlem jest teraz multipędzel, a następnie wywołaniu odpowiedniej jego metody. Zanim jednak odpowiednia funkcja zostanie wywołana, musimy rzutować pędzel na jego podklasę, czyli MultiBrush. Ponieważ korzystamy z inteligentnych wskaźników z biblioteki boost, nie możemy użyć zwykłego dynamic_cast z biblioteki standardowej. Na szczęście razem z klasą boost::shared_ptr otrzymujemy funkcję do rzutowania wskaźników -- boost::dynamic_pointer_cast.

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
// Plik editor/Editor.cpp
#include <boost/pointer_cast.hpp>
 
// (...)
 
void Editor::ReleaseAtCoords(double x, double y) {
    BrushPtr brush = m_gui->GetActiveBrush();
    if (! brush) {
        return;
    }
    if (brush->GetSpecialType() == Brush::ST::Multi) {
        MultiBrushPtr multibrush = boost::dynamic_pointer_cast<MultiBrush>(brush);
        multibrush->FinishAt(x, y);
    }
}
 
void Editor::MoveToCoords(double x, double y) {
    BrushPtr brush = m_gui->GetActiveBrush();
    if (! brush) {
        return;
    }
    if (brush->GetSpecialType() == Brush::ST::Multi) {
        MultiBrushPtr multibrush = boost::dynamic_pointer_cast<MultiBrush>(brush);
        multibrush->MoveTo(x, y);
    }
}
  

Wywołania powyższych funkcji umieszczamy w metodzie Editor::ProcessEvents w obsłudze odpowiednich zdarzeń (ruch myszą, zwolnienie klawisza myszy). Nowe wiersze zostały oznaczone gwiazdkami:

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
// Plik: editor/Editor.cpp
    } else if (event.type == SDL_MOUSEMOTION) {
        m_pointer_window_x =       event.motion.x / win_width;
        m_pointer_window_y = 1.0 - event.motion.y / win_height;
        if (IsGuiVisible()
         && m_gui->OnMouseMove(m_pointer_window_x, m_pointer_window_y)) {
            // przesuń kursor poza ekran (można to zrobić ładniej)
            m_pointer_x = m_pointer_y = 1000;
        } else {
            m_pointer_x = MapWindowCoordToWorldX(m_pointer_window_x);
            m_pointer_y = MapWindowCoordToWorldY(m_pointer_window_y);
*           MoveToCoords(m_pointer_x, m_pointer_y);
        }
    } else if (event.type == SDL_MOUSEBUTTONDOWN) {
        m_pointer_window_x =       event.motion.x / win_width;
        m_pointer_window_y = 1.0 - event.motion.y / win_height;
        if (IsGuiVisible() && m_gui->OnMouseDown(event.button.button,
                                                 m_pointer_window_x,
                                                 m_pointer_window_y)) {
        } else {
            m_pointer_x = MapWindowCoordToWorldX(m_pointer_window_x);
            m_pointer_y = MapWindowCoordToWorldY(m_pointer_window_y);
            ActionAtCoords(m_pointer_x, m_pointer_y);
        }
*   } else if (event.type == SDL_MOUSEBUTTONUP) {
*       m_pointer_x = MapWindowCoordToWorldX(m_pointer_window_x);
*       m_pointer_y = MapWindowCoordToWorldY(m_pointer_window_y);
*       ReleaseAtCoords(m_pointer_x, m_pointer_y);
    }
  

Wywołanie funkcji StartAt powinno pojawić się w momencie naciśnięcia klawisza myszy, czyli w metodzie Editor::ActionAtCoords. Jest w niej sprawdzany rodzaj pędzla, więc musimy dodać tam obsługę nowego typu Brush::ST::Multi. Zamieniamy więc:

1
2
3
4
5
6
      } else if (special_type == Brush::ST::Eraser) {
          ClearFieldAt(static_cast<size_t>(x), static_cast<size_t>(y));
      } else {
          std::cerr << "Niezdefiniowana akcja w trybie specjalnym" << std::endl;
      }
  

na

1
2
3
4
5
6
7
8
9
      } else if (special_type == Brush::ST::Eraser) {
          ClearFieldAt(static_cast<size_t>(x), static_cast<size_t>(y));
      } else if (special_type == Brush::ST::Multi) {
          MultiBrushPtr mb = boost::dynamic_pointer_cast<MultiBrush>(brush);
          mb->StartAt(x, y);
      } else {
          std::cerr << "Niezdefiniowana akcja w trybie specjalnym" << std::endl;
      }
  

Dodanie przycisku dla multipędzla do GUI

Mechanizm obsługi nowego pędzla jest już prawie ukończony. Zanim przejdziemy dalej, dodamy kod do wyświetlania w GUI przycisku skojarzonego z nowym pędzlem.
Wykorzystamy istniejącą klasę BrushButton, gdyż nasz przycisk będzie skojarzony z pędzlem. Dokładniej rzecz ujmując, będzie przechowywał wskaźnik (oczywiście inteligentny) na podklasę Brush, czyli MultiBrush. Dla przycisku nie ma to jednak większego znaczenia, więc nie musimy tworzyć nowej klasy reprezentującej przycisk.

Aby przycisk pojawił się na ekranie, należy utworzyć instancję BrushButton i dodać ją do kontenera m_buttons klasy EditorGui. Rejestrowanie kontrolek odbywa się w metodzie EditorGui::Init. Dopisujemy nowe makra, które będą pomocne w dodawaniu przycisków z multipędzlami. Wprawne oko natychmiast zauważy, że definicje ADD_BUTTON oraz ADD_MULTIBUTTON różnią się makrem wykorzystanym do tworzenia przycisku. Są to odpowiednio BUTTON i MULTIBUTTON. Te z kolei różnią się sposobem tworzenia pędzla. Pierwsza makrodefinicja tworzy pędzel typu Brush, a druga -- MultiBrush.

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
// Plik editor/EditorGui.cpp, metoda EditorGui::Init
#define BUTTON(name,pos,size,type)  \
    BrushButtonPtr(new BrushButton(Sprite::GetByName(name),  \
                                   pos, size,  \
                                   Brush::New(Sprite::GetByName(name), type)))
#define ADD_BUTTON(name,pos,size,type)  \
    m_buttons.push_back(BUTTON(name,pos,size,type))
 
#define MULTIBUTTON(name,pos,size,type)  \
    BrushButtonPtr(new BrushButton(Sprite::GetByName(name),  \
                                   pos, size,  \
                                   MultiBrush::New(Sprite::GetByName(name))))
#define ADD_MULTIBUTTON(name,pos,size,type)  \
    m_buttons.push_back(MULTIBUTTON(name,pos,size,type))
 
    // nazwy sprite'ów zdefiniowane są w SpriteConfig::SpriteConfig
    ADD_BUTTON("gui_eraser",           Position( 0, .0),
                                       default_size*2,
                                       Brush::ST::Eraser);
    ADD_MULTIBUTTON("PlatformMid",     Position(.8, .1),
                                       default_size,
                                       Brush::ST::Multi);
// (...)
 
#undef ADD_MULTIBUTTON
#undef MULTIBUTTON
#undef ADD_BUTTON
#undef BUTTON
} // koniec metody Init
  
0
Twoja ocena: Brak

Copyright © 2008-2010 Wrocławski Portal Informatyczny

design: rafalpolito.com