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

02.06.2011 - Marcin Milewski
TrudnośćTrudność

Pobieranie polecenia z klasy Brush

Wiemy już jak wygląda klasa bazowa dla poleceń wykonywanych w edytorze. Umiemy także wycofywać kolejne zadania. Potrzebne jest nam jeszcze uzyskiwanie poleceń z pędzla. Chcemy, żeby pędzle mogły zwracać różne zadania (definiowane jako podklasy EditorCommand), dlatego nową metodę Brush::GetCommand czynimy wirtualną. Klasa MultiBrush będzie zwracała polecenie PlatformEditorCommand, które zdefiniujemy niebawem.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Plik Brush.h, klasa Brush
#include "EditorCommand.h"
 
// (...)
 
public:
    virtual EditorCommandPtr GetCommand() const { return EditorCommandPtr(); }
 
// (..)
 
// klasa MultiBrush
public:
    virtual EditorCommandPtr GetCommand() const {
        if (IsActive()) {
            std::cerr << "UWAGA: Pobranie akcji z aktywnego pędzla multibrush. "
                      << "Akcja jest gotowa dopiero, gdy pędzel jest nieaktywny";
        }
        return PlatformEditorCommandPtr(
                   new PlatformEditorCommand(GetStart(), GetEnd()));
    }
  

Jak widać w metodzie Brush::GetCommand, domyślnie zwracana jest pusta komenda. Do tej pory akcja, którą należy wykonać dla instancji typu Brush była podejmowana w metodzie Editor::ActionAtCoords. Dlatego to ją poddamy refaktoringowi definiując jednocześnie następujące podklasy EditorCommand:

  • SetFieldCommand -- polecenie, które ustawia wskazane pole na odpowiedni typ;
  • AddEntityCommand -- polecenie, które dodaje jednostkę do planszy;
Metoda Editor::ActionAtCoords wygląda teraz następująco (uwagę należy zwrócić przede wszystkim na wywołania funkcji RegisterAndExecuteCommand):

Hierarchia dziedziczenia. EditorCommand to abstrakcyjna klasa bazowa.
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
void Editor::ActionAtCoords(double x, double y) {
    BrushPtr brush = m_gui->GetActiveBrush();
    if (brush) {
        if (InPaintingFieldMode()) {
            RegisterAndExecuteCommand(
                SetFieldCommandPtr(new SetFieldCommand(static_cast<size_t>(x),
                                                       static_cast<size_t>(y),
                                                       brush->GetFieldType()))
            );
        } else if (InPaintingEntityMode()) {
            const ET::EntityType entity_type = brush->GetEntityType();
            assert(entity_type!=ET::UNKNOWN);
            assert(entity_type!=ET::COUNT);
            const std::string name = EntityTypeAsString(entity_type);
            const LevelEntityData entity_data(name, x, y);
            RegisterAndExecuteCommand(
                AddEntityCommandPtr(new AddEntityCommand(entity_data))
            );
        } else if (InPaintingSpecialMode()) {
            const Brush::ST::SpecialType special_type = brush->GetSpecialType();
            if (special_type == Brush::ST::Player) {
                m_player_data = LevelEntityData("player", x, y);
            } else if (special_type == Brush::ST::Eraser) {
                RegisterAndExecuteCommand(
                    SetFieldCommandPtr(new SetFieldCommand(static_cast<size_t>(x),
                                                           static_cast<size_t>(y),
                                                           FT::None))
                );
            } else if (special_type == Brush::ST::Multi) {
                MultiBrushPtr mb = boost::dynamic_pointer_cast<MultiBrush>(brush);
                mb->StartAt(x, y);
                // Polecenie zostanie dodane po zwolnieniu klawisza (-> ReleaseAt)
            } else {
                std::cerr << "Niezdefiniowana akcja w trybie specjalnym\n";
            }
        }
        else {
            std::cerr << "Nie odnaleziono trybu rysowania" << std::endl;
            assert(false && "Nie odnaleziono trybu rysowania");
        }
    }
}
  

Jak sugeruje komentarz w kodzie, polecenie dla multipędzla pobierane jest w metodzie ReleaseAt. Wynika to z trójfazowego dostarczania informacji do tego zaawansowanego pędzla (StartAt, MoveTo, FinishAt).
Oczywiście moglibyśmy przenieść także pozostałe akcje do metody ReleaseAt. Jednak pozostaje kwestią gustu czy komenda powinna zostać wykonana w momencie naciśnięcia czy zwolnienia klawisza myszy. Pamiętajmy, że nawet jeżeli użytkownik się pomyli i wykona złą akcję, to będzie mógł wycofać wprowadzone zmiany.
Zaktualizowana definicja metody Editor::ReleaseAt wygląda następująco:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Plik: editor/Editor.cpp
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);
    }
 
    EditorCommandPtr command = brush->GetCommand();
    if (command && command->IsReady()) {
        command->Execute(this);
        m_commands.push_back(command);
    }
}
  
0
Twoja ocena: Brak

Copyright © 2008-2010 Wrocławski Portal Informatyczny

design: rafalpolito.com