Я думаю, что одним из надежных решений было бы пойти по объектно-ориентированному пути.
В зависимости от того, какое достижение вы хотите поддержать, вам нужен способ запросить текущее состояние вашей игры и / или историю действий / событий, совершенных игровыми объектами (например, игроком).
Допустим, у вас есть базовый класс достижения, такой как:
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(...) return true; else return false;
такой же, какreturn (...)
Короче говоря, достижения разблокируются, когда выполняется определенное условие. Таким образом, вы должны иметь возможность создавать операторы if для проверки желаемого условия.
Например, если вы хотите знать, что уровень был пройден или босс был побежден, вам нужно, чтобы булев флаг стал истинным, когда происходят эти события.
Затем:
Вы можете сделать это настолько сложным или упрощенным, насколько это необходимо, чтобы соответствовать желаемому условию.
Некоторую информацию о достижениях Xbox 360 можно найти здесь .
источник
Что если вы при каждом действии игрока отправляете сообщение на
AchievementManager
? Затем менеджер может проверить внутренне, были ли выполнены определенные условия. Первые объекты публикуют сообщения:А затем
AchievementManager
проверяет, нужно ли что-либо делать:Возможно, вы захотите сделать это с помощью перечислений, а не строк. ;)
источник
AchievementManager
каждым класс (это то, что ОП спрашивал, как избежать в первую очередь). И используйте перечисления или отдельные классы для своих сообщений, а не строковые литералы - использование строковых литералов для передачи состояния всегда плохая идея.Последний дизайн, который я использовал, был основан на наборе постоянных счетчиков для каждого пользователя, а затем на достижении определенного счетчика при достижении определенного значения. Большинство из них представляли собой одну пару достижений / счетчиков, в которой счетчик всегда был бы равен 0 или 1 (и достижение срабатывало при> = 1), но вы можете использовать его и для «убитых Х чуваков» или «найденных Х сундуков». Это также означает, что вы можете настроить счетчики для чего-то, что не имеет достижений, и все равно будет отслеживаться для будущего использования.
источник
Когда я реализовал достижения в моей последней игре, я сделал все это на основе статистики. Достижения разблокируются, когда наша статистика достигает определенного значения. Рассмотрим Modern Warfare 2: игра отслеживает тонны статистики! Сколько снимков вы сделали со SCAR-H? Сколько миль вы пробежали, используя легкий вес?
Поэтому в своей реализации я просто создал статистический движок, а затем создал менеджер достижений, который выполняет действительно простые запросы для проверки состояния достижений в течение всего игрового процесса.
Хотя моя реализация довольно проста, она выполняет свою работу. Я написал об этом и поделился своими вопросами здесь .
источник
Используйте исчисление событий . Затем выполните некоторые предварительные условия и действия, которые применяются после выполнения предварительных условий:
Используйте это как (не оптимизировано для скорости!):
Если вы хотите сделать это быстро:
Запись
Трудно дать лучший совет, так как все вещи имеют свои плюсы и минусы.
источник
Что не так с проверками IF после события достижения?
источник