Isle Survive v1.4 Released on Android (soon on iOS)

A new version of Isle Survive is now available on the Android Marketplace.

What’s new:

  • Bug fixes (including the load map one where only a few blocks would load)
  • Chests!

I really wanted to add a new enemy in this build but I wanted to get the bug fixes out as quick as possible. So a new enemy is half done, and when finished should drop a nice item(s) that you can now keep in the new chests! So definitely next version gets a new enemy and more loot!

Isle Survive for iPhone and iPad now available

At last, managed to get the game to run nicely on the iPhone and iPad. You can download it from iTunes here.

The optimisations will make it run smoother on Android as well, so everyone wins.

I’ve had to remove the free version of Isle Survive from Android since it was getting too much work to keep multiple versions updated. Also I want to add chests soon and they didn’t make much sense in the free version since the world doesn’t get saved so you would lose all your gear. That would have gone down well. The idea with the free version was that I was going to add an ad banner but in reality it got in the way and made the game unplayable.

So sorry, no more free version but the paid version will now get all the attention.

Creating a Simple Custom View in Android

Recently I needed to create an area on an Android screen that a user could scribble their signature on. Custom views are the ideal way to do this, they are simple to implement and make for good reusable code. This example application will consist of just 2 classes, the SignatureViewTutorialActivity class and the SignatureView class.

Here is a quick and dirty explanation of the code:

mBitmap and mCanvas are linked and are essentially an offscreen buffer that we write to
mPath holds all our signature strokes
mBitmapPaint just holds the dither style for writing our bitmap
mPaint holds our drawing styles for our strokes
On touch_move we update mPath with our strokes
On touch_up we draw our strokes to the offscreen buffer(mCanvas) and clear our strokes (mPath)
onDraw - draws the offscreen buffer(mBitmap) to the visible canvas and also any current strokes (mPath)

First let’s start with the SignatureView class, this is our custom view and so needs to inherit the View class so we start with:

 
public class SignatureView extends View {
	private Bitmap  mBitmap;
        private Canvas  mCanvas;
        private Path    mPath;
        private Paint   mBitmapPaint;
        private Paint   mPaint;

And then we need to set up the constructors…

//3 Constructors, covers instantiating manually or from a layout file
public SignatureView(Context context) {
	super(context);
	initSignatureView();
}
 
public SignatureView(Context context,AttributeSet attrs) {
	super(context,attrs);
	initSignatureView();
}
 
public SignatureView(Context context,AttributeSet attrs,int defaultStyle) {
	super(context,attrs,defaultStyle);
	initSignatureView();
}

This function does some initialisation:

protected void initSignatureView(){
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        mPaint.setColor(Color.BLACK);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setStrokeWidth(12);
}

Now we need to override the onDraw method to make it bend to our will. getMeasuredHeight() and getMeasuredWidth() return the size of the view so we can use this to create a bitmap of the same size. This makes the SignatureView nice and flexible:

@Override
public void onDraw(Canvas canvas) {
	 if(mBitmap == null)
	 {
		 Log.d("SignatureView","Height: " + getMeasuredHeight() + "Width: " + getMeasuredWidth());
		 mBitmap = Bitmap.createBitmap( getMeasuredWidth(), getMeasuredHeight(),Bitmap.Config.ARGB_8888);
		 mBitmap.eraseColor(Color.WHITE);
		 mCanvas = new Canvas(mBitmap);//offscreen canvas
		 mPath = new Path();
		 mBitmapPaint = new Paint(Paint.DITHER_FLAG );
	 }
	 canvas.drawColor(Color.WHITE);
	 canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint); //draw offscreen changes
	 canvas.drawPath(mPath, mPaint); //draw current path
}

We also have to override the onTouchEvent method to handle user input:

 @Override
 public boolean onTouchEvent(MotionEvent event) {
     float x = event.getX();
     float y = event.getY();
 
     switch (event.getAction()) {
         case MotionEvent.ACTION_DOWN:
             touch_start(x, y);
             invalidate();
             break;
         case MotionEvent.ACTION_MOVE:
             touch_move(x, y);
             invalidate();
             break;
         case MotionEvent.ACTION_UP:
             touch_up();
             invalidate();
             break;
     }
     return true;
 }

Here are our last bits of code:

 
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
 
 private void touch_start(float x, float y) {
     mPath.reset();
     mPath.moveTo(x, y);
     mX = x;
     mY = y;
 }
 private void touch_move(float x, float y) {
     float dx = Math.abs(x - mX);
     float dy = Math.abs(y - mY);
     if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
         mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);
         mX = x;
         mY = y;
     }
 }
 private void touch_up() {
     mPath.lineTo(mX, mY);
     mCanvas.drawPath(mPath, mPaint);// commit the path to our offscreen
     mPath.reset();// kill this so we don't double draw
 }

So that’s our custom view sorted, all we need to do now is use it! Very simple, all we need to do is create our SignatureViewTutorialActivity class and update our main.xml layout…

public class SignatureViewTutorialActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}

And our main.xml looks like this:

 
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
 
    <com.platoevolved.signatureviewtutorial.SignatureView
        android:id="@+id/signatureView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</LinearLayout>

Here are the complete listings:
SignatureView.java

package com.platoevolved.signatureviewtutorial;
 
import android.content.Context;
import android.graphics.*;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
 
public class SignatureView extends View {
 
	private Bitmap  mBitmap;
        private Canvas  mCanvas;
        private Path    mPath;
        private Paint   mBitmapPaint;
        private Paint   mPaint;
 
    //3 Constructors, covers instantiating manually or from a layout file
        public SignatureView(Context context) {
		super(context);
		initSignatureView();
	}
 
	public SignatureView(Context context,AttributeSet attrs) {
		super(context,attrs);
		initSignatureView();
	}
 
	public SignatureView(Context context,AttributeSet attrs,int defaultStyle) {
		super(context,attrs,defaultStyle);
		initSignatureView();
	}
 
	protected void initSignatureView(){
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        mPaint.setColor(Color.BLACK);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setStrokeWidth(12);
	}
 
	 @Override
	public void onDraw(Canvas canvas) {
 
		 if(mBitmap == null)
		 {
			 Log.d("SignatureView","Height: " + getMeasuredHeight() + "Width: " + getMeasuredWidth());
			 mBitmap = Bitmap.createBitmap( getMeasuredWidth(), getMeasuredHeight(),Bitmap.Config.ARGB_8888);
			 mBitmap.eraseColor(Color.WHITE);
			 mCanvas = new Canvas(mBitmap);//offscreen canvas
			 mPath = new Path();
			 mBitmapPaint = new Paint(Paint.DITHER_FLAG );
		 }
		 canvas.drawColor(Color.WHITE);
		 canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint); //draw offscreen changes
		 canvas.drawPath(mPath, mPaint); //draw current path
	}
 
 
 
 @Override
 public boolean onTouchEvent(MotionEvent event) {
     float x = event.getX();
     float y = event.getY();
 
     switch (event.getAction()) {
         case MotionEvent.ACTION_DOWN:
             touch_start(x, y);
             invalidate();
             break;
         case MotionEvent.ACTION_MOVE:
             touch_move(x, y);
             invalidate();
             break;
         case MotionEvent.ACTION_UP:
             touch_up();
             invalidate();
             break;
     }
     return true;
 }
 
 
 private float mX, mY;
 private static final float TOUCH_TOLERANCE = 4;
 
 private void touch_start(float x, float y) {
     mPath.reset();
     mPath.moveTo(x, y);
     mX = x;
     mY = y;
 }
 private void touch_move(float x, float y) {
     float dx = Math.abs(x - mX);
     float dy = Math.abs(y - mY);
     if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
         mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);
         mX = x;
         mY = y;
     }
 }
 private void touch_up() {
     mPath.lineTo(mX, mY);
     mCanvas.drawPath(mPath, mPaint);// commit the path to our offscreen
     mPath.reset();// kill this so we don't double draw
 }
}

SignatureViewTutorialActivity.java

package com.platoevolved.signatureviewtutorial;
 
import android.app.Activity;
import android.os.Bundle;
 
public class SignatureViewTutorialActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}