Skip to content

Instantly share code, notes, and snippets.

@frostney
Created April 19, 2011 01:58
Show Gist options
  • Save frostney/926660 to your computer and use it in GitHub Desktop.
Save frostney/926660 to your computer and use it in GitHub Desktop.
This is a code snippet on how to implement achievements (as seen on Steam or XBox Live) in JavaScript games using DOM. It utilizes jQuery, HTML5 LocalStorage and some CSS3. This runs completely client-side, but it could easily be integrated into a Node.js
/* You may want to style this to your personal need */
#status.achievement {
display: none;
position: absolute;
left: 50%;
bottom: -120px;
margin-left: -160px;
}
.achievement {
opacity: 1;
-moz-opacity: 1;
-webkit-opacity: 1;
-khtml-opacity: 1;
filter: alpha(opacity=100);
-ms-filter: "alpha(opacity=100)";
border: 1px solid #fff;
background-color: #000;
color: #ddd;
border-radius: 18px 18px 18px 18px;
-moz-border-radius: 18px;
-webkit-border-radius: 18px;
-moz-box-shadow: 0px 0px 8px #cccccc;
-webkit-box-shadow: 0px 0px 8px #cccccc;
-ms-box-shadow: 0px 0px 8px #cccccc;
box-shadow: 0px 0px 8px #cccccc;
background-image: url(data:image/png;base64,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);
background-size: 64px;
background-repeat: no-repeat;
background-position: 6px 6px;
margin-left: 0px;
width: 320px;
height: 64px;
padding-left: 80px;
padding-bottom: 8px;
padding-right: 12px;
padding-top: 8px;
z-index: 30000001;
}
.achievement.locked {
background-color: #ccc;
color: #444;
}
.achievement .title {
color: #fff;
font-weight: bold;
text-shadow: 0px 0px 8px #eee;
}
.achievement.locked .title {
color: #444;
font-weight: bold;
text-shadow: 0px 0px 4px #888;
}
// Achievement "Singleton": Revealing module pattern
Achievements = function()
{
//Private object "array" stores all achievements
var array = {},
_localStorageKey,
initialize = function(localStorageKey)
{
// Saves localStorage key internally
_localStorageKey = localStorageKey;
// Loads achievements from local storage if any
if (window.localStorage)
if ((typeof(window.localStorage[_localStorageKey]) != "undefined") && (window.localStorage[_localStorageKey] != null) && (window.localStorage[_localStorageKey] != "")) array = JSON.parse(window.localStorage[_localStorageKey]);
},
register = function(text, description, icon)
{
if ((typeof(text) !== "string") || (text === "")) return;
array[text] = { active: false };
if (typeof(description) !== "undefined") array[text]["description"] = description;
if (typeof(icon) !== "undefined") array[text]["icon"] = icon;
},
getCount = function()
{
var count = 0;
for (var i in array) count++;
return count;
},
getUnlockedCount = function()
{
var count = 0;
for (var i in array)
{
if (array[i]["active"]) count++;
}
return count;
},
clear = function()
{
// Reset active achievements
for (var i in array)
{
if (array[i]["active"]) array[i]["active"] = false;
}
},
list = function()
{
// Locked achievements will be shown in a grey-ish color
var result = "";
for (var i in array)
{
if (array[i]["active"]) result += '<div class="achievement"><span class="title">' + i + '</span><br /><span class="details">' + array[i]["description"] + '</span></div><br /><br />';
else result += '<div class="achievement locked"><span class="title">' + i + '</span><br /><span class="details">' + array[i]["description"] + '</span></div><br /><br />';
}
return result;
},
show = function(text)
{
if ((typeof(text) !== "string") || (text === "")) return;
// If someone forget to register an achievement
if (array[text] === "undefined") register(text);
if (!array[text]["active"])
{
if ((typeof(array[text].icon) != "undefined") && (array[text].icon != "")) $('#achievement_box').css("background-image", "url(" + array[text].icon + ")");
$('#status.achievement #text').html(text);
$('#status.achievement').show();
$('#status.achievement').css({opacity: 0.0});
$('#status.achievement').animate({opacity: 1.0, bottom: '8px'}, 750);
setTimeout(function()
{
$('#status.achievement').animate({opacity: 0.0, bottom: '-120px'}, 750, "linear", function() { $('#status.achievement').hide(); });
}, 5000);
array[text].active = true;
}
if (window.localStorage) window.localStorage[_localStorageKey] = JSON.stringify(array);
};
return {
initialize: initialize,
getCount: getCount,
getUnlockedCount: getUnlockedCount,
clear: clear,
list: list,
register: register,
show: show
};
}();
<!doctype html>
<head>
<title>Achievement example</title>
<link rel="stylesheet" type="text/css" href="achievements.css" />
<style>
html { overflow: hidden;}
body { font-family: Verdana,Arial,Helvetica; }
#list {
display: none;
background-color: #eee;
height: 240px;
width: 100%;
}
</style>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>
<script src="achievements.js"></script>
<script>
$(document).ready(function()
{
Achievements.initialize('myGame');
Achievements.register('I clicked a button', '...and I liked it');
Achievements.register('Second button', 'This button is just for show.');
});
function toggleList()
{
$('#list').toggle('slow');
$('#list').html(Achievements.list);
}
</script>
</head>
<body>
<div id="content">
<input name="Button1" type="button" value="Click for Achievement One" onclick="Achievements.show('I clicked a button');"><br />
<input name="Button2" type="button" value="Click for Achievement Two" onclick="Achievements.show('Second button');"><br />
<input name="Button3" type="button" value="Clear achievements" onclick="Achievements.clear();"><br />
<br />
<input name="Button4" type="button" value="List achievements" onclick="toggleList();"><br />
<div id="list">
</div>
</div>
<div id="status" class="achievement"><span class="title">Achievement unlocked</span><br/><span id="text">Some text</span></div>
</body>
@timjb
Copy link

timjb commented Apr 19, 2011

Ich hab das mal geforkt und stark verbessert.

@frostney
Copy link
Author

Super :) Bin aber noch nicht ganz fertig damit gewesen ;)

@slobdell
Copy link

You're a good man.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment