Eu acho que um tipo de solução robusta seria seguir o caminho orientado a objetos.
Dependendo do tipo de conquista que você deseja apoiar, você precisa de uma maneira de consultar o estado atual do seu jogo e / ou o histórico de ações / eventos que os objetos do jogo (como o jogador) fizeram.
Digamos que você tenha uma classe base de Conquistas, como:
class AbstractAchievement
{
GameState& gameState;
virtual bool IsEarned() = 0;
virtual string GetName() = 0;
};
AbstractAchievement
mantém uma referência ao estado do jogo. É usado para consultar as coisas que estão acontecendo.
Então você faz implementações concretas. Vamos usar seus exemplos:
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;
}
};
Cabe a você decidir quando verificar usando o IsEarned()
método Você pode verificar a cada atualização do jogo.
Uma maneira mais eficiente seria, por exemplo, ter algum tipo de gerente de eventos. E, em seguida, registre eventos (como PlayerHasSlicedSomethingEvent
ou PlayerGotInvicibleEvent
ou simplesmente PlayerStateChanged
) em um método que levaria a conquista no parâmetro Exemplo:
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;
é o mesmo quereturn (...)
Bem, em resumo, as conquistas são desbloqueadas quando uma determinada condição é atendida. Portanto, você precisa ser capaz de produzir instruções if para verificar a condição desejada.
Por exemplo, se você quiser saber que um nível foi concluído ou que um chefe foi derrotado, seria necessário que a bandeira booleana se tornasse verdadeira quando esses eventos acontecessem.
Então:
Você pode tornar isso o mais complexo ou simplista possível, para corresponder à condição desejada.
Algumas informações sobre as conquistas do Xbox 360 podem ser encontradas aqui .
fonte
E se todas as ações que o jogador executar postar uma mensagem no
AchievementManager
? Em seguida, o gerente pode verificar internamente se determinadas condições foram atendidas. Os primeiros objetos postam mensagens:E então
AchievementManager
verifica se ele precisa fazer alguma coisa:Você provavelmente desejará fazer isso com enumerações, em vez de seqüências de caracteres. ;)
fonte
AchievementManager
em todos os classe (que é o que o OP estava perguntando como evitar em primeiro lugar). E use um enum ou classes separadas para suas mensagens, não literais de cadeia - usar literais de cadeia para passar o estado é sempre uma má idéia.O último design que usei foi baseado em ter um conjunto de contadores persistentes por usuário e, em seguida, ter as chaves de realizações de um determinado contador atingindo um determinado valor. A maioria era um único par de conquistas / contadores, onde o contador seria apenas 0 ou 1 (e a conquista foi desencadeada em> = 1), mas você pode usar isso para "caras mortos X" ou "baús X encontrados" também. Isso também significa que você pode configurar contadores para algo que não possui conquistas e ainda será rastreado para uso futuro.
fonte
Quando implementei conquistas no meu último jogo, fiz tudo baseado em estatística. As conquistas são desbloqueadas quando nossas estatísticas atingem um determinado valor. Considere Modern Warfare 2: o jogo está acompanhando toneladas de estatísticas! Quantas fotos você tirou com a SCAR-H? Quantas milhas você correu enquanto usava o benefício Lightweight?
Então, na minha implementação, eu simplesmente criei um mecanismo de estatísticas e, em seguida, criei um gerenciador de conquistas que executa consultas muito simples para verificar o status das conquistas ao longo do jogo.
Embora minha implementação seja bastante simplista, ela faz o trabalho. Eu escrevi sobre isso e compartilhei minhas perguntas aqui .
fonte
Use Cálculo de Eventos . Em seguida, faça algumas pré-condições e ações que são aplicadas após as pré-condições serem atendidas:
Use-o como (não otimizado para velocidade!):
Se você deseja torná-lo rápido:
Nota
É difícil dar os melhores conselhos, pois todas as coisas têm prós e contras.
fonte
O que há de errado com as verificações de IF após a ocorrência do evento de conquista?
fonte