Zobaczmy teraz, co kryje się metodach SaveGame oraz LoadGame. Pierwsza z nich kopiuje
obiekt bohatera do zmiennej m_stored_player oraz pole m_player_saved_pos_x
do m_stored_saved_player_pos_x.
Druga z nich, co jest trochę zaskakujące, niczego nie przywraca. Wszystko za sprawą użycia efektu
przejścia. W momencie utraty życia przez gracza, gra wykona efektowne przejście... sama w siebie. Zgodnie z
podanym niżej przepisem nastąpi zaciemnienie, a potem przełączenie na stan spod zmiennej game
równej shared_from_this() (odpowiednik this). Stąd też wzięło się
pole m_should_load_when_active_again. W momencie uruchomienia metody Start w
klasie App, gra musi wiedzieć, że nastąpił respawn i przenieść gracza w odpowiednie miejsce.
Oczywiście cała ta gimnastyka jest zbędna jeżeli zadowolimy się zwykłym przeskoczeniem obrazu do nowego
stanu. Można jednak założyć się, że gracz byłby co najmniej zaskoczony nagłą zmianą scenerii. Dlatego warto
poświęcić chwilę na dodanie efektu płynnego przejścia. Oto ciała metod SaveGame
i LoadGame:
Właściwe odtworzenie stanu znajduje się w metodzie Game::Start (która poprzednio składała się
wyłącznie z linii odgrywającej muzykę):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Plik: Game.cppvoid Game::Start(){
Engine::Get().GetSound()->PlayMusic("game");// STAREif(m_should_load_when_active_again){
m_player->RespawnFrom(m_saved_player);
m_stored_player_pos_x = m_saved_stored_player_pos_x;
m_should_load_when_active_again =false;}else{// Zapisujemy na początku poziomu
SaveGame(m_player);}
SetDone(false);
m_next_app_state.reset();}
Do metody Init dopisujemy sprawdzenie stanu flagi m_should_load_when_active_again. Jeżeli
jest ustawiona, to znaczy że poziom już został załadowany z pliku, więc procedura kończy swoje działanie.
1
2
3
4
5
6
7
8
9
10
void Game::Init(){if(m_should_load_when_active_again){// NOWEreturn;// NOWE}// NOWE
Engine& engine = Engine::Get();if(!m_level){// (...)}
Przytoczoną przed chwilą metodę LoadGame wywołujemy w Game::Update po aktualizacji stanu
bohatera. Oczywiście najpierw sprawdzamy czy postać sobie tego życzy.
1
2
3
4
5
6
7
// Plik: Game.cpp, metoda Update// uaktualnij obiekt reprezentujący gracza
m_player->Update(dt, m_level);if(m_player->ShouldBeRespawned()){// NOWE
LoadGame();// NOWE
SetDone();// NOWE}// NOWE
Ostatnia zmiana to usunięcie z metody SweepAndAddEntities kawałka kodu usuwającego jednostki,
których gracz nie może już zobaczyć. Niestety, teraz bohater może się cofnąć na planszy, więc dlaczego mamy
mu ułatwiać zadanie usuwając przeciwników z którymi się jeszcze nie uporał?
void Game::SweepAndAddEntities(double/* dt */){// TEN FRAGMENT USUWAMY// // oznacz jednostki, które są za lewą krawędzią ekranu jako martwe// const double distance_of_deletion =// Engine::Get().GetRenderer()->GetHorizontalTilesOnScreenCount();// for (std::vector<EntityPtr>::iterator it = m_entities.begin();// it != m_entities.end();// ++it) {// EntityPtr e = *it;// if (e->GetX() + distance_of_deletion < m_player->GetX()) {// e->SetIsDead(true);// }// }// usuń martwe jednostkifor(size_t i =0; i < m_entities.size();++i){if(m_entities.at(i)->IsDead()){for(size_t c = i; c < m_entities.size()-1;++c){
m_entities.at(c)= m_entities.at(c +1);}
m_entities.resize(m_entities.size()-1);}}// dodaj kolejne jednostki z listy do gry// (...)}
Testujemy!
W celu przetestowania jak działa nasz kod, możemy dodać punkt zapisu gdzieś na planszy.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# ale po co jej szukać gdzieś w pliku?
player 12# kasa, kasa, kasa!
orb 28
orb 37
orb 47# pozostałe obiekty
mush 21
mush 41
mush 51
twinshot_upgrade 43
savepoint 44# NOWE
A oto efekt końcowy:
Masz pytanie, uwagę? Stworzyłeś poziom, którym chcesz się podzielić? A może zauważyłeś błąd? Powiedz o
tym na forum.