Method Overriding in C# and Java

Recently I have been trying to learn C#. As a long-time Java developer, there have been a few surprises. C# and Java have a lot in common, so it’s hard for me to notice some of the subtle differences. Today, for instance, I found out that method overriding in C# takes extra effort. In Java, you define methods and then override them in subclasses by simply declaring a method with the same name and arguments. That is not the case in C#, but it took me a while to figure out why the Java code I was converting to C# was not working correctly.

With C#, you have to add the “virtual” keyword to any method you intend to override. In subclasses, you have to add the “override” keyword. This is illustrated below for a “makeMove” method for the game of Gomoku.

public class GomokuPlayer
 ...

public virtual Move makeMove (State gstate, int whichPlayer) {
    ...
    return someMove;
 }
 ...
}
public class FastPlayer : GomokuPlayer {
 ...
 public override Move makeMove (State gstate, int whichPlayer) {
    ...
    return myMove;
 }
 ...
}

C# virtual and override are well explained here: http://www.codeproject.com/Articles/18734/Method-Overriding-in-C

Posted in Java | Tagged , | 1 Comment

Atari: Game Over

I just watched “Atari: Game Over”. It is a fun documentary about Atari, the old video game company from the 80’s. Interesting  to hear from some of the designers of the early video games. See http://www.imdb.com/title/tt3715406/.

It is available on Showtime, some Microsoft site, and probably a few other places.

Posted in Game Design | Leave a comment

Tutorial: How to Implement In-app Billing in Android – Part 2

Part 1 of this article was my guide on how to get started with in-app purchases in Android. It was published in July 2014. Finally, here is part 2.

Part 1 covered the operational side of things: defining in-app products, application keys, etc. In this article, I will offer some suggestions on how to adapt the TrivialDrive code for your own app. The code provided with TrivialDrive is very well structured and we all can learn a lot from it. Most of the hard work is done in classes inside the util package of TrivialDrive. Without that package, the MainActivity would be much more difficult. The changes I suggest below are refinements on what is in the MainActivity provided by the Google team.

For my own work, I made the transition from the in-app purchases of the TrivialDrive sample app (Figure 1, left) to the purchases of my space war app (Figure 2, right).

trivial_drive_01

Screenshot_2015-05-27-06-13-52

Adapting the Code for Your Own App

In this section, I will describe how I have adapted the code to an app I am working on. I will use the TrivialDrive app code to illustrate what I did, rather than using code from my space war app. The advantage of that is that I can provide the source code of a completely working app for you to try out and work with. If you want to have that code installed while you work, download the source code from here: zip file of finished sample for part 2.

Assuming you have gone through Part 1, you have a working copy of the TrivialDrive app. Make a copy of the whole app and make sure the copy compiles cleanly. Check the project preferences and be sure you are using a new version of Android (5 or higher).

The sample code that I provide starts with a package name of “com.wglxy.example.trivialdrive2”. Rename the project package. You need to do this because every app that gets published must have a unique package name. Then go into the AndroidManifest.xml file and change the package there too. You will also have to fix the line in MainActivity that imports the R class. Change that line so it gets R from your new package. (Note: the screenshots I include below are from Eclipse. I have not yet switched over to Android Studio.)

rename-package

I restructured the code in anticipation of building an app that might have more than one activity that deals with in-app products. I added several extra classes. One is a Constants class and another is a superclass (IabActivity) where I put code needed by all of the activities that do in-app billing transactions.

Constants are in a Constants.java file. The values are the id strings that you define in the Google Play Developer Console, in the section for in-app products. The values for this demo app are shown below. For your own app, you would add constants for your own set of products.

// In-app billing constants

// SKUs for our products: the premium upgrade (non-consumable)
// and gas (consumable)
public static final String SKU_PREMIUM = "premium2";
public static final String SKU_GAS = "gas2";

// SKU for our subscription (infinite gas)
public static final String SKU_INFINITE_GAS = "infinite_gas2";

Class IabActivity is a new class I added. I added it so I would have one place for the new code I added to support in-app billing. Because IAB code makes requests asynchronously, it is fairly complicated. If your app has only a single activity or you have only one activity where all in-app purchases are made, you won’t necessarily need a superclass.  I chose to add a subclass to reduce the amount of duplicate code in the subclasses. (That’s what I meant when I said earlier that my changes are a refinement of the original code.)

IabActivity bridges the gap between the generic, reusable code provided in the trivialdrive2.util package. IabActivity holds constants, variables, and methods that are specific to the application.

Let’s have a look at the different sections of the IabActivity class.

Variables Section of IabActivity

// Does the user have the premium upgrade?
 protected boolean mIsPremium = false;

// Does the user have an active subscription to the 
// infinite gas plan?
 protected boolean mSubscribedToInfiniteGas = false;

These variables indicate which items have been purchased. In TrivialDrive, you can upgrade your car to use premium gas. Another one of the purchases you can make is a subscription for unlimited gas. The regular gas that you purchase is a consumable item. That means that its purchase affects the state of the app as soon as the purchase  goes through. The state variables related to regular gas are in MainActivity.

Listeners section of IabActivity

A lot of work gets done inside the IabHelper class in the util package of TrivialDrive. For that to work, we need some Listener objects.

/**
 * mGotInventoryListener - Listener that's called when we finish 
 * querying the items and subscriptions we own.
 */
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = 
   new IabHelper.QueryInventoryFinishedListener() {
      public void onQueryInventoryFinished
                   (IabResult result, Inventory inventory) {
         ...
      }
}
/**
 * mConsumeFinishedListener - Listener that's called when 
 * consumption of the purchase item finishes
 */
IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = 
   new IabHelper.OnConsumeFinishedListener() {
      public void onConsumeFinished
                     (Purchase purchase, IabResult result) {
         ...
      }
  }
/**
 * mPurchaseFinishedListener - Listener that's called when a 
 * purchase is finished
 */
IabHelper.OnIabPurchaseFinishedListener 
   mPurchaseFinishedListener = 
      new IabHelper.OnIabPurchaseFinishedListener() {
         public void onIabPurchaseFinished 
                       (IabResult result, Purchase purchase) {
            ...
          }
}

Most of the work associated with an in-app purchase is done by code in the util package. Asynchronous calls are made there. The listeners defined in the Listeners section define the callback methods for those calls. I will say more about these listeners below.

IabHelper variable in IabActivity

 /**
  * This variable holds the value of an IabHelper object.
  */
private IabHelper pIabHelper;

The most important class in the TrivialDrive util package is IabHelper. As stated in its comment section, IabHelper “provides convenience methods for inapp billing. You can create one instance of this class for your application and use it to process inapp billing operations”. You will see how that is done below.

Methods section of IabActivity

There are quite a few methods in the IabActivity superclass. Some of them are there to support the activity lifecycle and to make in-app purchases. Others are just there for convenience. Subclasses can make use of them and the Listener methods use them too.

// Methods for the Activity lifecycle
@Override protected void onActivityResult 
             (int requestCode, int resultCode, Intent data);
@Override protected void onCreate(Bundle savedInstanceState);
@Override public void onDestroy ();

// Methods for in-app purchases
protected void launchInAppPurchaseFlow 
                 (Activity a, String sku);
protected void launchSubscriptionPurchaseFlow 
                 (Activity a, String sku);
protected void launchInAppPurchaseFlow 
                 (Activity a, String sku, String itemType);
void onIabPurchaseFailed (IabHelper h, int errorNum);
boolean verifyDeveloperPayload(Purchase p);
protected void setupIabHelper 
                (final boolean showListedSkus, 
                 final boolean showErrors);
// Methods to help with setup and consuming items. 
void onIabConsumeItemFailed (IabHelper h);
void onIabConsumeItemSucceeded 
       (IabHelper h, Purchase purchase, IabResult result);
void onIabSetupFailed (IabHelper h);
void onIabSetupSucceeded 
       (IabHelper h, IabResult result, Inventory inventory);
// Miscellaneous methods
protected void alert(String message);
protected void complain(String message);
public void toast (int stringId);
public void toast (String msg);

Rather than explain all of these on their own, let’s take a look at the MainActivity class and see how these superclass methods are used.

Handling Purchases in MainActivity

In the TrivialDrive sample app, there is a single class, MainActivity, which uses in-app billing code. It can serve as a model for how purchases can be done in an app.

A purchase in TrvialDrive starts when the user touches one of the purchase buttons. The three buttons are “Buy Gas”, “Upgrade My Car”, and “Get Infinite Gas”. They are shown in the figure below. For those buttons to work, there is a fair amount of set up work that has to be done. There is code to handle the button clicks and to react to the asynchronous calls, which are made by the IabHelper class in the util package and by the IabActivity class.

trivial_drive_01

When the user touches a button, like the “Buy Gas” button, a call is made to a method that is defined in class IabActivity.

launchInAppPurchaseFlow (this, SKU_GAS);

The value of the util package, the IabHelper class, and the IabActivity class is obvious. Look how simple the code is. That’s an indication of a good design. To understand how it works, we will look at the method and at the set up needed for it to work.

Let’s start with the onCreate method of the MainActivity. Method onCreate is a good place to get everything set up and check to see what purchases have already been made.

@Override public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);

 // Start setup of in-app billing.
 // (Note that the work is done using methods in superclass
 // IabActivity. The original code had all the code here.)
 setupIabHelper (true, true);

 // Set a variable for convenient access
 // to the iab helper object.
 mHelper = getIabHelper ();

 // load game data
 loadData();
 updateUi ();

 // enable debug logging
 // (For a production application, you would set this to false).
 mHelper.enableDebugLogging(true);
}

The onCreate method looks pretty simple, but that is because most of the work is being done inside the setupIabHelper method of IabActivity. If you look inside IabActivity’s setupIabHelper method (next figure below), you will see that it creates an IabHelper object and then uses it to set up connections to a service that is running to handle in-app billing. When set up completes, note how the listener builds up a list of products to check on.

protected void setupIabHelper (final boolean showListedSkus, 
                               final boolean showErrors) {

setShowIabErrors (showErrors);
try {

 String base64EncodedPublicKey 
         = "--- YOUR APPLICATION KEY GOES HERE ---";

 // Create the helper, passing it our context
 // and the public key to verify signatures with
 IabHelper ih = new IabHelper(this, base64EncodedPublicKey);
 setIabHelper (ih); // sets pIabHelper

 // Start setup. This is asynchronous.
 ih.startSetup(new IabHelper.OnIabSetupFinishedListener() {
 public void onIabSetupFinished(IabResult result) {
  if (!result.isSuccess()) {
   if (pShowIabErrors)
    complain ("Problem setting up in-app billing: " + result);
   onIabSetupFailed (getIabHelper ());
   return;
  }

  // IAB is fully set up. Get an inventory of stuff we own.
  // Build up a list of the SKUs so other parts of
  // of the app can get information on prices.
  ArrayList<String> skusToBeListed = null;
  if (showListedSkus) {
   skusToBeListed = new ArrayList<String> ();
   skusToBeListed.add (SKU_PREMIUM);
   skusToBeListed.add (SKU_GAS);
   skusToBeListed.add (SKU_INFINITE_GAS);
  }
  pIabHelper.queryInventoryAsync 
              (true, skusToBeListed, mGotInventoryListener);
  }
 });

} catch (Throwable ex) {

 ...

}

Notice the asynchronous query for information at the end. That method expects a listener for the last argument. We have one already set up in mGotInventoryListener. If you read the code for that listener, you will see that basically all it is doing is checking the result and calling either onIabSetupSucceeded or onIabSetupFailed. It is in method onIabSetupSucceeded (in IabActivity) where the checks are made.

The implementation of onIabSucceeded in IabActivity is shown below. This is the code in the superclass, so all subclasses benefit from the standard handling of set up. See how it checks on each of the three products in the app.  It takes immediate action for premium and infinite gas. Those two products are not consumable, which means users only have to purchase them one time. Once purchased, the user has them forever. For that to work, the code in onCreate has to check to if they have already been purchased. For those two products, it sets two global variables.

From class IabActivity:

void onIabSetupSucceeded (IabHelper h, 
                          IabResult result, Inventory inventory) {
 //
 // Check for the in-app purchases of items.
 // (Do this here in the superclass so we write it only once.
 // Note that several global variables are set here.)
 //

 // Do we have the premium upgrade?
 Purchase premiumPurchase = inventory.getPurchase(SKU_PREMIUM);
 mIsPremium = (premiumPurchase != null 
               && verifyDeveloperPayload(premiumPurchase));

 // Do we have the infinite gas plan?
 Purchase infiniteGasPurchase = 
             inventory.getPurchase(SKU_INFINITE_GAS);
 mSubscribedToInfiniteGas = (infiniteGasPurchase != null &&
     verifyDeveloperPayload(infiniteGasPurchase));

 // Check for gas delivery
 // -- if we own gas, we should fill up the tank immediately
 Purchase gasPurchase = inventory.getPurchase(SKU_GAS);
 if (gasPurchase != null 
     && verifyDeveloperPayload(gasPurchase)) {
    pIabHelper.consumeAsync (gasPurchase, 
                             mConsumeFinishedListener);
    return;
 }

}

The third item, regular gas, is consumable. That means it can be used up and the user might have to purchase it multiple times, over the course of the game or sessions that a user has with the app. Since the gas item is consumable, the onCreate code initiates the operation to consume the item. That’s another asynchronous call that goes through the IabHelper object.

It’s not shown here, but if you check the definition of mConsumeFinishedListener, you will see how a successful call results in a callback to the onIabConsumeItemSucceeded method. I did it that way so the MainActivity, which is a subclass of IabActivity, could do its own special handling for consuming that item. That is shown below.

In MainActivity:

/** 
 * Called when consumption of a purchase item succeeds.
 *
 * SKU_GAS is the only consumable item. 
 * So this is the place where the tank is filled.
 *
 */

void onIabConsumeItemSucceeded 
       (IabHelper h, Purchase purchase, IabResult result) {
 super.onIabConsumeItemSucceeded (h, purchase, result);

 // Update the state of the app and the ui
 // to show the item we purchased and consumed.
 String purchaseSku = purchase.getSku (); 
 if (purchaseSku.equals(SKU_GAS)) {
 if (result.isSuccess()) {
    // successfully consumed, so we apply the effects 
    // of the item in our game world's logic.
    // That means filling the gas tank a bit.
    mTank = mTank == TANK_MAX ? TANK_MAX : mTank + 1;
    saveData();
    alert ("You filled 1/4 tank. Your tank is now "
           + String.valueOf(mTank) + "/4 full!");
 } else {
   complain("Error while consuming regular gas: " + result);
 }

 }

 updateUi();
 setWaitScreen(false);
}

The tank is filled up some and then the user interface is updated.

(I have to admit that as I write this explanation now that I am not completely sure that the way I have broken this code down is ideal. In my real app, it worked out pretty well to have the extra methods (like onIabConsumeItemSucceeded). Anyway, it’s always a good thing to reflect on the code you build and be open to restructuring it. What I was after was more common code in a superclass. That’s a good thing if it saves you work in your subclasses. So, that is something for you to think about in your own app.)

Initiating Purchases in MainActivity

Let’s go back to the point where a purchase starts and examine it in more detail.  A purchase begins when the user touches one of the purchase buttons.

trivial_drive_01

For the “Buy Gas” the following code in MainActivity runs:

public void onBuyGasButtonClicked(View arg0) {
 if (mTank >= TANK_MAX) {
 complain("Your tank is full. Drive around a bit!");
 return;
 }

 // Set up to show and wait screen.
 // Launch the gas purchase UI flow.
 setWaitScreen(true);
 launchInAppPurchaseFlow (this, SKU_GAS);

}

It changes the user interface to show the wait screen, and then it launches the in-app purchase flow, using method launchInAppPurchaseFlow in superclass IabActivity.

protected void launchInAppPurchaseFlow (Activity a, String sku) {
 launchInAppPurchaseFlow (a, sku, 
                          IabHelper.ITEM_TYPE_INAPP);
}

protected void launchInAppPurchaseFlow 
                 (Activity a, String sku, String itemType) {
 IabHelper h = getIabHelper ();
 if (h != null) {
    h.launchPurchaseFlow (a, sku, itemType, RC_PURCHASE_REQUEST,
                          mPurchaseFinishedListener, "");
 }
}

Notice that the real work gets done in IabHelper, the class defined in the util package. Basically, it is setting up an Intent so an activity can be started to do the work. The return code RC_PURCHASE_REQUEST is defined in IabActivity, as is the onActivityResult method. The onActivityResult method in IabActivity runs when the second activity completes. As you see below, it lets the IabHelper do the work.

@Override protected void onActivityResult 
                           (int requestCode, 
                            int resultCode, Intent data) {
 switch (requestCode) {

 case RC_PURCHASE_REQUEST :
 //
 // Handle the purchase request here.
 // 
 if (pIabHelper != null) {
  //
  // The interesting work is done in IabHelper class.
  // See its handleActivityResult method.
  //
  if (!pIabHelper.handleActivityResult 
                   (requestCode, resultCode, data)) {
   // Not handled by the helper, so let other code do it.
   super.onActivityResult (requestCode, resultCode, data);
  }
 }
 break;

 ...
 }
}

So much goes on in IabHelper. That’s a good thing for you me. We are left with a small number of places in our own activities where we have to react to in-app purchase requests. Also note that onActivityResult is defined in IabActivity rather than MainActivity. In an app with more than one activity doing in-app purchase calls, that works out well. There only has to be the one definition because the code does not change. MainActivity, and any future activities, are a little easier to implement and understand.

If you work through the rest of the code (below), you will see how two more of the handlers defined in IabActivity initiate more asynchronous calls and then make callbacks to some success and failure methods that are easily implemented in MainActivity. The two methods are onConsumeItemSucceeded and onConsumeItemFailed.

IabHelper.OnIabPurchaseFinishedListener
 mPurchaseFinishedListener 
   = new IabHelper.OnIabPurchaseFinishedListener() {
 public void onIabPurchaseFinished 
              (IabResult result, Purchase purchase) {
   ...
 
   if (purchase.getSku().equals (SKU_PREMIUM)) {
     pIabHelper.consumeAsync (purchase, mConsumeFinishedListener);
   } else if (purchase.getSku().equals (SKU_GAS)) {
     pIabHelper.consumeAsync (purchase, mConsumeFinishedListener);
   } else if (purchase.getSku().equals (SKU_INFINITE_GAS)) {
     pIabHelper.consumeAsync (purchase, mConsumeFinishedListener);
 }
 }
};

IabHelper.OnConsumeFinishedListener
 mConsumeFinishedListener 
   = new IabHelper.OnConsumeFinishedListener() {
 public void onConsumeFinished
  (Purchase purchase, IabResult result) {
 if (result.isSuccess ()) {
  synchronized (pIabHelper) {
   onIabConsumeItemSucceeded (pIabHelper, purchase, result);
  } 
 } else {
  complain ("Did not handle purchase: " 
            + purchase + " result: " + result);
  onIabConsumeItemFailed (pIabHelper);
 }
 }
};

The other two buttons are not much different than what I have shown for the “Buy Gas” button. Work through those, too, so you will have a complete understanding of consumable and non-consumable in-app products.

Additional Changes Needed for Your Own App

I have already talked about your own products sku strings. Once you have defined those in the Android Developer Console, you would update those in the Constants class.

In method setupIabHelper of IabActivity, there is a line you have to change so the code knows the application key for your app. Be sure to do that. Without it, your app will not be able to tie your product sku strings with the information you entered in the Android Developer Console.

protected void setupIabHelper
 (final boolean showListedSkus, final boolean showErrors) {

 ...
 String base64EncodedPublicKey = "YOUR APPLICATION KEY GOES HERE";
 ...

}

The image below (from Part 1 of this article) shows where to look for the public key. It is the very long string that is blurred out and highlighted with the orange text.

dev-console-03

There is a method in the TrivialDrive sample code named “verifyDeveloperPayload”. It is there to ensure that all purchase attempts are valid. I have not implemented this method for my app, but at some point, I should, and you should too. Very good guidance has been provided by the author of the sample app, Bruno Oliveira.

boolean verifyDeveloperPayload(Purchase p) {

String payload = p.getDeveloperPayload();

/*
 * TODO: verify that the developer payload of the purchase 
 * is correct. It will be
 * the same one that you sent when initiating the purchase.
 *
 * WARNING: Locally generating a random string when 
 * starting a purchase and
 * verifying it here might seem like a good approach, 
 * but this will fail in the
 * case where the user purchases an item on one device 
 * and then uses your app on
 * a different device, because on the other device 
 * you will not have access to the
 * random string you originally generated.
 *
 * So a good developer payload has these characteristics:
 *
 * 1. If two different users purchase an item, 
 * the payload is different between them,
 * so that one user's purchase can't be replayed to another user.
 * 2. The payload must be such that you can verify it even 
 * when the app wasn't the
 * one who initiated the purchase flow 
 * (so that items purchased by the user on
 * one device work on other devices owned by the user).
 *
 * Using your own server to store and verify developer 
 * payloads across app installations is recommended.
 */

return true;
}

Source Code

The source code for the modified TrivialDrive app can be found at the wglxy.com website. Click here: zip file of finished sample for part 2.

Acknowledgements

The sample TrivialDrive app provided by Google is very well done. There is so much to learn from it. Thanks go to Bruno Oliveira from Google.

References

How To Implement In-App Billing in Android – Part 1 – the first part of my blog article. Be sure to read it for details about setting up you in-app products, packaging your app, and release your app.

Preparing Your In-App Billing Application – the primary reference with the TrivialDrive example from the Google team.

Android Fragment for an Item in a Store – A sample app that shows how to build a store listing for products. Also see the follow-up article on store items.

Double Star app – This is my space war app. It includes in-app products. If you’d to see how all the pieces for in-app billing came together, please try the game.

Posted in Android | Tagged , , , , | 2 Comments

Update – Java Version of Super Star Trek

I have updated the Java code for my port of the old Super Star Trek game. I first wrote about this about a year ago. See “Java Version of Super Star Trek“.

This update has many bug fixes and enhancements, including support for a deathray, a special kind of beacon planet, an alien home world, and firing phasers and torpedos at up to three targets at once. The new code is available on the SST2k page on Wglxy.com.

Moving in Super Star Trek

More News about Super Star Trek

Android App. I have built an Android app based on this Java code. The app is more than just destroying alien ships, which is the main objective of Super Star Trek. The app has a storyline about searching the galaxy for the aliens’ home world. If you would like to try that game, more information is available here: Beta Test for Double Star.

SST2k website. The website where I originally found the C code for Super Star Trek is no longer around. I did locate the original SST2k manual in one of the places that caches old web pages. See SST2k game instructions.

Review on Space Game Junkie. The old game was recently reviewed on the Space Game Junkie website. That website is a fun place to visit if you like space games. Brian Rubin did a four part series on Super Star Trek. The link to the first of the series is here: Super Star Trek (entry 1). If you like the game, you will undoubtedly enjoy Brian’s entertaining attempt to learn the game. If you visit his Google+ page, you will find a post from me about movement, where I try to explain why his encounters with blackholes are not as unpredictable as he thought.

Posted in Android, Java | Tagged , , , , | Leave a comment

Beta Test for the Double Star Game

Beta test continues for the Double Star app for Android.

Double Star is a turn-based, single player, space war game. In the game, you command a powerful starship. You must first save our planet from the invasion. Then you search the galaxy for the home world of the invaders so you can destroy them once and for all.

Double Star is available now for Android phones and tablets. To install the Double Star game, do one of the following:

Instructions on how to install the app from the Google Play store are in the group and in the community.

double_star_play_red

For a longer version of this announcement, visit the Double Star page on wglxy.com.

 

Posted in Android | Tagged , , , , | 2 Comments

A game to try: Alien Star Menace

Don’t know if you have time to try another game, but here’s one from one of the people in an Indie Games Meetup group I am in. It’s called “Alien Star Menace” and it is available on both Android and iOS. It was just released in the last two weeks. It’s done in Unity.

alien_star_menace_screenshot

Alien Star Menace

I like it because it is a turn-based strategy game, and it is set in space. I also like the “Hero Academy” style of play. I am not very far along, just a few levels in. It’s amusing and challenging at the same time.

For more information, see http://spacehorrorgame.tumblr.com/.

Posted in Android | Tagged , , , , | Leave a comment

The Thirty Second Hook and Double Star

I recently read “The 30-Second Hook” on Gamasutra. It’s a very good article about the experience of the developers of Blowfish Meets Meteor. The author talks about the experience dealing with the problem of making your game appealing to players trying your game.  You have to hook players quickly or, in many cases, you lose them. He writes, “people played only the rather simplistic, tutorial-based first level and put it down because they assumed this was a reflection of the entire game.”

It seems that this might apply to my Double Star Android game. Twenty levels of play are provided in the game, with mysteries, challenges, and rewards along the way. However, it all starts with a training level. If training takes too long, players might never get to the more interesting parts of the game. So that got me thinking that maybe I have to throw players in the deep end. Wait until they flounder and then offer them further assistance. The latest release does just that.

Short / Long Training in Double Star

The latest beta version of Double Star (version 0.980) has a new setting where the player selects either short or long training. Short is the default setting. Players receive minimal training, which includes only the basics of issuing commands and checking ship status. That gets players into battle sooner, but it also means that players are responsible for learning most of the game on their own. To help with that, an extensive list of tutorials is available on the Help screen.

Long training is much more complete. For long training, players learn how to play as they progress through five ranks, from Weapons Officer to Captain. By the time they reach Captain, players know about all of the equipment and have had several longer training missions to practice.

ds-admiral-advice

Training is the first of three phases of game play in Double Star: (1) training at the academy; (2) saving our planet from the alien invasion; (3) searching the galaxy for the alien home world. Throughout the game, you receive suggestions and hints from Star Fleet and your crew, as shown in the figure above.

I hope that a few readers will try the game and let me know what you think about the new training level.

Posted in Android, Game Design | Tagged , , , | Leave a comment