Light Racer 2.0 - Days 56-60 - Achievements and Leaderboards

The past several days of work have been focused on two things: Achievements and Leaderboards. We implemented an AchievementsCalculator that was called after every round and each multiplayer match. The achievements themselves are simply stored as a set of SharedPreferences. I decided to go that route because of how much simpler the implementation is than using an SQLLite Provider. I used a Provider to do the stats on Wuzzle and I believe it's simply overkill for storing 15 booleans. The leaderboard was a little more complex. Fortunately I had written most of the code once before when doing the Online Multiplayer code for Wuzzle so it was a matter of copy, paste and modify. The client uses REST/JSON to communicate with the leaderboard server which is Ruby on Rails deployed using Phusion Passenger on Apache.

Day 56 - what was done:

Added fields to player for achievements - collisionType, itemPickupCount, invincibleCount
Created ending sequence

Day 57 - What was done:

Added Check for achievements even on loss
Got domain and web server configured for BPG leaderboard server
Created SVN repository for leaderboard server
Created default project in Rails 2.3.2
Created partial leaderboard screens
Copied over much of the json/leaderboard code from Wuzzle

Day 58 - What was done:

Created the db migrations
Created the scaffolds
Customized the routes and controllers and implemented this_Week, this_month and all_time sql.

Day 59 - What was done:

Implemented score submissions on server
Copied lots of code from Wuzzle
Implemented leaderboard client code
Implemented leaderboard screens
Tied it all together
Deployed on server
Finished Achievements for multiplayer

Day 60 - What was done:

Made leaderboard pretty
Tested leaderboard score submissions
Made achievements pretty


The leaderboard and achievements add a certain level of depth to the game that just makes you want to keep playing it to try to win everything and beat other people. The technology was fairly simple and straight forward to implement. I'd recommend the Ruby on Rails with JSON combo to anyone looking to implement a quick and easy web service for their android app. I have a class I used for both Wuzzle and Light Racer called OnlineGateway that handles all of the calls to the server. I used an asynchronous mechanism, creating a thread for every call that calls back to a handler and delivers a message. If there is an exception, it delivers that, otherwise it delivers the result. This works very well with the exception of activity lifecycle, which is why I wrapped Wuzzle's online gateway in a service. Since Light Racer only submits scores and then retrieves leaderboard scores, I decided it wasn't intense enough to need a service.

I decided to have a running-leaderboard, that is, a leaderboard that isn't always dominated by the long-term high scores. There are several methods to do this, but I chose using time-periods. Light Racer's leaderboards have an all-time, this-month and this-week category. This was easy enough to do. Here's my code in Ruby on Rails to do it:

In the routes:

map.connect 'lightracer/speed_trial_scores/all_time.:format', :controller => 'lightracer_speed_trial_scores', :action => 'all_time'
map.connect 'lightracer/speed_trial_scores/this_week.:format', :controller => 'lightracer_speed_trial_scores', :action => 'this_week'
map.connect 'lightracer/speed_trial_scores/this_month.:format', :controller => 'lightracer_speed_trial_scores', :action => 'this_month'

In the speed trials controller:

# GET /lightracer/speed_trial_scores/this_week.json
def this_week
scores = LightracerSpeedTrialScore.find(:all, :conditions => "YEARWEEK(created_at) = YEARWEEK(CURDATE())", :order => "score DESC, speed DESC", :limit => 20);
respond_to do |format|
format.json { render :json => scores }

# GET /lightracer/speed_trial_scores/this_month.json
def this_month
scores = LightracerSpeedTrialScore.find(:all, :conditions => "YEAR(created_at) = YEAR(CURDATE()) AND MONTH(created_at) = MONTH(CURDATE())", :order => "score DESC, speed DESC", :limit => 20);
respond_to do |format|
format.json { render :json => scores }

# GET /lightracer/speed_trial_scores/all_time.json
def all_time
scores = LightracerSpeedTrialScore.find(:all, :order => "score DESC, speed DESC", :limit => 20);
respond_to do |format|
format.json { render :json => scores }

I made a video to show how things are going. In this video, I have 3 devices playing a multiplayer game. After the match, you can see that one of the phones has achieved something.


Post a comment here or discuss this and other topics in the forums



really great online book - I enjoy reading it. I am watching your videos and your emulator performance level seems to be very high, how is that possible? As I can see, your emulator is very fast and responsive, nothing like AVDs included in Android SDK (or am I wrong?). The screen updating seems to be at the higher frame rate than Android's AVDs and everything seems to be very smooth.

Are you running the emulator on a high-end machine, using some special emulator, or did you tweak the Android AVD?

Best regards
Greetings from Hrvatska

That was on the 1.5 emulator

That was on the 1.5 emulator which runs quite a bit faster than the newer ones. Basically Android emulators got much slower as versions went on.


Thank you very much for answering me.

Best regards

Game looks fantastic

Hey Robert, I want to thank you for sharing your development logs with us. Your posts are very informative and it's a pleasure to watch your game become more complete and polished (I can't wait to try out LR 2). The title screens and menus are excellent now.

It's also very cool to meet a fellow RoR + Android developer (among other things I'm sure). BTW, you could use named_scope for your finds (ie: LightracerSpeedTrialScore.this_week_top(20)) but that's probably just overkill for your purposes. ;-)

Thanks for reading, Jason!

Thanks for reading, Jason! I'm glad to hear that someone is getting something out of this. I'm actually just finishing up LR2 as we speak. I'll be posting about it soon and it should be on the market any time now :)

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.