10

I mistakenly posted this to stackoverflow and now posting here based on a suggestion in on that site ...

This is a very high level conceptual question. Say in a software application I have 4 different actions, for example: Upload, Share, Comment, and like

And I want to give achievement badges to users like:

  • Rookie - Upload your first 5 files
  • Upload Junkie - Upload 20 files in 1 day
  • Night Crawler - Upload a file after midnight
  • Share-a-holic - Share 10 different files
  • Likes-everything - Like 20 different files

you get the idea. What is he best way to check and see if a user has achieved a particular achievement without having to compile the logic for achievement into my code? And .. - Retain the ability to Add new achievements post compile (xml or db) - Achievements must track specific actions, number of times, and additional criteria (like time of day) - Detection should be near real-time so the user notified almost instantly when an achievement is completed

My biggest questions is, how do I detect these achievements being achieved? Do I:

1) Check after every action to see if ... (Most real time) 2) Have another program check the DB at all times against a set of rules? (Easiest)

Is there another paradigm that I'm missing? I feel there definitely is because in many games (like jetpack for iOS for example), I'm notified of the achievement I have unlocked in the instant in which I unlock it which I found pretty impressive.

Thank you

Robert Harvey
  • 199,517
  • Have you designed or made the achievements system yet and just want to know how to using it in game? –  Jun 19 '13 at 01:27

2 Answers2

6

What you generally do is have an "achievement" system. Any action which happens calls the achievement system and says "hey this thing just happened".

The achievement system is then usually a set of rules, usually made up of simple combinatinatorial logic and counters for various "events". It’s then the achievement system's responsibility for working out, with the help of the rules and counters, whets happened, and what achievements to give out.

The reason you do this is twofold.

  • You don’t want to spread your state logic all over your codebase.

  • sometimes achievements will require combinations of "stuff" which may happen in completely different systems / times / etc and spreading the logic for this around your code base would result in large amounts of unneeded complexity.

The way I've done it in the past is to use a scriptable rules system (where scriptable is a very loose term, usually just some kind of data driven comparator). So you can say something akin to:

  • When "the user pressed something" happens "increment" the "pressed" variable.

Then you could have another rule which is

  • When the "pressed" variable is "greater" than “some value" hand out achievement "blah"

Or perhaps

  • When "the user kills the boss" happens "hand out" achievement "blah".

The achievement system will also be tasked with maintaining the state of which achievements have already been handed out, so you don't get duplicates.

Robert Harvey
  • 199,517
Matt D
  • 1,045
0

A neat way to handle this might be a specification pattern. You'd have an achievement manager, which would periodically query your users for those that match any of a set of specifications. Your achievement class would therefore include a name, logo, point score etc as normal, and also a specification describing a user who has gained that achievement. For example, in C#, the "Share-a-holic" achievement might look like this:

AchievementType ShareAHolic = new AchievementType
{
    Name = "Share-a-holic",
    Description = "Shared 10 files",
    Score = 25,
    Specification = (user => user.SharedFiles.Distinct().Count() > 10)
};

AchievementManager.AddAchievementType(ShareAHolic);

and then at the appropriate point, your achievement manager can do something like this:

foreach (AchievementType achievement in AchievementTypes)
{
    var users = DB.Users.Where(achievement.Specification && !(user.Achievements.Contains(achievement)));
    foreach (User u in shareaholics)
    {
        AchievementManager.Award(u, achievement);
    }
}

Your achievement manager's .Award() method can also be called directly at the appropriate point if an achievement can be detected immediately. You might also want to add a method to run these checks on a specific user, so that you can trigger a check after important events to allow users to get achievements immediately.

anaximander
  • 2,285
  • Partitioning achievements becomes quickly important, AchType needs a 'sope' property that can assist DB.Users.Where() to avoid unrelated achievements, when possible. So if the file share achievement can only be awarded in PvP, only search for PvP. While something generic like looting might be feasible globally and would not have such a scope. – hpavc Mar 21 '16 at 19:42