In this post(or maybe one more) we would integrate scoreoid with libgdx for Android environment. Before we start, check out Scoreoid here. Let me quote from their site “Scoreoid is a non-restrictive, reliable and easy to use server platform for game developers designed to handle but not limited to leaderboards, game achievements, player login, in-game features, built in CRM features and game management including advanced functions such as platform content awareness, for multi-platform games.” . In other words its awesome and it is free.
We used Scoreoid for our last two games TapShootZombie and SpeedboatSanta and we were really happy with the result because its easy to integrate, it uses web services (REST) to handle all the features so it is easy to implement in any platform and no need to include any library with the game, because as we mentioned it uses web based services. And when we thought of publishing SpeedboatSanta for Html5 we just had to write code for html5 and now we can compare android and html5 game scores.
Ok lets start. First goto Scoreoid, add a developer account, create a game after which you will get two strings -> API Key and Game ID which would be needed to get game data. In this post we would be just calling a service to get players best scores but it can be expanded for other features very easily. You can create players/scores though Scoreoid Console to test the code.
In this example we would have a sprite in the game which on clicked would open highscores window. Highscore window would be an android activity with a list view to display all the scores. In the end we will provide the eclipse project which you can download and import it in the workspace to play with it.
We would make a interface -> ScoreInterface with a function called OpenHighScores() which would be implemented differently in Android,HTML5 and would show a list with scores. For now we focus on android, in some of the later tutorials we would cover GWT part. Here is how ScoreInterface.java looks.
public interface ScoreInterface { public void OpenHighScores(); }
You can check older tutorial (Writing Android Specific Code in Libgdx) to see how to implement platform specific code in Libgdx so i won’t go into details here. The mainActivity in Android would implement ScoreInterface and we would pass this object to our ApplicationListener super class (starting point for a libgdx game). Below is a snippet of what i was talking out.
public class MainActivity extends AndroidApplication implements ScoreInterface { Handler handler; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); AndroidApplicationConfiguration cfg = new AndroidApplicationConfiguration(); cfg.useGL20 = false; //To queue new threads in Main UI Threads handler=new Handler(); //Call new ApplicationListener object with this argument as //MainActivity implements ScoreInterface initialize(new ScoreoidExample(this), cfg); } }
So in the game code if we call this function we would be able to see the scores activity in Android (as we will implement the changes in Android) but no change would happen in the desktop application (because we will leave this function empty).
For ScoreActivity we need to do the following :-
- Custom Layout for ListView item having three textviews (Rank, Player Name & Score) which would be inflated when we add scores.
- Create a data adapter to load in the list view to show the scores
- AsyncTask to read scores over the internet using Scoreoid Api.
- Scoreoid Wrapper to make http calls and receive response from Scoreoid Api.
- A JSON Parser to parse the data in the required object (rank, player and score) and update the list view with them.
List View Item :-
We would make an xml layout with three text views to hold Rank, Player Name and Score. Below is the snippet.
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="30dp" > <TextView android:id="@+id/playerPosition" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_alignParentLeft="true" android:paddingLeft="5dp" android:textColor="@color/White" /> <TextView android:id="@+id/playerTitle" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_toRightOf="@+id/playerPosition" android:paddingLeft="5dp" android:textColor="@color/White" /> <TextView android:id="@+id/playerScore" android:layout_width="100dp" android:layout_height="match_parent" android:layout_alignParentRight="true" android:textColor="@color/White" /> </RelativeLayout>
List Adapter :-
Here is the snippet of the custom data adapter.
//Score class to hold scores we retrieve from Scoreoid public class Score { public String PlayerName; public String ScoreValue; public long Position; public Score(String pn,String sv){ Set(pn,sv); } public void Set(String pn,String sv){ PlayerName=pn; ScoreValue=sv; } } //We will attach this adapter with list view to show the scores in the activity class ScoreAdapter extends BaseAdapter{ List<Score> scores; //To inflate the custom list-view-item layout dynamically LayoutInflater inflater; public ScoreAdapter(Context ctx,List<Score> scores){ inflater = (LayoutInflater) ctx .getSystemService(Context.LAYOUT_INFLATER_SERVICE); this.scores=scores; } public int getCount() { // TODO Auto-generated method stub if (scores != null) return scores.size(); return 0; } public Object getItem(int position) { // TODO Auto-generated method stub if (scores != null && position >= 0 && position < getCount()) return scores.get(position); return null; } public long getItemId(int position) { // TODO Auto-generated method stub if (scores != null && position >= 0 && position < getCount()) return position; return 0; } //Here we inflate list item view and update it with score values public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub View view = convertView; Score sc = scores.get(position); if (view == null) { view = inflater .inflate(R.layout.list_score_item, parent, false); } TextView pTitle = (TextView) view.findViewById(R.id.playerTitle); TextView pScore = (TextView) view.findViewById(R.id.playerScore); TextView pPos=(TextView) view.findViewById(R.id.playerPosition); if (pTitle != null) pTitle.setText(sc.PlayerName); if (pScore != null) pScore.setText(sc.ScoreValue); if(pPos!=null) pPos.setText(""+String.valueOf(sc.Position)+"."); return view; } }
In the getview function we inflate the view which we want to show in the row in the list view. Then we update the values with the scores and return the udpated view.
In the activity we would just set an empty data adapter to the list view and when we get the data from scoreoid, we would update the scores array and notify the data adapter. Then the scores would start showing in the view.
AsyncTask to make Http Requests :-
With changes in new Android API (I guess 4.0),we are required to do network intensive tasks in an AsyncTask before it was optional. Anyways it is always good to use AsyncTask when we fetch something from internet. Here is the snippet.
A class to hold constants
public class ScoreoidConstants { public static final String SCOREOID_APIKEY="PLACE YOUR API KEY HERE !!!"; public static final String SCOREOID_GAME_ID="PLACE YOUR GAME ID HERE !!!"; public static final String SCOREOID_BUNDLE_API="api"; public static final String SCOREOID_BUNDLE_SCORESTART="scoreStart"; public static final String SCOREOID_BUNDLE_LIMIT="limit"; public static final String SCOREOID_URL="https://www.scoreoid.com/api/"; public static final String SCOREOID_RESPONSETYPE="json"; public static final String SCOREOID_SCORE_ORDERBY="score"; public static final String SCOREOID_SCORE_ORDER="desc"; public static final String GETBESTSCORES="getBestScores"; }
Class extending AsyncTask to make run in background to fetch scoreoid scores.
public class WebService extends AsyncTask<Bundle, String, String>{ //Player's Rank long startPosition; @Override protected String doInBackground(Bundle... arg0) { // TODO Auto-generated method stub String response=""; if(arg0[0]!=null){ // THIS TO CHECK WHICH API CALL IS MADE. FOR THIS EXAMPLE WE ARE // CALLING ONLY HIGHSCORES // String api=arg0[0].getString(ScoreoidConstants.SCOREOID_BUNDLE_API); long limit=arg0[0].getLong(ScoreoidConstants.SCOREOID_BUNDLE_LIMIT); long start=arg0[0].getLong(ScoreoidConstants.SCOREOID_BUNDLE_SCORESTART); //The scores return would be in descending order by score value response=scoreoidObject.GetBestScores(start, limit); //As the scores are in descending order we would just calculate rank //by incrementing the start values by 1 during the loop startPosition=start+1; } return response; } @Override protected void onPostExecute(String response) { // TODO Auto-generated method stub if(response!=null && response!=""){ List<Score> tempScores= parser.ParseScores(response); if(tempScores!=null && tempScores.size()>0){ scores.clear(); for(Score s:tempScores){ s.Position=startPosition; scores.add(s); startPosition++; } //To notify the list view to show the updated scores adapter.notifyDataSetChanged(); }else{ Toast.makeText(getApplicationContext(), "No scores Available or Some Error Ocurred", Toast.LENGTH_SHORT).show(); } } super.onPostExecute(response); } }
So we call the task with a bundle where we put data like which Scoreoid Api to call, with the different parameters required for the call. For this example we are using only one api , so we just mention the start and limit of number of scores we need from scoreoid.
Then we make the http call to Scoreoid and get the response which is json (you can set that to be xml also). After that we parse the data and update the list which we would do in the next two steps.
Scoreoid Wrapper :-
In this class we would do a http post to retreive the data from scoreoid.
public class ScoreoidWrapper { DefaultHttpClient httpClient; HttpContext localContext; String webServiceUrl; Handler handler; HttpPost httpPost = null; HttpResponse response = null; public ScoreoidWrapper(){ HttpParams myParams = new BasicHttpParams(); HttpConnectionParams.setConnectionTimeout(myParams, 10000); HttpConnectionParams.setSoTimeout(myParams, 10000); httpClient = new DefaultHttpClient(myParams); localContext = new BasicHttpContext(); //Scoreoid API BASE URL webServiceUrl = ScoreoidConstants.SCOREOID_URL; handler=new Handler(); } String webInvoke(String methodName,List<NameValuePair> params){ httpPost = new HttpPost(webServiceUrl + methodName); String data = ""; try { //Passing the parameters httpPost.setEntity(new UrlEncodedFormEntity(params)); //Getting HttpResponse response = httpClient.execute(httpPost); //Reading the content part of the response InputStream is = response.getEntity().getContent(); BufferedInputStream bis = new BufferedInputStream(is); ByteArrayBuffer baf = new ByteArrayBuffer(20); int current = 0; while ((current = bis.read()) != -1) { baf.append((byte) current); } //Convert the reponse to string to parse it later String ret = new String(baf.toByteArray()); // Response from the server data = ret; } catch (Exception e) { // Exception handling } return data; } }
JSON Parser :-
After getting the response (in Json format) we parse the result and fill scores array with player’s first name and score value and return the array list.
public class JSONParser { List<Score> scores; public JSONParser(){ scores=new ArrayList<Score>(); } public List<Score> ParseScores(String response){ scores.clear(); if(response==null) return scores; try { //For parsing the data just look at the content in the Scoreoid Console //and then we can understand the structure of the return data //We receive a json array of player scores JSONArray json=new JSONArray(response); for (int i = 0; i < json.length(); i++) { //To get each player score object from the array JSONObject jsonObject = json.getJSONObject(i); //Inside this object we have two objects of Player and Score //Get Player Object's json string String player=jsonObject.getString("Player"); //Create json object from player String JSONObject playerobj=new JSONObject(player); //Retrieve the player first name String playerFName=playerobj.getString("first_name"); if(playerFName.equals("")) playerFName=playerobj.getString("username"); //Get Score object's json string String scoreStr=jsonObject.getString("Score"); //Create json object from score string JSONObject scoreobj=new JSONObject(scoreStr); //Retrieve score String score=scoreobj.getString("score"); //Create a Score object and add it in the arraylist scores.add(new Score(playerFName, score)); } } catch (JSONException e) { // TODO Auto-generated catch block e.printStackTrace(); } return scores; } }
In PostExecute code of the AsyncTask we get the scores parsed and then loop through them to get the player rank. In the end we update the adapter with the new scores and list view is notified. Here is the snippet of the AsyncTask (same as above in the section where we showed AsyncTask code).
JSONParser parser protected void onPostExecute(String response) { // TODO Auto-generated method stub if(response!=null && response!=""){ List<Score> tempScores= parser.ParseScores(response); if(tempScores!=null && tempScores.size()>0){ scores.clear(); for(Score s:tempScores){ s.Position=startPosition; scores.add(s); startPosition++; } //To notify the list view to show the updated scores adapter.notifyDataSetChanged(); }else{ Toast.makeText(getApplicationContext(), "No scores Available or Some Error Ocurred", Toast.LENGTH_SHORT).show(); } } super.onPostExecute(response); }
Source Code:
Finally the source code with all the snippets mentioned above which can be imported in eclipse workspace. Download from Box.net
Note :- Input your Scoreoid API Key and Game ID in ScoreoidConstants.java file for the code to show some data.
When you run the example click on the trophy icon at the centre to see the scores.
If you have any query/suggestion/feeback please put it in the comments section.
Thanks
Hello good sir,
Will there be another tutorial for desktop versions? Would be quite awesome! Will now see if i can get this cutie integrated to my game with this tutorial, thanks so much for writing it!
Hi,
Glad to know this is helpful. Actually we only tried integrating scoreoid in android and gwt(html5). So if you wish we can post a tutorial for integrating it in gwt.
Thanks
Hi Rahul, thanks for answering! Now I can say: yes, very helpful. Got it to work with posting scores as well in just a few hours without any prior knowledge, awesome! I do not know which desktop frontend I will use for my game up to now so any tutorial would be helpful!
Thanks again,
Daranus
Hi Daranus,
We posted a tutorial on integrating Scoreoid in libgdx/desktop. Check it out. Hopefully it helps.
Thanks,
Rahul