Achievement System Class


(Tal3D) #1

Hey there. I recently have been working on a mini game in flixel and stumbled upon something that could really improve the replay value of my game: achievements. As I couldn’t find an existing flixel implementation of achievements I decided to create one myself. I ended up with a stable version that suited my needs and decided to share it with more people.

I wasn’t quite sure whether to post it here or not, as it is built on top of flixel but I thought it might come in handy for someone - and besides, it probably won’t be too difficult to port over to flashpunk.

As for now, it really only has the very basic features you would need for an achievement system:

  • Pass and read achievements and their status
  • Unlock achievements
  • Save achievement progress (using flash’s sharedObject)
  • Visual overlay that pops-up on unlock

In case you are interested I have set up an github repo for what I call the FlxAchievementSystem: https://github.com/Tal3D/FlxAchievementSystem


If you have any questions or feedback feel free to comment. Also I would love to improve this project, so help on it is highly appreciated!


(Tal3D) #2

I have been working on it a bit more. Do you guys think making the class a singleton would work? How could I do this the best (I need help obviously).

Thanks.


(Zachary Lewis) #3

The first improvement I can think of is to make it work with FlashPunk. This could be done by dropping all framework-specific code and allow the user to provide function hooks to achievement notifications.

A singleton class would be ideal, since there should really only be one achievement manager running. If you were asking for help on how to “do this (singletons) the best,” there’s a good article on gskinner.com that provides a singleton implementation for AS3.

Another improvement I’d like to see in a drop-in system like this would be to allow it to manage itself. I want to be able to create achievement types and have the achievement system track them for me.

Achievement Setup

// Get a handle to the Achievement System.
var cheevos:AchievementSystem = AchievementSystem.getInstance();

// Add an achievement that continues to build up to a target value.
cheevos.add(new IncrementalAchievement("Kissed one million enemies.", 1000000));

// Add an achievement that will trigger once a minimum value is reached.
cheevos.add(new MinimumValueAchievement("Kissed five enemies at once.", 5));

// Add a new achievement that is unlocked manually.
cheevos.add(new BooleanAchievement("Kissed the biggest boss."));

Updating Achievements

// Called in an enemy's onKissed function.
cheevos.getAchievement("Kissed one million enemies.").update();

// Called in the player's update once the kiss streak ends.
cheevos.getAchievement("Kissed five enemies at once.").update(currentKissStreak);

// Called from BiggestBoss's overloaded onKissed function.
cheevos.getAchievement("Kissed the biggest boss.").update();

Creating Custom Achievements

public class CostumeAchievement extends Achievement implements IAchievement
{
  private var _headApparel:String;
  private var _chestApparel:String;

  public function CostumeAchievement(name:String, head:String, chest:String)
  {
    _headApparel = head;
    _chestApparel = chest;
    super(name);
  }

  public function update(args:*):Boolean
  {
    // If both "head" and "chest" exist and match the requirements, award cheevo.
    return args != null && args["head"] != null && args["chest"] != null &&
          args["head"] == _headApparel && args["chest"] == _chestApparel;
  }
}
cheevos.add(new CostumeAchievement ("I am a blood ninja.", "wizard hat", "robe"));

// Called from the player's onEquip function.
cheevos.getAchievement("I am a blood ninja.").update(
  {head:currentHat, chest:currentArmor});

Also, the ability to authenticate with an external server. :wink:


(Tal3D) #4

@zachwlewis Thanks for the advice!

I have two concerns, though.

Achievement Types

If I get you right, the achievements would always check for completion themselves, which means that you would have to call update() or somehting like this on every achievement every frame. But instead of doing this, wouldn´t it be easier and less restrictive (instead of special achievement “types”) to just call AchievementSystem.unlock(“Achievement_Name”) if an achievement should be unlocked? Then you would for example do:

if(enemiesKissed >= 100)
{
    AchievementSystem.unlock("Enemie Lover");
}

Rather than inside the achievement itself, this would be placed inside the world or a function like check() and be called every frame. Right now if you call unlock it would then change the unlocked boolean of the achievement and update it in the save file aswell as call start() on the notifier which would then play its animation. If you did it like this you could check for everything and just call update() if its completed instead of creating a special class for that behaviour (especially for more fancy things like your costume demo).

Adding Achievements After Instantiation

Lastly the way I am saving/loading achievement progress right now is by checking the local achievement array against the saved version of it when the achievementsystem gets instantiated. To eradicate errors when loading the saved file will only load when the local achievements array has the same length, same sequence of achievements (always checks for the name), otherwise it will be overwritten by the local array which has no progress in it.

What I am saying is that to be able to track achievement progress the achievement array mustnt be changed after instantiating the achievementsystem. You can check out the quickstart on my repo mainpage where I point this out.

Anyways thanks a lot for the advice, I will look into converting it to a singleton (I suppose first of all I will have to change a lot of variables to be static).

P.S. In case anyone whith a better understanding and insight into the flashpunk world would be willing to lend me a hand porting it just let me know :wink:


(Zachary Lewis) #5

You can have the achievement unlock itself, though.

public class CostumeAchievement extends Achievement implements IAchievement
{
  private var _headApparel:String;
  private var _chestApparel:String;

  public function CostumeAchievement(name:String, head:String, chest:String)
  {
    _headApparel = head;
    _chestApparel = chest;
    super(name);
  }

  public function update(args:*):Boolean
  {
    // If both "head" and "chest" exist and match the requirements, award cheevo.
    var isUnlocked:Boolean = args != null &&
                                          args["head"] != null &&
                                          args["chest"] != null &&
                                          args["head"] == _headApparel &&
                                          args["chest"] == _chestApparel;
    if (isUnlocked)
    {
      AchievementSystem.unlock(this);
    }

    return isUnlocked;
  }
}

(Tal3D) #6

##Singleton

Ok so it turned out to be a little difficult to convert it to a singleton. Is there anything bad about just setting the achievement system up as a static var (just as I explain it in my repo readme)? It seems like the most convenient way besides it is the cleanest one.

##AchievementSystem self organisation

Making the achievements check for their completion themselves is a really good idea and works real nice, so an update will be pushed soon.

##Porting

About porting it to flashpunk: because I would like to have at least one default notifier (visual overlay) style could anyone help me create one (basically only several flashpunk display objects). Additionally, is there anything like flixel’s plugin list in flashpunk? In flixel the plugin list is useful cause the update function of FlxAchievementSystem will be called before all of the other non-plugin entities just like the render/draw function will be called after all of the other entities which makes the notifier appear on top of everything.


More info on the website.

Thanks for the help.