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

15.03.2011 - Marcin Milewski
TrudnośćTrudność

Nieodłącznym elementem każdego edytora poziomów jest graficzny interfejs, zatem dodamy go także do naszej gry. W artykule zajmiemy się opracowaniem klas do obsługi graficznego interfejsu użytkownika. Dzięki temu edytowanie poziomów będzie łatwe i przyjemne.

Poprzedni artykuł - Edytor poziomów cz. 1 Następny artykuł - Edytor poziomów cz. 3

Pobierz początkowy kod źródłowy do tego artykułu.

W poprzedniej części cyklu o tworzeniu gry platformowej zajmowaliśmy się implementacją mechaniki edytora poziomów. W tym artykule dodamy do niego graficzny interfejs użytkownika. Użytkownik, przy pomocy myszki, będzie mógł wybierać jakie elementy chce dodawać do poziomu klikając w wybrane miejsce na planszy. Dodamy także możliwość usuwania pól z poziomu.

Od strony wizualnej, widoczny będzie tak naprawdę tylko jeden rodzaj elementu -- przycisk. Zadbamy jednak o to, aby można było łatwo dodawać kolejne kontrolki (zwane także widżetami, z ang. widget). Do reprezentacji przycisków wykorzystamy animowane sprite'y obiektów, które się pod nimi kryją.

Poniżej możemy zobaczyć film prezentujący naszą grę wzbogaconą o graficzny interfejs użytkownika w edytorze.

Graficzny interfejs użytkownika

Jak dotąd nie potrzebowaliśmy mechanizmu GUI (Graphical User Interface). Aby się o tym przekonać, wystarczy sięgnąć pamięcią do poprzednich części cyklu tworzenia gry w stylu Mario. Wydaje się jednak, że z czasem przybędzie elementów do wyświetlania, zatem warto wydzielić klasy odpowiedzialne za obsługę poszczególnych elementów graficznego interfejsu użytkownika.

Abstrakcyjna klasa bazowa Gui będzie wykonywała zadanie analogiczne do AppState, dlatego znajdziemy tutaj takie metody czysto wirtualne, jak Init, Draw czy Update. Ponadto udostępnimy kilka publicznych (ale także wirtualnych) funkcji do obsługi urządzeń wejścia -- klawiatury oraz myszy. Deklarację (i jednocześnie definicję) klasy Gui umieszczamy w podkatalogu gui/ w pliku Gui.h:

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
// Plik: gui/Gui.h
#ifndef __GUI_H_INCLUDED__
#define __GUI_H_INCLUDED__
 
class Gui;
typedef boost::shared_ptr<Gui> GuiPtr;
 
class Gui : public boost::enable_shared_from_this<Gui> {
public:
    virtual ~Gui() {}
 
    virtual void Start() = 0;
    virtual void Init() = 0;
    virtual void Draw() = 0;
    virtual void Update(double dt) = 0;
 
    virtual bool OnKeyDown(const SDLKey& /* key */) { return false; }
    virtual bool OnKeyUp(const SDLKey& /* key */)   { return false; }
    virtual bool OnMouseMove(double /* x */, double /* y */)
                 { return false; }
    virtual bool OnMouseDown(Uint8 /* button */, double /* x */, double /* y */)
                 { return false; }
};
 
#endif
  

Podstawową jednostką wyświetlaną w GUI będzie widżet. Będziemy reprezentować go klasą GuiWidget, która dostarcza podstawowych metod do rysowania, aktualizacji, manipulacji pozycją oraz rozmiarem kontrolki na ekranie. Metoda GetPosition zwraca położenie lewego dolnego narożnika widżetu, a GetSizedPosition -- prawego górnego. Możliwa jest także manipulacja widocznością kontrolki przez wykorzystanie metody SetVisible. Oto pełny kod klasy GuiWidget:

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// Plik: gui/GuiWidget.h
#ifndef __GUIWIDGET_H_INCLUDED__
#define __GUIWIDGET_H_INCLUDED__
 
#include "../BasicMathTypes.h"
 
class GuiWidget;
typedef boost::shared_ptr<GuiWidget> GuiWidgetPtr;
 
class GuiWidget {
public:
    explicit GuiWidget(Position position, Size size, bool is_visible)
        : m_position(position),
          m_size(size),
          m_is_visible(is_visible) {
    }
    virtual ~GuiWidget() {}
 
    virtual void Draw() const = 0;
    virtual void Update(double dt) = 0;
 
    Position GetPosition()      const { return m_position; }
    Position GetSizedPosition() const {
        return Position(GetPosition().X() + GetSize().X(),
                      GetPosition().Y() + GetSize().Y()); }
    Size     GetSize()          const { return m_size; }
    bool     IsVisible()        const { return m_is_visible; }
    GuiWidget* MoveBy(const Vector2& vector)
        { m_position += vector; return this; }
    GuiWidget* SetSize(const Vector2& vector)
        { m_size = vector;      return this; }
    GuiWidget* SetPosition(const Vector2& vector)
        { m_position = vector;  return this; }
    GuiWidget* SetVisible(bool is_visible)
        { m_is_visible = is_visible; return this; }
 
    Aabb GetAabb() const {
        return Aabb(GetPosition().X(), GetPosition().Y(),
                    GetSizedPosition().X(), GetSizedPosition().Y());
    }
private:
    Size m_size;          // rozmiar (szerokość i wysokość) (przestrzeń okna)
    Position m_position;  // położenia (lewego dolnego narożnika) na ekranie
    bool m_is_visible;    // czy kontrolka jest widoczna
};
 
#endif
  

GuiWidget jest jedynie klasą bazową dla wszystkich kontrolek. Poniżej przedstawiamy kod bardzo prostego elementu graficznego interfejsu użytkownika -- przycisk wyświetlany w postaci sprite'a:

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
33
34
35
36
// Plik: gui/SpriteButton.h
#ifndef __SPRITEBUTTON_H__INCLUDED__
#define __SPRITEBUTTON_H__INCLUDED__
 
#include "GuiWidget.h"
#include "../Sprite.h"
 
class SpriteButton;
typedef boost::shared_ptr<SpriteButton> SpriteButtonPtr;
 
class SpriteButton : public GuiWidget {
public:
    explicit SpriteButton(SpritePtr sprite, Position position, Size size)
        : GuiWidget(position, size, true),
          m_sprite(sprite) {
    }
      
    virtual ~SpriteButton()   {}
 
    virtual void Draw() const
        { m_sprite->DrawCurrentFrame(GetPosition(), GetSize()); DoDraw(); }
 
    virtual void Update(double dt) { m_sprite->Update(dt); DoUpdate(); }
 
    SpritePtr GetSprite() const    { return m_sprite; }
 
protected:
    virtual void DoDraw() const {}
    virtual void DoUpdate() {}
 
private:
    SpritePtr m_sprite;
};
 
#endif
  

To proste opakowanie sprite'a jest klasą wyjściową dla przycisku przedstawiającego konkretne pędzle. W konstruktorze podajemy sprite, który będzie reprezentacją pędzla na ekranie, położenie oraz rozmiar przycisku, a także wskaźnik na przechowywany pędzel. Całość zajmuje jedynie kilka wierszy kodu. Ponieważ jest to element GUI specyficzny dla edytora, dlatego plik BrushButton.h umieszczamy w katalogu editor.

Hierarchia dziedziczenia dla przycisku.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#ifndef __BRUSHBUTTON_H__INCLUDED__
#define __BRUSHBUTTON_H__INCLUDED__
 
#include "../gui/SpriteButton.h"
#include "Brush.h"
 
class BrushButton;
typedef boost::shared_ptr<BrushButton> BrushButtonPtr;
 
class BrushButton : public SpriteButton {
public:
    explicit BrushButton(SpritePtr sprite, Position position,
                         Size size,        BrushPtr brush)
        : SpriteButton(sprite, position, size),
          m_brush(brush)  {
    }
    BrushPtr  GetBrush()  const    { return m_brush; }
private:
    BrushPtr m_brush;
};
 
#endif
  
0
Twoja ocena: Brak

Copyright © 2008-2010 Wrocławski Portal Informatyczny

design: rafalpolito.com