Answers:
我认为一种可靠的解决方案是采用面向对象的方式。
根据您要支持的成就类型,您需要一种查询游戏当前状态和/或游戏对象(例如玩家)所做的动作/事件的历史的方法。
假设您有一个基本的成就类,例如:
class AbstractAchievement
{
GameState& gameState;
virtual bool IsEarned() = 0;
virtual string GetName() = 0;
};
AbstractAchievement
持有游戏状态的参考。它用于查询正在发生的事情。
然后,您进行具体的实现。让我们使用您的示例:
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;
}
};
然后由您决定何时使用该IsEarned()
方法进行检查。您可以在每次游戏更新时进行检查。
例如,一种更有效的方法是拥有某种事件管理器。然后将事件(例如PlayerHasSlicedSomethingEvent
或PlayerGotInvicibleEvent
或简单地PlayerStateChanged
)注册到将取得成就作为参数的方法。例:
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语句来检查所需的条件。
例如,如果您想知道某个级别已完成或老板被击败,则需要在这些事件发生时使布尔标志变为true。
然后:
if(GlobalFlags.MasterBossDefeated == true && AchievementClass.MasterBossDefeatedAchievement == false)
{
AchievementClass.MasterBossDefeatedAchievement = true;
showModalPopUp("You defeated the Master Boss! 30 gamerscore");
}
您可以根据需要将其设置为复杂或简单,以匹配所需的条件。
有关Xbox 360成就的一些信息可以在这里找到。
如果玩家采取的每一项操作都会向该消息发布消息AchievementManager
怎么办?然后,经理可以在内部检查是否满足某些条件。第一个对象发布消息:
AchievementManager::PostMessage("Jump", "162");
AchievementManager::PostMessage("Slice", "Strawberry");
AchievementManager::PostMessage("Slice", "Watermelon");
AchievementManager::PostMessage("Kill", "Goomba");
然后AchievementManager
检查是否需要做任何事情:
if (!strcmp(m_Message.name, "Slice") && !strcmp(m_LastMessage.name, "Slice"))
{
if (!strcmp(m_Message.value, "Watermelon") && !strcmp(m_LastMessage.value, "Strawberry"))
{
// achievement unlocked!
}
}
您可能会希望使用枚举而不是字符串来实现。;)
AchievementManager
在每个类(OP最初要求如何避免)。并为消息使用枚举或单独的类,而不是字符串文字-使用字符串文字传递状态始终是一个坏主意。
使用事件演算。然后制定一些先决条件和满足先决条件后要执行的操作:
像这样使用(未针对速度进行优化!):
如果您想使其更快:
注意
由于万事都有利弊,因此很难给出最佳建议。
成就事件发生后,IF检查有什么问题?
if (Cinimatics.done)
Achievement.get(CINIMATICS_SEEN);
if (EnemiesKiled > 200)
Achievement.get(KILLER);
if (Damage > 2000 && TimeSinceFirstDamage < 2000)
Achievement.get(MEAT_SHIELD);
InvitationAccepted = Invite.send(BestFriend);
if (InvitationAccepted)
Achievement.get(A_FRIEND_IN_NEED);
if(...) return true; else return false;
与return (...)