Achievements? (aka How do you do it #6)

14 posts

Flag Post

I’m close to making my TD game to count some statistics, and make some achievements out of that. Say, monsters killed number, highest damage, etc. I wonder how do you people do statistics collection and pass-through around the class hierarchy. How to display achievements (tweening is ignored, the question is to how to arrange data flow to statistics module so it’ll say “Achievement!”), how to properly make it accessible for the game engine, etc.

 
Flag Post

Mine are simple booleans that check for certain variables.

if (score>=500) {
fiveHundredPointsAchieved = true;
}

For popups, I just create a text box and set the alpha to 0.
When the achievement is earned, I set the text to “Achievement Unlocked!” or whatever, and set the alpha to 100%.

 
Flag Post

Well, I wonder how do you store them all. “Score>=500” should be checked each time the score is updated, right? There are many different places where one can earn an achievement, up to being spread through different classes, so a mechanism to consolidate all of them is sought. In my case – an achievement “500 kills for a single tower”, shuold be checked within a tower, an achievement “10 dragons slain” (they are rare and powerful) should be checked somewhere that has access to the information that a dragon was killed – which is likely Monster class, an achievement “5 rocks left” (a special case regarding gem combination) should be checked within controller class, as only it has information about whether there are rocks left, and where are they “left”. Etc. While yes, many of them could be implemented as simple booleans, within just the controller class.

Making a popup is pretty easy as is, either make a tweened thing, fill it with info, place and start tweening – voila. Or make a plain sprite with a textfield, slap it over the stage, and remove after a certain amount of frames :)

 
Flag Post

Make a score function, that can be accessed from all those different places.

public function scoreFunct(s:Number) {
score += s;
//check for achievement here
}

That way it checks each time the score is updated.

I always have the stats and achievements in the Document Class, or a special stats Class.

 
Flag Post

Now we’re speaking :) “Special Stats class”. You mean a singleton, a static class like Math, or anything else? Document class is unavailable for me, as I don’t code in the timeline (I don’t even have access to the timeline).

 
Flag Post

Document Class = Constructor Class = Controller Class, no timeline needed.
As for a stats class, it was just a random idea I had then, I’ve never tried it before. :P

 
Flag Post

Hmm. Currently my controller class is pretty much overloaded, and I don’t want to split it, as I’ve already pulled user interface away. All that’s left of it is UI interface code (to interact with UI instance) and game engine. Perhaps yes, that class is the place for one-game statistics, but what about overall? Static structure?

 
Flag Post

The last time I did achievement popups was in AS2. (this game although I also have this game 1 which has a similar implementation)

I created a movieclip that would contain the text to display, and the background tidly bit, and an icon that went with the achievement, and then created a timeline-based animation of it showing up, holding, and then going away again. I forget the exact arrangement of nesting, though. If I were to do the same again today, I’d use a Tween Engine.

Then in my code, I had an array that held all of the achievements, so they were numbered from 0 to 53, I think. Anyway, I’d check for the conditions that any given achievement was achieved in the place where such things were modified, then call a function back to the document class passing a variable that was the achievement number; e.g. gotAchievement(6) and that would cause the popup, do a switch statement to get the text that went with that achievement, set the picture movieclip to n+1 (array value to timeline value), run the animation, and set the achievement array index to true.

Because it was an array, it made it very easy to save and load between sessions.

1 Wow. I am going to want to dig that code back up and figure out why it’s so laggy. Back when I made it I could get 30 FPS on high quality except in the larger boss fights (where it’d drop to medium or low quality and stay at 20+). Now I’m getting 3-5 FPS in the first couple of minutes! Yikes!

 
Flag Post

I have an Achievement class which stores one achievement, whether it’s been obtained yet, and a function that says whether it should be awarded now; and an AchievementManager which maintains the list and has an Update method that can be called from the game code, which returns all the achievements awarded that frame.

On the UI side, I have an AchievementPanel, which is just a Sprite that has a text field and an icon, and an AchievementUI, which handles showing multiple popups at once, fading them out and so on. It’s actually a bit misnamed because I use it for information popups as well as achievements (the ‘Welcome to bZone’ the first time you play that, for instance).

 
Flag Post

What I did was something like this:

All achievements saved for the player are basically booleans stored in a vector (simple and predictable enough, right?) as the player either has them or they don’t. I’d also typically use a vector filled with instances from an achievement class to hold names, descriptions, and relevant icons for each.

Then, check for specific criteria, as in “kill this boss without taking damage,” “kill every add this boss spawns,” “obtain every upgrade” etc. I suspect I was rather graceless with this part though:

For example, on the boss that has “kill this boss without taking damage” I’d specifically write in the code (in the boss’s AI) to track the player’s health from the start of the encounter all the way to the end, and if their health ever went down, a boolean flag would be set to false to indicate that they failed the achievement. Upon the boss’s death, if the player hadn’t failed, it would call a function on the main game container class with the achievement ID (place in player’s achievement vector, of course) This function would check to see if the player already had the achievement. If so, do nothing. If not, flip that achievement’s boolean to true and pop up an achievement toast to show the player that they earned something.

To avoid overlapping in the event of multiple achievements, the achievement popup class would save a static reference to the most recent achievement popup—if one existed, the next achievement popup would be spawned to appear beside it rather than on top of it.

So yeah, I’m sure my implementation could’ve been better, but this was on my first real game. :)

 
Flag Post
Originally posted by Draco18s:

I created a movieclip that would contain the text to display, and the background tidly bit, and an icon that went with the achievement, and then created a timeline-based animation of it showing up, holding, and then going away again. I forget the exact arrangement of nesting, though. If I were to do the same again today, I’d use a Tween Engine.

Then in my code, I had an array that held all of the achievements, so they were numbered from 0 to 53, I think. Anyway, I’d check for the conditions that any given achievement was achieved in the place where such things were modified, then call a function back to the document class passing a variable that was the achievement number; e.g. gotAchievement(6) and that would cause the popup, do a switch statement to get the text that went with that achievement, set the picture movieclip to n+1 (array value to timeline value), run the animation, and set the achievement array index to true.

Because it was an array, it made it very easy to save and load between sessions.

1 Wow. I am going to want to dig that code back up and figure out why it’s so laggy. Back when I made it I could get 30 FPS on high quality except in the larger boss fights (where it’d drop to medium or low quality and stay at 20+). Now I’m getting 3-5 FPS in the first couple of minutes! Yikes!

I did pretty much exactly the same as this.

Also, on your sudden increase in lag, if it is in AS2, the newer flash player updates seem to have decreased AS2 performance, apparently.

 
Flag Post

Well, an array of booleans under all the stuff is a must, after all an achievement is either there or not. I am now thinking to use events to throw towards anything that would catch achievement-related info with a data object attached. Like this:

var e:AchEvent=new AchEvent(AchEvent.GOT_ACHIEVEMENT);
e.data=preparedObject;
dispatchEvent(e);

Both sides cache if the event regarding a particular achievement was fired, so no flooding will occur, and the actual “Achievement!” notification will only raise the first time. This might work towards accumulating statistics from the game, completed, quitted or restarted as needed.

A vector of achievement descriptions and stuff? Sounds reasonable, I’ll look into it. (I have to draw that many icons first, of course)

Checking a special condition like “kill boss without taking damage” – well, this could be solved via statistics collection. Say, when you take damage, the amount is delivered to you, and added to battle statistics object of any kind, and at the end of battle you plain check if there’s a zero, and the monster defeated is “this boss”, I’d say messing with an AI is not good for such purposes. But, it’s about the same why I currently look at my code I’ve made some ten years before, and cry. :-) I guess no one is prone to such decisions. That’s why I’m trying to collect various approaches, so to weigh them against my condition and take the best suited one.

Originally posted by BobJanova:

I have an Achievement class which stores one achievement, whether it’s been obtained yet, and a function that says whether it should be awarded now; and an AchievementManager which maintains the list and has an Update method that can be called from the game code, which returns all the achievements awarded that frame.

Hmm, checking achievements per frame? Isn’t it performance heavy? It shouldn’t be an issue with a TD I think, and there aren’t any reasonable achievements to be checked for every frame except time based ones (“survived 60 seconds” etc).

 
Flag Post

If it’s too much then you can call that update method less often. I haven’t found it to be a serious problem, though. The test functions to trigger an achievement are generally trivial. Checking them is a negligible amount compared to the physics-heavy game logic I tend to go in for.

 
Flag Post
Originally posted by feartehstickman:

Also, on your sudden increase in lag, if it is in AS2, the newer flash player updates seem to have decreased AS2 performance, apparently.

Yeah, I was thinking it was AS3, but it’s not now that I think about it. Was done for the Scion Driving Creativity contest (aka Shootorial).