Ich denke, eine Art robuste Lösung wäre, den objektorientierten Weg zu gehen.
Abhängig davon, welche Art von Leistung Sie unterstützen möchten, benötigen Sie eine Möglichkeit, den aktuellen Status Ihres Spiels und / oder den Verlauf von Aktionen / Ereignissen abzufragen, die die Spielobjekte (wie der Spieler) ausgeführt haben.
Angenommen, Sie haben eine Basis-Leistungsklasse wie:
class AbstractAchievement
{
GameState& gameState;
virtual bool IsEarned() = 0;
virtual string GetName() = 0;
};
AbstractAchievement
enthält einen Verweis auf den Stand des Spiels. Es wird verwendet, um die Ereignisse abzufragen.
Dann machen Sie konkrete Umsetzungen. Lassen Sie uns Ihre Beispiele verwenden:
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;
}
};
Dann liegt es an Ihnen, zu entscheiden, wann die IsEarned()
Methode angewendet werden soll . Sie können bei jedem Spiel Update überprüfen.
Ein effizienterer Weg wäre zum Beispiel, eine Art Event-Manager zu haben. Registrieren Sie dann Ereignisse (wie PlayerHasSlicedSomethingEvent
oder PlayerGotInvicibleEvent
oder einfach PlayerStateChanged
) für eine Methode, die die Leistung als Parameter annehmen würde. Beispiel:
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;
ist das gleiche wiereturn (...)
Kurz gesagt, Erfolge werden freigeschaltet, wenn eine bestimmte Bedingung erfüllt ist. Sie müssen also in der Lage sein, if-Anweisungen zu erstellen, um die gewünschte Bedingung zu überprüfen.
Wenn Sie beispielsweise wissen möchten, dass ein Level abgeschlossen oder ein Boss besiegt wurde, muss die Boolesche Flagge auf "Wahr" gesetzt sein, wenn diese Ereignisse eintreten.
Dann:
Sie können dies so komplex oder simpel gestalten, wie es für die gewünschte Bedingung erforderlich ist.
Einige Informationen zu den Erfolgen von Xbox 360 finden Sie hier .
quelle
Was passiert, wenn Sie bei jeder Aktion, die der Spieler ausführt, eine Nachricht an die
AchievementManager
senden? Dann kann der Manager intern prüfen, ob bestimmte Bedingungen erfüllt sind. Erste Objekte posten Nachrichten:Und dann die
AchievementManager
Überprüfung, ob etwas getan werden muss:Wahrscheinlich möchten Sie dies jedoch mit Aufzählungen anstelle von Zeichenfolgen tun. ;)
quelle
AchievementManager
in every Klasse (das ist es, was OP gefragt hat, wie es zu vermeiden ist). Verwenden Sie eine Aufzählung oder separate Klassen für Ihre Nachrichten und keine Zeichenfolgenliterale. Es ist immer eine schlechte Idee , Zeichenfolgenliterale zum Weitergeben des Status zu verwenden .Das letzte Design, das ich verwendete, basierte darauf, eine Reihe von beständigen Leistungsindikatoren pro Benutzer zu haben und dann Leistungsschlüssel von einem bestimmten Leistungsindikator zu entfernen, der einen bestimmten Wert erreicht. Die meisten waren ein einzelnes Erfolgs- / Gegenpaar, bei dem der Zähler immer nur 0 oder 1 war (und der Erfolg bei> = 1 ausgelöst wurde), aber Sie können dies auch für "getötete X-Typen" oder "gefundene X-Truhen" verwenden. Dies bedeutet auch, dass Sie Zähler für etwas einrichten können, das keine Erfolge erzielt hat, und dass diese weiterhin für die zukünftige Verwendung verfolgt werden.
quelle
Als ich in meinem letzten Spiel Erfolge erzielt habe, habe ich alles statistikbasiert gemacht. Erfolge werden freigeschaltet, wenn unsere Statistiken einen bestimmten Wert erreichen. Betrachten Sie Modern Warfare 2: Das Spiel verfolgt Unmengen von Statistiken! Wie viele Aufnahmen haben Sie mit der SCAR-H gemacht? Wie viele Meilen sind Sie mit dem Lightweight-Vorteil gesprintet?
In meiner Implementierung habe ich einfach eine Statistik-Engine erstellt und dann einen Leistungs-Manager, der wirklich einfache Abfragen ausführt, um den Status der Leistungen während des gesamten Spiels zu überprüfen.
Während meine Implementierung ziemlich simpel ist, erledigt sie die Arbeit. Ich habe darüber geschrieben und meine Fragen hier geteilt .
quelle
Verwenden Sie die Ereignisberechnung . Machen Sie dann einige Vorbedingungen und Aktionen, die angewendet werden, nachdem die Vorbedingungen erfüllt sind:
Verwenden Sie es wie folgt (nicht für Geschwindigkeit optimiert!):
Wenn Sie es schnell machen wollen:
Hinweis
Es ist schwierig, die besten Ratschläge zu geben, da alle Dinge Vor- und Nachteile haben.
quelle
Was ist los mit einer IF- Prüfung nach dem Erfolg?
quelle