| Author |
Message |
MrSnowflake Moderator


Joined: 16 Feb 2008 Posts: 1437 Location: Flanders, Belgium
|
Posted: Sun Oct 12, 2008 5:42 pm Post subject: 2D tutorial |
|
|
Hi guys,
I've made a simple 2D Game template. I created it so I could easilly create new games without doing a lot of redundant stuff. The code is fairly simple and not really suitable for big games (ie. the GameThread should probably be in a seperate file, the snowflake should actually be in a Sprite class, ...). But you get the idea for simple games.
It's based upon the LunarLander example in the SDK, but I remove all unnescessairy stuff, so you are left with an (almost) empty framework.
| Description: |
| GameTemple: Android 2D game template. |
|
 Download |
| Filename: |
GameTemplate.zip |
| Filesize: |
18.09 KB |
| Downloaded: |
4428 Time(s) |
|
|
| Back to top |
|
 |
|
|
 |
ninor Moderator


Joined: 14 Aug 2008 Posts: 180 Location: Barcelona, Spain
|
Posted: Sun Oct 12, 2008 7:16 pm Post subject: |
|
|
Thanks MrSnowFlake!
Mental note: I *must* develop a little game when I have some spare time
_________________
AndDev: Your Android Development Community / Tutorials | Here's my Basic ToolKit |
|
| Back to top |
|
 |
Danuubz Experienced Developer


Joined: 19 Dec 2007 Posts: 75 Location: Germany
|
Posted: Thu Oct 30, 2008 10:11 pm Post subject: |
|
|
I've got a litte code sample for 2D animating on a simple UI extending View (wich is a little bit different from the SurfaceView approach, more like J2SE).
Red box is moving from one edge to the other. You can stop (and restart) him by pressing DPAD_CENTER.
| Java: | // author: Robert Kanzamar
package de.testing;
import android.app.Activity;
import android.os.Bundle;
import android.view.*;
import android.graphics.*;
import android.content.*;
public class Testing extends Activity
{
public static final int DIRECTION_RIGHT = 0, DIRECTION_LEFT = 1;
private Panel main;
private Bitmap scratch;
private Canvas offscreen;
public boolean start = true;
private volatile boolean running = true;
private int direction = DIRECTION_RIGHT;
private int box = 10;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setOffscreenBitmap();
main = new Panel(this);
setContentView(main,new ViewGroup.LayoutParams(320,480));
(new Thread(new AnimationLoop())).start();
}
private void setOffscreenBitmap()
{
scratch = Bitmap.createBitmap(30,30,Bitmap.Config.ARGB_8888);
offscreen = new Canvas();
offscreen.setBitmap(scratch);
offscreen.drawColor(Color.RED);
}
private synchronized void updatePhysics()
{
if(box < 10)
{
direction = DIRECTION_RIGHT;
}
else if(box > 250)
{
direction = DIRECTION_LEFT;
}
if(direction == DIRECTION_RIGHT)
{
box += 10;
}
else
{
box -= 10;
}
}
private synchronized void doDraw(Canvas canvas, Paint paint)
{
if(start)
{
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(scratch,10,10,paint);
start = false;
}
else
{
canvas.save();
canvas.clipRect(box,8,box+32,40);
canvas.drawColor(Color.RED);
// canvas.drawBitmap(scratch,box,10,paint);
canvas.restore();
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
if(keyCode == KeyEvent.KEYCODE_DPAD_CENTER)
{
if(running)
{
running = false;
}
else
{
running = true;
}
}
else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN)
{
finish();
}
return true;
}
class Panel extends View
{
Paint paint;
public Panel(Context context)
{
super(context);
paint = new Paint();
}
@Override
protected void onDraw(Canvas canvas)
{
doDraw(canvas,paint);
}
}
class AnimationLoop implements Runnable
{
public void run()
{
while(true)
{
while(running)
{
try
{
Thread.sleep(30);
}
catch(InterruptedException ex) {}
updatePhysics();
main.postInvalidate();
}
}
}
}
} |
|
|
| Back to top |
|
 |
timcharper Freshman

Joined: 29 Nov 2008 Posts: 2 Location: Utah
|
Posted: Sat Nov 29, 2008 11:16 am Post subject: |
|
|
Is the view the best place to handle physics and game logic?
_________________ http://tim.theenchanter.com/ |
|
| Back to top |
|
 |
Danuubz Experienced Developer


Joined: 19 Dec 2007 Posts: 75 Location: Germany
|
Posted: Sat Nov 29, 2008 11:53 am Post subject: |
|
|
| Actually, in this example, the view only handles drawings. Which is the best depends on what you want to do.
|
|
| Back to top |
|
 |
Croccy22 Developer

Joined: 03 Dec 2008 Posts: 31
|
Posted: Wed Dec 03, 2008 5:01 pm Post subject: |
|
|
Hi guys,
Can someone give me a piece of sample code that does the following:
I see in the code above where it places your png on the screen. Thats fine.
What I want to do is have a png that is 150 pixels wide and 50 pixels high. This png will acually be three frames of animation each that are 50x50 pixels.
What I want to do is copy a 50x50 part of that image and place it on the screen. So I can then have one image which contains several frames of animation.
If someone could just give me a piece of sample code just to do this, a couple of lines will do.
Thanks, Matt.
|
|
| Back to top |
|
 |
|
|
 |
MrSnowflake Moderator


Joined: 16 Feb 2008 Posts: 1437 Location: Flanders, Belgium
|
Posted: Wed Dec 03, 2008 7:03 pm Post subject: |
|
|
You can use Canvas.drawBitmap() with the source rectangle being: (0, 0),(0, 50), (50, 50), (50,0) and then move it: (50, 0),(50, 50), (100, 50), (100,50).
Coordinates might be off, but you should ge the point (i guess).
|
|
| Back to top |
|
 |
Croccy22 Developer

Joined: 03 Dec 2008 Posts: 31
|
Posted: Wed Dec 03, 2008 11:59 pm Post subject: |
|
|
Ok thanks for that. I've actually developed quie a few things for mobile phones before (do a google search for smartanoid) but I used to program on Windows Mobile, so now i'm just trying to work out the syntax for commands I need to use.
Just uploaded an app to the markey place (XmasCard, under apps/demos). I know it is really naff but it was just a test to see how everything tied together and how fast stuff would run. It consists of three alpha layers and 50 alpha sprites on screen at same time. Takes a while to load but I guess that is down to the size of the png files as they weren't very optomized.
Anyone else noticed the comments/ratings on market place are REALLY abusive, I know that they are mostly a bunch of low lifes who don' understand what android is about but I hope google sort it out soon!
Cheers, Matt.
|
|
| Back to top |
|
 |
Croccy22 Developer

Joined: 03 Dec 2008 Posts: 31
|
Posted: Thu Dec 04, 2008 9:17 pm Post subject: |
|
|
Can anyone give me an example of how I could incorporate reading of the accelerometer from within this template?
I have tried the example on this site but I guess they have updated how it works as the tutorial code doesn't seem to work anymore. There is a demo game that uses it but I have tried to 'borrow' the bits of code I need but although I got it to read the values it never seemed to update them.
If someone could give us a hand with this it would be much appreciated, I'll carry on trying myself and will post if I get it working.
Thanks, Matt.
|
|
| Back to top |
|
 |
MrSnowflake Moderator


Joined: 16 Feb 2008 Posts: 1437 Location: Flanders, Belgium
|
Posted: Thu Dec 04, 2008 9:40 pm Post subject: |
|
|
| There's an example in de samples dir of the SDK and the docs should also help you as there is not a lot to do.
|
|
| Back to top |
|
 |
Croccy22 Developer

Joined: 03 Dec 2008 Posts: 31
|
Posted: Thu Dec 04, 2008 9:52 pm Post subject: |
|
|
Ok cheers,
I'm sure everything is there in my code but it's just not recieving the updates by the look of it. I'm new to java so still trying to get my head around the overall structure as I come from C++.
I'll take alook at the sdk.
Matt.
|
|
| Back to top |
|
 |
MrSnowflake Moderator


Joined: 16 Feb 2008 Posts: 1437 Location: Flanders, Belgium
|
Posted: Thu Dec 04, 2008 10:27 pm Post subject: |
|
|
| Do you have a G1?
|
|
| Back to top |
|
 |
Croccy22 Developer

Joined: 03 Dec 2008 Posts: 31
|
Posted: Thu Dec 04, 2008 10:51 pm Post subject: |
|
|
Ok, just done it. And yes it was me being a class a muppet!!
For anyone else here is the code. It's the GameTemplate project but just displays the x,y,z of the accelerometer. People can chop and change as they wish.
Matt.
GameTemplate
| Java: |
package eu.MrSnowflake.android.gametemplate;
import android.app.Activity;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.Window;
import eu.MrSnowflake.android.gametemplate.GameView.GameThread;
public class GameTemplate extends Activity {
private static final int MENU_PAUSE = Menu.FIRST;
private static final int MENU_RESUME = Menu.FIRST + 1;
private static final int MENU_START = Menu.FIRST + 2;
private static final int MENU_STOP = Menu.FIRST + 3;
private SensorManager mSensorManager;
/** A handle to the thread that's actually running the animation. */
private GameThread mGameThread;
/** A handle to the View in which the game is running. */
private GameView mGameView;
/**
* Invoked during init to give the Activity a chance to set up its Menu.
*
* @param menu the Menu to which entries may be added
* @return true
*/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
menu.add(0, MENU_START, 0, R.string.menu_start);
menu.add(0, MENU_STOP, 0, R.string.menu_stop);
menu.add(0, MENU_PAUSE, 0, R.string.menu_pause);
menu.add(0, MENU_RESUME, 0, R.string.menu_resume);
return true;
}
/**
* Invoked when the user selects an item from the Menu.
*
* @param item the Menu entry which was selected
* @return true if the Menu item was legit (and we consumed it), false
* otherwise
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_START:
mGameThread.doStart();
return true;
case MENU_STOP:
mGameThread.setState(GameThread.STATE_LOSE);
return true;
case MENU_PAUSE:
mGameThread.pause();
return true;
case MENU_RESUME:
mGameThread.unpause();
return true;
}
return false;
}
/**
* Invoked when the Activity is created.
*
* @param savedInstanceState a Bundle containing state saved from a previous
* execution, or null if this is a new execution
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// turn off the window's title bar
requestWindowFeature(Window.FEATURE_NO_TITLE);
// tell system to use the layout defined in our XML file
setContentView(R.layout.main);
// get handles to the LunarView from XML, and its LunarThread
mGameView = (GameView) findViewById(R.id.game);
mGameThread = mGameView.getThread();
// set up a new game
mGameThread.setState(GameThread.STATE_READY);
Log.w(this.getClass().getName(), "SIS is null");
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
mSensorManager.registerListener(mGameThread, SensorManager.SENSOR_ACCELEROMETER);
}
/**
* Invoked when the Activity loses user focus.
*/
@Override
protected void onPause() {
super.onPause();
mGameView.getThread().pause(); // pause game when Activity pauses
}
}
|
GameThread
| Java: |
package eu.MrSnowflake.android.gametemplate;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.hardware.SensorListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.TextView;
/**
* View that draws, takes keystrokes, etc. for a simple LunarLander game.
*
* Has a mode which RUNNING, PAUSED, etc. Has a x, y, dx, dy, ... capturing the
* current ship physics. All x/y etc. are measured with (0,0) at the lower left.
* updatePhysics() advances the physics based on realtime. draw() renders the
* ship, and does an invalidate() to prompt another draw() as soon as possible
* by the system.
*/
class GameView extends SurfaceView implements SurfaceHolder.Callback {
class GameThread extends Thread implements SensorListener{
/*
* State-tracking constants
*/
public static final int STATE_LOSE = 1;
public static final int STATE_PAUSE = 2;
public static final int STATE_READY = 3;
public static final int STATE_RUNNING = 4;
public static final int STATE_WIN = 5;
private float x;
private float y;
private static final int SPEED = 100;
private boolean dRight;
private boolean dLeft;
private boolean dUp;
private boolean dDown;
private int mCanvasWidth;
private int mCanvasHeight;
private long mLastTime;
private Bitmap mSnowflake;
float sensorx;
float sensory;
float sensorz;
public void onSensorChanged(int sensor, float[] values) {
sensorx = values[0];
sensory = values[1];
sensorz = values[2];
}
public void onAccuracyChanged(int sensor, int accuracy) {
// TODO Auto-generated method stub
}
/** Message handler used by thread to post stuff back to the GameView */
private Handler mHandler;
/** The state of the game. One of READY, RUNNING, PAUSE, LOSE, or WIN */
private int mMode;
/** Indicate whether the surface has been created & is ready to draw */
private boolean mRun = false;
/** Handle to the surface manager object we interact with */
private SurfaceHolder mSurfaceHolder;
public GameThread(SurfaceHolder surfaceHolder, Context context,
Handler handler) {
// get handles to some important objects
mSurfaceHolder = surfaceHolder;
mHandler = handler;
mContext = context;
x = 10;
y = 10;
mSnowflake = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.snowflake);
}
/**
* Starts the game, setting parameters for the current difficulty.
*/
public void doStart() {
synchronized (mSurfaceHolder) {
// Initialize game here!
x = 10;
y = 10;
mLastTime = System.currentTimeMillis() + 100;
setState(STATE_RUNNING);
}
}
/**
* Pauses the physics update & animation.
*/
public void pause() {
synchronized (mSurfaceHolder) {
if (mMode == STATE_RUNNING)
setState(STATE_PAUSE);
}
}
@Override
public void run() {
while (mRun) {
Canvas c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
if (mMode == STATE_RUNNING)
updateGame();
doDraw(c);
}
} finally {
// do this in a finally so that if an exception is thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
/**
* Used to signal the thread whether it should be running or not.
* Passing true allows the thread to run; passing false will shut it
* down if it's already running. Calling start() after this was most
* recently called with false will result in an immediate shutdown.
*
* @param b true to run, false to shut down
*/
public void setRunning(boolean b) {
mRun = b;
}
/**
* Sets the game mode. That is, whether we are running, paused, in the
* failure state, in the victory state, etc.
*
* @see #setState(int, CharSequence)
* @param mode one of the STATE_* constants
*/
public void setState(int mode) {
synchronized (mSurfaceHolder) {
setState(mode, null);
}
}
/**
* Sets the game mode. That is, whether we are running, paused, in the
* failure state, in the victory state, etc.
*
* @param mode one of the STATE_* constants
* @param message string to add to screen or null
*/
public void setState(int mode, CharSequence message) {
synchronized (mSurfaceHolder) {
mMode = mode;
}
}
/* Callback invoked when the surface dimensions change. */
public void setSurfaceSize(int width, int height) {
// synchronized to make sure these all change atomically
synchronized (mSurfaceHolder) {
mCanvasWidth = width;
mCanvasHeight = height;
}
}
/**
* Resumes from a pause.
*/
public void unpause() {
// Move the real time clock up to now
synchronized (mSurfaceHolder) {
mLastTime = System.currentTimeMillis() + 100;
}
setState(STATE_RUNNING);
}
/**
* Handles a key-down event.
*
* @param keyCode the key that was pressed
* @param msg the original event object
* @return true
*/
boolean doKeyDown(int keyCode, KeyEvent msg) {
boolean handled = false;
synchronized (mSurfaceHolder) {
if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT){
dRight = true;
handled = true;
}
if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT){
dLeft = true;
handled = true;
}
if (keyCode == KeyEvent.KEYCODE_DPAD_UP){
dUp = true;
handled = true;
}
if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN){
dDown = true;
handled = true;
}
return handled;
}
}
/**
* Handles a key-up event.
*
* @param keyCode the key that was pressed
* @param msg the original event object
* @return true if the key was handled and consumed, or else false
*/
boolean doKeyUp(int keyCode, KeyEvent msg) {
boolean handled = false;
synchronized (mSurfaceHolder) {
if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT){
dRight = false;
handled = true;
}
if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT){
dLeft = false;
handled = true;
}
if (keyCode == KeyEvent.KEYCODE_DPAD_UP){
dUp = false;
handled = true;
}
if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN){
dDown = false;
handled = true;
}
return handled;
}
}
/**
* Draws the ship, fuel/speed bars, and background to the provided
* Canvas.
*/
private void doDraw(Canvas canvas) {
// empty canvas
canvas.drawARGB(255, 128, 128, 128);
canvas.drawBitmap(mSnowflake, x, y, new Paint());
canvas.drawText(Float.toString(sensorx), 20, 100, new Paint());
canvas.drawText(Float.toString(sensory), 20, 150, new Paint());
canvas.drawText(Float.toString(sensorz), 20, 200, new Paint());
}
/**
* Updates the game.
*/
private void updateGame() {
//// <DoNotRemove>
long now = System.currentTimeMillis();
// Do nothing if mLastTime is in the future.
// This allows the game-start to delay the start of the physics
// by 100ms or whatever.
if (mLastTime > now)
return;
double elapsed = (now - mLastTime) / 1000.0;
mLastTime = now;
//// </DoNotRemove>
/*
* Why use mLastTime, now and elapsed?
* Well, because the frame rate isn't always constant, it could happen your normal frame rate is 25fps
* then your char will walk at a steady pace, but when your frame rate drops to say 12fps, without elapsed
* your character will only walk half as fast as at the 25fps frame rate. Elapsed lets you manage the slowdowns
* and speedups!
*/
if (dUp)
y -= elapsed * SPEED;
if (dDown)
y += elapsed * SPEED;
if (y < 0)
y = 0;
else if (y >= mCanvasHeight - mSnowflake.getHeight())
y = mCanvasHeight - mSnowflake.getHeight();
if (dLeft)
x -= elapsed * SPEED;
if (dRight)
x += elapsed * SPEED;
if (x < 0)
x = 0;
else if (x >= mCanvasWidth - mSnowflake.getWidth())
x = mCanvasWidth - mSnowflake.getWidth();
}
}
/** Handle to the application context, used to e.g. fetch Drawables. */
private Context mContext;
/** The thread that actually draws the animation */
private GameThread thread;
public GameView(Context context, AttributeSet attrs) {
super(context, attrs);
// register our interest in hearing about changes to our surface
SurfaceHolder holder = getHolder();
holder.addCallback(this);
// create thread only; it's started in surfaceCreated()
thread = new GameThread(holder, context, new Handler() {
@Override
public void handleMessage(Message m) {
// Use for pushing back messages.
}
});
setFocusable(true); // make sure we get key events
}
/**
* Fetches the animation thread corresponding to this LunarView.
*
* @return the animation thread
*/
public GameThread getThread() {
return thread;
}
/**
* Standard override to get key-press events.
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent msg) {
return thread.doKeyDown(keyCode, msg);
}
/**
* Standard override for key-up. We actually care about these, so we can
* turn off the engine or stop rotating.
*/
@Override
public boolean onKeyUp(int keyCode, KeyEvent msg) {
return thread.doKeyUp(keyCode, msg);
}
/**
* Standard window-focus override. Notice focus lost so we can pause on
* focus lost. e.g. user switches to take a call.
*/
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
if (!hasWindowFocus)
thread.pause();
}
/* Callback invoked when the surface dimensions change. */
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
thread.setSurfaceSize(width, height);
}
/*
* Callback invoked when the Surface has been created and is ready to be
* used.
*/
public void surfaceCreated(SurfaceHolder holder) {
// start the thread here so that we don't busy-wait in run()
// waiting for the surface to be created
thread.setRunning(true);
thread.start();
}
/*
* Callback invoked when the Surface has been destroyed and must no longer
* be touched. WARNING: after this method returns, the Surface/Canvas must
* never be touched again!
*/
public void surfaceDestroyed(SurfaceHolder holder) {
// we have to tell thread to shut down & wait for it to finish, or else
// it might touch the Surface after we return and explode
boolean retry = true;
thread.setRunning(false);
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
}
|
|
|
| Back to top |
|
 |
chefgon Junior Developer

Joined: 31 Oct 2008 Posts: 17
|
Posted: Tue Dec 23, 2008 12:07 am Post subject: |
|
|
I tried throwing several PNGs on the screen and it works pretty well, when it loads. But after loading it up once and then exiting and loading again, I always get "Force Close" errors for the next two or three times I try to load it until it will work again, and then sometimes it doesn't load quite right.
Any idea what's going on? Is there something in here that isn't suitable for a large number of sprites? I've noticed that there's no logic to prevent it from restarting every time it loses focus (or the keyboard opens), is it possible I'm leaking memory somewhere? I just find it weird that it always works the first time but gets unreliable after that.
|
|
| Back to top |
|
 |
Danuubz Experienced Developer


Joined: 19 Dec 2007 Posts: 75 Location: Germany
|
Posted: Tue Dec 23, 2008 9:27 am Post subject: |
|
|
How do you exit the application?
It sounds like you press 'HOME' button or maybe 'Back'. I would use System.exit(0) for this.
_________________ Check my app on Youtube: http://www.youtube.com/watch?v=j8ZHq8ZOvtM |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum You cannot attach files in this forum You can download files in this forum
|
© 2007, Android Development Community
All rights reserved.
Powered by phpBB.
|