답변:
나는 일종의 강력한 해결책이 객체 지향 방식으로가는 것이라고 생각합니다.
지원하고자하는 업적에 따라 게임의 현재 상태 및 / 또는 게임 오브젝트 (플레이어 등)가 만든 액션 / 이벤트 기록을 쿼리 할 수있는 방법이 필요합니다.
다음과 같은 기본 Achievement 클래스가 있다고 가정 해 봅시다.
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가 처음에 피하는 방법을 묻는 것입니다). 그리고 문자열 리터럴이 아닌 메시지에 열거 형 또는 별도의 클래스를 사용하십시오. 문자열 리터럴을 사용하여 상태를 전달 하는 것은 항상 나쁜 생각입니다.
마지막 게임에서 업적을 구현했을 때 모든 통계를 기반으로했습니다. 통계가 특정 값에 도달하면 성과가 잠금 해제됩니다. Modern Warfare 2 : 게임이 수많은 통계를 추적합니다! SCAR-H로 몇 번이나 촬영 했습니까? 경량 특전을 사용하는 동안 몇 마일을 질주 했습니까?
구현에서 통계 엔진을 만든 다음 게임 플레이 전체의 업적 상태를 확인하기 위해 매우 간단한 쿼리를 실행하는 업적 관리자를 만들었습니다.
내 구현은 매우 단순하지만 작업이 완료됩니다. 나는 그것에 대해 쓰고 여기에서 내 질문을 공유했습니다 .
이벤트 미적분을 사용하십시오 . 그런 다음 전제 조건이 충족 된 후 적용되는 전제 조건 및 조치를 작성하십시오.
다음과 같이 사용하십시오 (속도에 최적화되지 않았습니다!) :
빨리 만들고 싶다면 :
노트
모든 것이 장단점이 있기 때문에 최선의 조언 을하기는 어렵습니다 .
업적 이벤트가 발생한 후 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 (...)