Myślę, że pewnego rodzaju solidnym rozwiązaniem byłoby pójście w kierunku obiektowym.
W zależności od rodzaju osiągnięcia, które chcesz wesprzeć, potrzebujesz sposobu na sprawdzenie aktualnego stanu gry i / lub historii działań / zdarzeń, które dokonały obiekty gry (takie jak gracz).
Załóżmy, że masz podstawową klasę osiągnięć, taką jak:
class AbstractAchievement
{
GameState& gameState;
virtual bool IsEarned() = 0;
virtual string GetName() = 0;
};
AbstractAchievement
zawiera odniesienie do stanu gry. Służy do sprawdzania, co się dzieje.
Następnie wykonujesz konkretne wdrożenia. Wykorzystajmy twoje przykłady:
class MasterSlicerAchievement : public AbstractAchievement
{
string GetName() { return "Master Slicer"; }
bool IsEarned()
{
Action lastAction = gameState.GetPlayerActionHistory().GetAction(0);
Action previousAction = gameState.GetPlayerActionHistory().GetAction(1);
if (lastAction.GetType() == ActionType::Slice &&
previousAction.GetType() == ActionType::Slice &&
lastAction.GetObjectType() == ObjectType::Watermelon &&
previousAction.GetObjectType() == ObjectType::Strawberry)
return true;
return false;
}
};
class InvinciblePipeRiderAchievement : public AbstractAchievement
{
string GetName() { return "Invincible Pipe Rider"; }
bool IsEarned()
{
if (gameState.GetLocationType(gameState.GetPlayerPosition()) == LocationType::OVER_PIPE &&
gameState.GetPlayerState() == EntityState::INVINCIBLE)
return true;
return false;
}
};
Następnie to Ty decydujesz, kiedy sprawdzić za pomocą tej IsEarned()
metody. Możesz sprawdzić przy każdej aktualizacji gry.
Bardziej efektywnym sposobem byłoby na przykład posiadanie jakiegoś menedżera zdarzeń. A następnie zarejestruj zdarzenia (takie jak PlayerHasSlicedSomethingEvent
lub PlayerGotInvicibleEvent
po prostu PlayerStateChanged
) w metodzie, która zajęłaby osiągnięcie parametru. Przykład:
class Game
{
void Initialize()
{
eventManager.RegisterAchievementCheckByActionType(ActionType::Slice, masterSlicerAchievement);
// Each time an action of type Slice happens,
// the CheckAchievement() method is invoked with masterSlicerAchievement as parameter.
eventManager.RegisterAchievementCheckByPlayerState(EntityState::INVINCIBLE, invinciblePiperAchievement);
// Each time the player gets the INVINCIBLE state,
// the CheckAchievement() method is invoked with invinciblePipeRiderAchievement as parameter.
}
void CheckAchievement(const AbstractAchievement& achievement)
{
if (!HasAchievement(player, achievement) && achievement.IsEarned())
{
AddAchievement(player, achievement);
}
}
};
if(...) return true; else return false;
jest taki sam jakreturn (...)
Krótko mówiąc, osiągnięcia są odblokowane, gdy spełniony zostanie określony warunek. Musisz więc mieć możliwość tworzenia instrukcji if, aby sprawdzić żądany warunek.
Na przykład, jeśli chcesz wiedzieć, że poziom został ukończony lub boss został pokonany, musisz mieć flagę boolowską, która stanie się prawdziwa, gdy te zdarzenia się zdarzą.
Następnie:
Możesz uczynić to tak złożonym lub uproszczonym, jak to konieczne, aby dopasować się do pożądanego warunku.
Niektóre informacje o osiągnięciach Xbox 360 można znaleźć tutaj .
źródło
Co się stanie, jeśli po każdej akcji, którą gracz wykona, opublikuje wiadomość na adres
AchievementManager
? Następnie menedżer może wewnętrznie sprawdzić, czy zostały spełnione określone warunki. Pierwsze obiekty wysyłają wiadomości:A następnie
AchievementManager
sprawdza, czy musi coś zrobić:Prawdopodobnie będziesz chciał to zrobić z wyliczeniami zamiast ciągów. ;)
źródło
AchievementManager
każdym klasa (o co OP pytał przede wszystkim, jak tego uniknąć). I używaj wyliczeń lub oddzielnych klas dla swoich wiadomości, a nie literałów łańcuchowych - używanie literałów łańcuchowych do przekazywania stanu jest zawsze złym pomysłem.Ostatni zastosowany przeze mnie projekt opierał się na posiadaniu zestawu trwałych liczników na użytkownika, a następnie na osiągnięciu pewnej wartości licznika osiągniętego klucza. Większość stanowiła pojedynczą parę osiągnięć / liczników, gdzie licznik wynosiłby zawsze 0 lub 1 (a osiągnięcie uruchomiono przy> = 1), ale można tego również użyć do „zabitych X kolesi” lub „znalezionych X skrzyń”. Oznacza to również, że możesz skonfigurować liczniki dla czegoś, co nie ma osiągnięć, i nadal będzie śledzone do wykorzystania w przyszłości.
źródło
Kiedy zaimplementowałem osiągnięcia w mojej ostatniej grze, stworzyłem to w oparciu o statystyki. Osiągnięcia są odblokowywane, gdy nasze statystyki osiągną określoną wartość. Zastanów się nad Modern Warfare 2: gra śledzi mnóstwo statystyk! Ile zdjęć wykonałeś za pomocą SCAR-H? Ile mil przebiegłeś podczas korzystania z lekkiego profitu?
Więc w swojej implementacji po prostu stworzyłem silnik statystyk, a następnie stworzyłem menedżera osiągnięć, który uruchamia naprawdę proste zapytania, aby sprawdzić status osiągnięć w trakcie gry.
Chociaż moja implementacja jest dość uproszczona, wykonuje zadanie. Napisałem o tym i udostępniłem tutaj moje zapytania .
źródło
Użyj rachunku zdarzeń . Następnie wykonaj kilka warunków wstępnych i działań, które zostaną zastosowane po spełnieniu warunków wstępnych:
Używaj go jak (niezoptymalizowany pod kątem prędkości!):
Jeśli chcesz zrobić to szybko:
Uwaga
Trudno dać najlepszą radę, ponieważ wszystkie rzeczy mają zalety i wady.
źródło
Co jest nie tak z czekami JEŻELI po wystąpieniu zdarzenia osiągnięcia?
źródło