回答:
一種の堅牢な解決策は、オブジェクト指向の方法を採用することだと思います。
サポートしたい成果に応じて、ゲームの現在の状態やゲームオブジェクト(プレーヤーなど)が行ったアクション/イベントの履歴を照会する方法が必要です。
次のような基本的な達成クラスがあるとします。
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 (...)