Translate And Rotate On GameSurface In Android

In this example we will implement GestureOverlayView to detect gestures input, and apply scale, translate and rotate on canvas accordingly. To do this first you need to create gestures of up/down/left/right, clockwise/anti-clockwise, enlarge/reduce and copy the generated file to /res/raw folder.
I will show you how to create your own gesture library in my next post. For now you can download the file from here.

Algorithm:

1.) Create a new project by File-> New -> Android Project name it TranslateAndRotateGame.

2.) Write following into main.xml:

 

<?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" >

<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Hello" />
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>

<com.example.translateandrotategame.GameSurface
android:id="@+id/myview1"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<com.example.translateandrotategame.ForegroundSurface
android:id="@+id/myforeground"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />

<android.gesture.GestureOverlayView
android:id="@+id/gestures"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gestureStrokeType="multiple"
android:eventsInterceptionEnabled="true"/>

</FrameLayout>
</LinearLayout>

 

 

3.) Create and write following into GameThread.java:

 

package com.example.translateandrotategame;

public class GameThread extends Thread {

volatile boolean running = false;

GameSurface parent;
long sleepTime;

GameThread(GameSurface sv, long st){
super();
parent = sv;
sleepTime = st;
}

public void setRunning(boolean r){
running = r;
}

@Override
public void run() {
// TODO Auto-generated method stub
while(running){

try {
sleep(sleepTime);
parent.updateSurfaceView();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}

}

 

 

4.) Create and write following into GameSurface.java:

 

package com.example.translateandrotategame;

import java.util.Random;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class GameSurface extends SurfaceView implements SurfaceHolder.Callback{

SurfaceHolder surfaceHolder;

GameThread myGameThread = null;

int myCanvas_w, myCanvas_h;
Bitmap myCanvasBitmap = null;
Canvas myCanvas = null;
Matrix identityMatrix;

private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
Random random;

public GameSurface(Context context) {
super(context);
// TODO Auto-generated constructor stub
}

public GameSurface(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}

public GameSurface(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// TODO Auto-generated method stub

}

@Override
public void surfaceCreated(SurfaceHolder holder) {

myCanvas_w = getWidth();
myCanvas_h = getHeight();
myCanvasBitmap = Bitmap.createBitmap(myCanvas_w, myCanvas_h, Bitmap.Config.ARGB_8888);
myCanvas = new Canvas();
myCanvas.setBitmap(myCanvasBitmap);

identityMatrix = new Matrix();
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub

}

public void MyGameSurfaceView_OnResume(){

random = new Random();
surfaceHolder = getHolder();
getHolder().addCallback(this);

//Create and start background Thread
myGameThread = new GameThread(this, 200);
myGameThread.setRunning(true);
myGameThread.start();

}

public void MyGameSurfaceView_OnPause(){
//Kill the background Thread
boolean retry = true;
myGameThread.setRunning(false);

while(retry){
try {
myGameThread.join();
retry = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

@Override
protected void onDraw(Canvas canvas) {

paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(3);

//int w = myCanvas.getWidth();
//int h = myCanvas.getHeight();
int x = random.nextInt(myCanvas_w-1);
int y = random.nextInt(myCanvas_h-1);
int r = random.nextInt(255);
int g = random.nextInt(255);
int b = random.nextInt(255);

paint.setColor(0xff000000 + (r << 16) + (g << 8) + b);
myCanvas.drawPoint(x, y, paint);

canvas.drawBitmap(myCanvasBitmap, identityMatrix, null);

}

public void updateStates(){
//Dummy method() to handle the States
}

public void updateSurfaceView(){
//The function run in background thread, not ui thread.

Canvas canvas = null;

try{
canvas = surfaceHolder.lockCanvas();

synchronized (surfaceHolder) {
updateStates();
onDraw(canvas);
}
}finally{
if(canvas != null){
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}

}

 

 

5.) Create and write following into ForegroundSurface.java:

 

package com.example.translateandrotategame;

import android.content.Context;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PorterDuff.Mode;
import android.util.AttributeSet;

public class ForegroundSurface extends GameSurface {

SpriteExample mySprite;

float canvasScaleX = 1.0f;
float canvasScaleY = 1.0f;
float canvasTranslateX = 0.0f;
float canvasTranslateY = 0.0f;
float canvasRotate = 0.0f;

public ForegroundSurface(Context context) {
super(context);
init();
}

public ForegroundSurface(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

public ForegroundSurface(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}

private void init(){
mySprite = new SpriteExample(
BitmapFactory.decodeResource(getResources(), R.drawable.myicon),
100, 100);
}

@Override
protected void onDraw(Canvas canvas) {
//Clear Canvas with transparent background
canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);

canvas.scale(canvasScaleX, canvasScaleY);
canvas.translate(canvasTranslateX, canvasTranslateY);
canvas.rotate(canvasRotate, mySprite.getX(), mySprite.getY());
mySprite.draw(canvas);

}

@Override
public void updateStates() {
// TODO Auto-generated method stub
mySprite.update();
}

void updateAccelerometer(float tx, float ty){
mySprite.setX((int)tx);
mySprite.setY((int)ty);
}

public void canvasUp(){
canvasTranslateY -= 10.0f;
}

public void canvasDown(){
canvasTranslateY += 10.0f;
}

public void canvasLeft(){
canvasTranslateX -= 10.0f;
}

public void canvasRight(){
canvasTranslateX += 10.0f;
}

public void canvasClockwise(){
canvasRotate += 10.0f;
}

public void canvasAntiClockwise(){
canvasRotate -= 10.0f;
}

public void canvasEnlarge(){
canvasScaleX *= 2.0f;
canvasScaleY *= 2.0f;
}

public void canvasReduce(){
canvasScaleX /= 2.0f;
canvasScaleY /= 2.0f;
}

}
[/code]

6.) Create and write following into SpriteExample.java:


package com.example.translateandrotategame;

import android.graphics.Bitmap;
import android.graphics.Canvas;

public class SpriteExample {
private Bitmap bitmap;
private int x;
private int y;
float bitmap_halfWidth, bitmap_halfHeight;

public SpriteExample(Bitmap bm, int tx, int ty){
bitmap = bm;
x = tx;
y = ty;
bitmap_halfWidth = bitmap.getWidth()/2;
bitmap_halfHeight = bitmap.getHeight()/2;
}

public void setX(int tx){
x = tx;
}

public void setY(int ty){
y = ty;
}

public int getX(){
return x;
}

public int getY(){
return y;
}

public void draw(Canvas canvas){
canvas.drawBitmap(bitmap, x-bitmap_halfWidth, y-bitmap_halfHeight, null);
}

public void update(){
}
}

 

 

7.) Create and write following into AccelerometerClass.java:

 

package com.example.translateandrotategame;

import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;

public class AccelerometerClass implements SensorEventListener{

private SensorManager sensorManager;
private Sensor sensorAccelerometer;
private TranslateAndRotateGameActivity parent;

private float maximumRange;

public AccelerometerClass(Context c) {
parent = (TranslateAndRotateGameActivity)c;
}

void registerListener(){
sensorManager = (SensorManager)parent.getSystemService(Context.SENSOR_SERVICE);
sensorAccelerometer = sensorManager.getDefaultSensor(
Sensor.TYPE_ACCELEROMETER);

maximumRange = sensorAccelerometer.getMaximumRange();

sensorManager.registerListener(this,
sensorAccelerometer,
SensorManager.SENSOR_DELAY_NORMAL);
}

void unregisterListener(){
sensorManager.unregisterListener(this);
}

@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {

}

@Override
public void onSensorChanged(SensorEvent event) {

float valueAzimuth = event.values[0];
float valuePitch = event.values[1];

parent.updateAccelerometer(
valueAzimuth/maximumRange, -valuePitch/maximumRange);
}
}

 

8.) Write following permissions to your manifest

 

<activity android:name=".GameSurface" ></activity>
<activity android:name=".GameThread" ></activity>
<activity android:name=".ForegroundSurface" ></activity>
<activity android:name=".SpriteExample" ></activity>
<activity android:name=".AccelerometerClass" ></activity>

 

9.) Run for output.

Steps:

1.) Create a project named TranslateAndRotateGame and set the information as stated in the image.

Build Target: Android 4.4
Application Name: TranslateAndRotateGame
Package Name: com.example.TranslateAndRotateGame
Activity Name: TranslateAndRotateGameActivity

translate1

2.) Open TranslateAndRotateGameActivity.java file and write following code there:

 

package com.example.translateandrotategame;

import java.util.ArrayList;

import android.app.Activity;
import android.gesture.Gesture;
import android.gesture.GestureLibraries;
import android.gesture.GestureLibrary;
import android.gesture.GestureOverlayView;
import android.gesture.GestureOverlayView.OnGesturePerformedListener;
import android.gesture.Prediction;
import android.graphics.PixelFormat;
import android.os.Bundle;
import android.view.SurfaceView;
import android.widget.Toast;

public class TranslateAndRotateGameActivity extends Activity {

GameSurface myGameSurfaceView1;
ForegroundSurface myForeground;

//MyAccelerometer myAccelerometer;

GestureLibrary gestureLibrary = null;
GestureOverlayView gestureOverlayView;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
myGameSurfaceView1 = (GameSurface)findViewById(R.id.myview1);
myForeground = (ForegroundSurface)findViewById(R.id.myforeground);

//Set myForeground using transparent background
myForeground.setZOrderOnTop(true);
myForeground.getHolder().setFormat(PixelFormat.TRANSPARENT);

//myAccelerometer = new MyAccelerometer(this);

gestureOverlayView = (GestureOverlayView)findViewById(R.id.gestures);
gestureLibrary = GestureLibraries.fromRawResource(this, R.raw.gestures);
gestureLibrary.load();
gestureOverlayView.addOnGesturePerformedListener(gesturePerformedListener);

}

OnGesturePerformedListener gesturePerformedListener
= new OnGesturePerformedListener(){

@Override
public void onGesturePerformed(GestureOverlayView view, Gesture gesture) {
ArrayList<Prediction> prediction = gestureLibrary.recognize(gesture);

if(prediction.size() > 0){
String gesturePerformed = prediction.get(0).name;
if(gesturePerformed.equals("up")){
myForeground.canvasUp();
Toast.makeText(TranslateAndRotateGameActivity.this,
"up", Toast.LENGTH_LONG).show();
}else if(gesturePerformed.equals("down")){
myForeground.canvasDown();
Toast.makeText(TranslateAndRotateGameActivity.this,
"down", Toast.LENGTH_LONG).show();
}else if(gesturePerformed.equals("left")){
myForeground.canvasLeft();
Toast.makeText(TranslateAndRotateGameActivity.this,
"left", Toast.LENGTH_LONG).show();
}else if(gesturePerformed.equals("right")){
myForeground.canvasRight();
Toast.makeText(TranslateAndRotateGameActivity.this,
"right", Toast.LENGTH_LONG).show();
}else if(gesturePerformed.equals("clockwise")){
myForeground.canvasClockwise();
Toast.makeText(TranslateAndRotateGameActivity.this,
"clockwise", Toast.LENGTH_LONG).show();
}else if(gesturePerformed.equals("anti-clockwise")){
myForeground.canvasAntiClockwise();
Toast.makeText(TranslateAndRotateGameActivity.this,
"anti-clockwise", Toast.LENGTH_LONG).show();
}else if(gesturePerformed.equals("enlarge")){
myForeground.canvasEnlarge();
Toast.makeText(TranslateAndRotateGameActivity.this,
"enlarge", Toast.LENGTH_LONG).show();
}else if(gesturePerformed.equals("reduce")){
myForeground.canvasReduce();
Toast.makeText(TranslateAndRotateGameActivity.this,
"reduce", Toast.LENGTH_LONG).show();
}
}

}

};

@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
myGameSurfaceView1.MyGameSurfaceView_OnResume();
myForeground.MyGameSurfaceView_OnResume();

//myAccelerometer.registerListener();

}

@Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
myGameSurfaceView1.MyGameSurfaceView_OnPause();
myForeground.MyGameSurfaceView_OnPause();

//myAccelerometer.unregisterListener();
}

void updateAccelerometer(float tx, float ty){
int w = myForeground.getWidth();
int h = myForeground.getHeight();

float x = ((w/2) * tx) + (w/2);
float y = ((h/2) * ty) + (h/2);
myForeground.updateAccelerometer(x, y);
}
}

 

3.) Compile and build the project.

Output

translate2

translate3