Improved Double Star

If you are having trouble finishing the really difficult levels (5, 9, 11), the new version of Double Star should help. Changes:
(1) your ship starts out with 10% more energy and stronger shields;
(2) your ship starts out with 12 photon torpedoes;
(3) the time to complete a mission is longer;
(4) a problem with the enemy tractor beams was fixed.

photons_popup

Get version 1.1.1 from the Google Play Store. See https://play.google.com/store/apps/details?id=com.wglxy.starship

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

More on the Thirty Second Hook

With so many games out there, you have to hook players right away. Double Star has a setting that let’s you choose between short and long training. My hope was that the short training would get players into the real game faster. Any thoughts? Did it work for you?

More on the “thirty second hook” here: The Thirty Second Hook and Double Star.

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

Double Star game for Android

Many of you know that I have been working on an Android game. Well, it took me almost 4 years, but it’s finally done. Yay!

The game is called Double Star. It is a space war game — you against the aliens. The game is turn-based, which means you get a turn, then the aliens get a turn. It’s not a game where your reaction time matters. Here’s a little preview video:

Double Star is available in the Google Play store, if you would like to try it. Here is the link:google-play-badge-bordered-half-size
It’s free so don’t worry about money … just worry about those pesky aliens.

 

Posted in Android, Double Star | Tagged , , , , , | 3 Comments

New Code for Super Star Trek

There is new code for the Java version of Super Star Trek. The latest changes include improvements in the way events are handled.

Moving in Super Star Trek

See the Wglxy.com download page: http://www.wglxy.com/java-port-of-super-star-trek.

Related Articles

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

Screenshots from Latest Double Star Beta Release

Here are a few screenshots from the latest beta release of my Double Star game for Android. Double Star is a turn-based space war game in which you must first stop the alien invasion and then search the stars to find their home world and destroy it.

ds-story-save-our-worldds-message-from-admiralds-play-01ds-message-after-dockingds-constellation-02ds-story-find-the-alien-worldds-defeat ds-leaderboards-and-saved-games

Play Double Star

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

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

Fix to Double Star, Level 6

The latest update of Double Star fixes Level 6. For fun, for those of you who already finished that level, you might want to go back and try again. You can get to some of the alien starships, but not all of them. So you have that to figure out. And watch out if the tractor beam gets you and pulls you into the the cluster of alien ships. Certain death awaits.

level6_snapshot

Posted in Android, Double Star | Leave a comment

Android Example of a Zoomable Game Board

In this article, I briefly describe how I built a simple game board in Android. The sample app shows a grid of squares that represents a game board. The squares on the game board come from an array of image tiles. The grid can be zoomed in or out and moved. Touch events are triggered when squares are touched. Regular and long press are handled. With this sample as a starting point, it should be easy to build a game that uses a board of squares.

The video below shows the demo app in action.

Source Code

Full source code for this game board demo is available at the Wglxy.com download page.

Classes in the Demo Application

There are four classes that do most of the work in this sample application.

PanZoomView
This view supports both zooming and panning and other touch gestures and actions. What gets shown on the screen depends on the onDraw method provided by its subclasses. In this demo app, the subclass is GameBoardView.

GameBoardView
This view displays a grid of rectangles that can be zoomed in and out and panned. The default grid is 13 x 13, with 8 squares showing. The zoom point is the center of the view.

GameBoardTouchListener
Defines the listener interface for touch actions on a GameBoardView. It defines onTouchDown, onTouchUp, and onLongTouchUp. For onTouchUp and onLongTouchUp, the listener receives index numbers of the squares of the grid. No coordinates are included in the call to onTouchDown.

GameBoardActivity
This is the class that brings together all the other classes into a working demo. It generates a random grid of of squares. The grid represents a simple game board. Each of the squares shows a white, red, or blue tile. This activity connects a GameBoardView to the game board grid so it can be displayed on the screen. This activity also defines actions for touch actions. When a square is touched, it is highlighted to show that it is selected. When a long touch is done, the square changes to the next color in the sequence (white, red, blue).

How It Works

GameBoardActivity has a simple layout. A real game would have much more, but all I wanted to show was how easily a GameBoardView could be used to fill up the screen with a grid of squares, even if there were more squares than fit on the screen. The layout says to fill the screen width with a game board and to fill whatever is left of the height after the simple text instructions are displayed.

<LinearLayout 
 android:layout_width="fill_parent"
 android:layout_height="fill_parent"
 android:orientation="vertical" >

<com.wglxy.example.gameboard.GameBoardView
 android:id="@+id/boardview"
 android:layout_width="fill_parent"
 android:layout_height="0dp"
 android:layout_weight="1"
 android:background="@color/grid_background"
 />

<TextView
 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
 android:text="@string/instructions" />

</LinearLayout>

The connection to the GameBoardView is made in onCreate. The activity provides the initial grid values to the view and attaches itself as the listener for touch events.

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

 setupMyGrid (NumSquaresOnGridSide);
 
 GameBoardView gv = (GameBoardView) findViewById (R.id.boardview);
 if (gv != null) {
  setGridView (gv);

  gv.setNumSquaresAlongCanvas (NumSquaresOnGridSide);
  gv.setNumSquaresAlongSide (NumSquaresOnViewSide);
  gv.updateGrid (getGrid ());
  gv.setTouchListener (this);
 }
 
}

The grid is generated randomly with values between 0 and 2. Those values are used by GameBoardView to look up images from its array of three images.

The touch listener methods in GameBoardActivity are there to illustrate how to select squares and take actions on the game board. The onTouchUp method shows how a square is selected. It does not update the grid value because selection state can be maintained in the GameBoardView. In a real game, other actions, such as button presses, might want to know which square is selected.

public void onTouchUp (int downX, int downY, int upX, int upY) {
 GameBoardView gv = getGridView ();
 if (gv == null) return;

 boolean isSelected = gv.isSelected (upX, upY);
 gv.clearSelections ();
 if (!isSelected) gv.toggleSelection (upX, upY);
 gv.invalidate ();

}

The long press handling is a bit different. It results in the value on the grid being changed. After a change to the board, the view is informed that it needs to redraw itself. This happens in this demo in response to a long press, but in a real game, it’s more likely going to be that some computations have modified the grid contents and it’s time to update the display.

public void onLongTouchUp (int downX, int downY, int upX, int upY) {
 GameBoardView gv = getGridView ();
 if (gv == null) return;

 int oldValue = gv.gridValue (upX, upY);
 int newValue = oldValue + 1;
 if (newValue >= NumRedBlueTypes) newValue = 0;
 gv.setGridValue (upX, upY, newValue);
 gv.invalidate ();

}

Next we will examine how a GameBoardView works. Its two main responsibilities are: (1) drawing the images squares onto the screen as an x-y grid; (2) handling touch events and converting the view coordinates of the touch points into the index values between 1 and the number of squares along the side of the grid.

The most complicated thing the view has to do is be able to draw all the squares of the grid on the screen, even if they do not all fit in the height and width allocated for the view. It writes onto a canvas that is just large enough to hold all the squares on the grid. The figure below depicts roughly what is going on.

canvas-and-view

The size of the canvas object is determined by the size of a square. The square size is determined by how many squares fit along the shortest edge of the view. In this demo program, the activity requests for there to be 13 squares along the edge of the grid and for there to be 8 squares visible in the view. So the canvas size is roughly 1.6 times the view size. (13 / 8 = 1.625).

The view supports panning and zooming, but most of the work for that is done in class PanZoomView. Think of it this way. Panning means the green canvas box moves around, left and right, and up and down. The user sees what is in the gray zone. Zooming means that the green canvas box gets larger and smaller, revealing different parts of the canvas in the gray box.

The images that are drawn on the screen are defined in an array in GameBoardView. There is one set of images for the regular images and another set for when a square is selected. As you read through the code, you will see the place in getBitmapsArray where the bitmap objects are set up from the resource ids. Doing this once speeds up the drawing operation.

static private final int [] mUnselectedImageIds
 = {R.drawable.no_marker_default,
    R.drawable.red_marker_default,
    R.drawable.blue_marker_default};
static private final int [] mSelectedImageIds
 = {R.drawable.no_marker_highlighted,
    R.drawable.red_marker_highlighted,
    R.drawable.blue_marker_highlighted};
private Bitmap [] mBitmaps;
private Bitmap [] mSelectedBitmaps;

Drawing is done in the onDrawPz method and drawOnCanvas methods. The onDrawPz method is what the superclass (PanZoomView) put in place for subclasses to draw their contents.  There is a lot going on in that method, but basically all it is doing is (1) figuring out the square size from the view size; (2) figuring out how large the canvas is and where its origin point; (3) looping through the grid values and displaying the right image in each square. Parts 1-2 are done in onDrawPz. It looks a bit like this:

@Override public void onDrawPz(Canvas canvas) {
 
 canvas.save();

 ... setting a lot of variables ...
 
 // The canvas is translated by the amount we have
 // scrolled and the standard amount to move the origin
 // of the canvas up and left so the region is centered
 // in the view. (Note: mPosX/mPosY are defined in PanZoomView.)
 float x = 0, y = 0;
 mPosX0 = mOriginOffsetX;
 mPosY0 = mOriginOffsetY;
 x = mPosX - mPosX0;
 y = mPosY - mPosY0;
 canvas.translate (x, y);

 ... more set up ...

 // The focus point for zooming is the center of the
 // displayable region. That point is defined by half
 // the canvas width and height.
 mFocusX = mHalfMaxCanvasWidth;
 mFocusY = mHalfMaxCanvasHeight;
 canvas.scale (mScaleFactor, mScaleFactor, mFocusX, mFocusY);

 ... more ...
 
 // Do the drawing operation for the view.
 drawOnCanvas (canvas);
 
 canvas.restore();
 
}

It is a bit more involved than that because it has to account for the fact that code in its superclass is tracking touches and supporting pan and zoom gestures. So that’s why you see calls like “canvas.translate” and “canvas.scale”.

With all of that set up there in onDrawPz, the drawOnCanvas method is actually pretty easy to understand. It is going to draw the 13 x 13 grid on the canvas. It looks up values from the grid object, which specifies the index number of what image to put on the screen. It then has two loops where each of the images is drawn onto the canvas.

public void drawOnCanvas (Canvas canvas) {
 
 int x, y;
 float fx, fy;
 Paint paint = new Paint();
 Bitmap [] bitmaps = getBitmapsArray ();
 Bitmap [] selectedBitmaps = mSelectedBitmaps;
 int [] [] grid = getGridArray ();
 
 // Set width and height for the rectangle to be drawn.
 ... 
 RectF dest1 = mDestRectF;
 
 // Draw squares to fill the grid.
 float dx = 0, dy = 0;
 for (int j = 0; j < pNumSquaresAlongCanvas; j++) {
 dx = 0;
 for (int i = 0; i < pNumSquaresAlongCanvas; i++) {
   int bindex = grid [j][i];
   boolean isSelected = (mGridSelect [j][i] > 0);

   Bitmap b1 = isSelected ? selectedBitmaps [bindex]
                          : bitmaps [bindex];
   dest1.offsetTo (dx, dy);
   canvas.drawBitmap (b1, null, dest1, paint);
   dx = dx + mSquareWidth;
 }
 dy = dy + mSquareHeight;
 }
 
}

Touches are tracked in PanZoomView. All GameBoardView has to do is convert touches in the View coordinate system that PanZoomView supports into the x-y grid coordinates that GameBoardView listener objects expect. Here is an excerpt from the onTouchUp method.

public void onTouchUp (float downX, float downY, 
                       float upX, float upY) {

 long nDownTime = mDownTime;
 mDownTime = 0l;

 // Figure out if this was a long press.
 long nUpTime = System.nanoTime ();
 boolean isLongPress = (nUpTime - nDownTime) > mLongPressTimeOut;

 // Convert view coordinates to canvas coordinates
 // and, eventually to index values for the grid cells.
 ...

 // We want integer index values to call the listener.
 int sUpX = (int) Math.floor (fx2) + 1;
 int sUpY = (int) Math.floor (fy2) + 1;
 int sDownX = (int) Math.floor (dfx2) + 1;
 int sDownY = (int) Math.floor (dfy2) + 1;

 // Next check to see if there is a listener for these events.
 // If there is not, there is nothing else to do.
 GameBoardTouchListener listener = getTouchListener ();
 if (listener == null) return;

 // Tell the listener about the Up event. 
 if (isLongPress)
 listener.onLongTouchUp (sDownX, sDownY, sUpX, sUpY); 
 else listener.onTouchUp (sDownX, sDownY, sUpX, sUpY); 
}

A very important class in this sample app is PanZoomView. It handles all the low level things related to touch, pan, and zoom gestures. I won’t go into the details here, but you should spend some time reading through it so you understand its onTouchEvent method and its ScaleGestureDetector object. You will also see how it translates all of the touches into method calls (as with onTouchUp and onTouchDown) or changes in instance variables for panning and zooming.

PanZoomView is a class that you might be able to reuse in different contexts. Just override its assorted “supportsXXX” methods (e.g. supportsPan, supportsZoom) and implement your own onDrawPz method for new subclasses.

Game Board In Action

This code is a good starting point for a game. I have used it in my Gomoku game: Gomoku League on Google Play. That game can be played on an 11×11 board or a 19×19 board. For the larger board, the zooming and panning features are very useful.

Another game app that uses the PanZoomView and has its own variation of a GameBoardView is my Double Star game. It is in beta test. Go the Double Star page on Google Play and click “Become a Tester”.

References

I have experimented with panning and zooming and displaying grids in several different demo applications.

  • Android Rotate and Scale Bitmaps  (February, 2014) – This tutorial app covers scaling of bitmaps, rotation of bitmaps, and translation from one origin to another.
Posted in Android, Game Design | Tagged , , , , , , | 1 Comment