|
Android example source code file (AmazedView.java)
The AmazedView.java Android example source code
/*
* Copyright (C) 2008 Jason Tomlinson.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.amazed;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.hardware.SensorListener;
import android.hardware.SensorManager;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
/**
* Custom view used to draw the maze and marble. Responds to accelerometer
* updates to roll the marble around the screen.
*/
public class AmazedView extends View {
// Game objects
private Marble mMarble;
private Maze mMaze;
private Activity mActivity;
// canvas we paint to.
private Canvas mCanvas;
private Paint mPaint;
private Typeface mFont = Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD);
private int mTextPadding = 10;
private int mHudTextY = 440;
// game states
private final static int NULL_STATE = -1;
private final static int GAME_INIT = 0;
private final static int GAME_RUNNING = 1;
private final static int GAME_OVER = 2;
private final static int GAME_COMPLETE = 3;
private final static int GAME_LANDSCAPE = 4;
// current state of the game
private static int mCurState = NULL_STATE;
// game strings
private final static int TXT_LIVES = 0;
private final static int TXT_LEVEL = 1;
private final static int TXT_TIME = 2;
private final static int TXT_TAP_SCREEN = 3;
private final static int TXT_GAME_COMPLETE = 4;
private final static int TXT_GAME_OVER = 5;
private final static int TXT_TOTAL_TIME = 6;
private final static int TXT_GAME_OVER_MSG_A = 7;
private final static int TXT_GAME_OVER_MSG_B = 8;
private final static int TXT_RESTART = 9;
private final static int TXT_LANDSCAPE_MODE = 10;
private static String mStrings[];
// this prevents the user from dying instantly when they start a level if
// the device is tilted.
private boolean mWarning = false;
// screen dimensions
private int mCanvasWidth = 0;
private int mCanvasHeight = 0;
private int mCanvasHalfWidth = 0;
private int mCanvasHalfHeight = 0;
// are we running in portrait mode.
private boolean mPortrait;
// current level
private int mlevel = 1;
// timing used for scoring.
private long mTotalTime = 0;
private long mStartTime = 0;
private long mEndTime = 0;
// sensor manager used to control the accelerometer sensor.
private SensorManager mSensorManager;
// accelerometer sensor values.
private float mAccelX = 0;
private float mAccelY = 0;
private float mAccelZ = 0; // this is never used but just in-case future
// versions make use of it.
// accelerometer buffer, currently set to 0 so even the slightest movement
// will roll the marble.
private float mSensorBuffer = 0;
// http://code.google.com/android/reference/android/hardware/SensorManager.html#SENSOR_ACCELEROMETER
// for an explanation on the values reported by SENSOR_ACCELEROMETER.
private final SensorListener mSensorAccelerometer = new SensorListener() {
// method called whenever new sensor values are reported.
public void onSensorChanged(int sensor, float[] values) {
// grab the values required to respond to user movement.
mAccelX = values[0];
mAccelY = values[1];
mAccelZ = values[2];
}
// reports when the accuracy of sensor has change
// SENSOR_STATUS_ACCURACY_HIGH = 3
// SENSOR_STATUS_ACCURACY_LOW = 1
// SENSOR_STATUS_ACCURACY_MEDIUM = 2
// SENSOR_STATUS_UNRELIABLE = 0 //calibration required.
public void onAccuracyChanged(int sensor, int accuracy) {
// currently not used
}
};
/**
* Custom view constructor.
*
* @param context
* Application context
* @param activity
* Activity controlling the view
*/
public AmazedView(Context context, Activity activity) {
super(context);
mActivity = activity;
// init paint and make is look "nice" with anti-aliasing.
mPaint = new Paint();
mPaint.setTextSize(14);
mPaint.setTypeface(mFont);
mPaint.setAntiAlias(true);
// setup accelerometer sensor manager.
mSensorManager = (SensorManager) activity.getSystemService(Context.SENSOR_SERVICE);
// register our accelerometer so we can receive values.
// SENSOR_DELAY_GAME is the recommended rate for games
mSensorManager.registerListener(mSensorAccelerometer, SensorManager.SENSOR_ACCELEROMETER,
SensorManager.SENSOR_DELAY_GAME);
// setup our maze and marble.
mMaze = new Maze(mActivity);
mMarble = new Marble(this);
// load array from /res/values/strings.xml
mStrings = getResources().getStringArray(R.array.gameStrings);
// set the starting state of the game.
switchGameState(GAME_INIT);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// get new screen dimensions.
mCanvasWidth = w;
mCanvasHeight = h;
mCanvasHalfWidth = w / 2;
mCanvasHalfHeight = h / 2;
// are we in portrait or landscape mode now?
// you could use bPortrait = !bPortrait however in the future who know's
// how many different ways a device screen may be rotated.
if (mCanvasHeight > mCanvasWidth)
mPortrait = true;
else {
mPortrait = false;
switchGameState(GAME_LANDSCAPE);
}
}
/**
* Called every cycle, used to process current game state.
*/
public void gameTick() {
// very basic state machine, makes a good foundation for a more complex
// game.
switch (mCurState) {
case GAME_INIT:
// prepare a new game for the user.
initNewGame();
switchGameState(GAME_RUNNING);
case GAME_RUNNING:
// update our marble.
if (!mWarning)
updateMarble();
break;
}
// redraw the screen once our tick function is complete.
invalidate();
}
/**
* Reset game variables in preparation for a new game.
*/
public void initNewGame() {
mMarble.setLives(5);
mTotalTime = 0;
mlevel = 0;
initLevel();
}
/**
* Initialize the next level.
*/
public void initLevel() {
if (mlevel < mMaze.MAX_LEVELS) {
// setup the next level.
mWarning = true;
mlevel++;
mMaze.load(mActivity, mlevel);
mMarble.init();
} else {
// user has finished the game, update state machine.
switchGameState(GAME_COMPLETE);
}
}
/**
* Called from gameTick(), update marble x,y based on latest values obtained
* from the Accelerometer sensor. AccelX and accelY are values received from
* the accelerometer, higher values represent the device tilted at a more
* acute angle.
*/
public void updateMarble() {
// we CAN give ourselves a buffer to stop the marble from rolling even
// though we think the device is "flat".
if (mAccelX > mSensorBuffer || mAccelX < -mSensorBuffer)
mMarble.updateX(mAccelX);
if (mAccelY > mSensorBuffer || mAccelY < -mSensorBuffer)
mMarble.updateY(mAccelY);
// check which cell the marble is currently occupying.
if (mMaze.getCellType(mMarble.getX(), mMarble.getY()) == mMaze.VOID_TILE) {
// user entered the "void".
if (mMarble.getLives() > 0) {
// user still has some lives remaining, restart the level.
mMarble.death();
mMarble.init();
mWarning = true;
} else {
// user has no more lives left, end of game.
mEndTime = System.currentTimeMillis();
mTotalTime += mEndTime - mStartTime;
switchGameState(GAME_OVER);
}
} else if (mMaze.getCellType(mMarble.getX(), mMarble.getY()) == mMaze.EXIT_TILE) {
// user has reached the exit tiles, prepare the next level.
mEndTime = System.currentTimeMillis();
mTotalTime += mEndTime - mStartTime;
initLevel();
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// we only want to handle down events .
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (mCurState == GAME_OVER || mCurState == GAME_COMPLETE) {
// re-start the game.
mCurState = GAME_INIT;
} else if (mCurState == GAME_RUNNING) {
// in-game, remove the pop-up text so user can play.
mWarning = false;
mStartTime = System.currentTimeMillis();
}
}
return true;
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// quit application if user presses the back key.
if (keyCode == KeyEvent.KEYCODE_BACK)
cleanUp();
return true;
}
@Override
public void onDraw(Canvas canvas) {
// update our canvas reference.
mCanvas = canvas;
// clear the screen.
mPaint.setColor(Color.WHITE);
mCanvas.drawRect(0, 0, mCanvasWidth, mCanvasHeight, mPaint);
// simple state machine, draw screen depending on the current state.
switch (mCurState) {
case GAME_RUNNING:
// draw our maze first since everything else appears "on top" of it.
mMaze.draw(mCanvas, mPaint);
// draw our marble and hud.
mMarble.draw(mCanvas, mPaint);
// draw hud
drawHUD();
break;
case GAME_OVER:
drawGameOver();
break;
case GAME_COMPLETE:
drawGameComplete();
break;
case GAME_LANDSCAPE:
drawLandscapeMode();
break;
}
gameTick();
}
/**
* Called from onDraw(), draws the in-game HUD
*/
public void drawHUD() {
mPaint.setColor(Color.BLACK);
mPaint.setTextAlign(Paint.Align.LEFT);
mCanvas.drawText(mStrings[TXT_TIME] + ": " + (mTotalTime / 1000), mTextPadding, mHudTextY,
mPaint);
mPaint.setTextAlign(Paint.Align.CENTER);
mCanvas.drawText(mStrings[TXT_LEVEL] + ": " + mlevel, mCanvasHalfWidth, mHudTextY, mPaint);
mPaint.setTextAlign(Paint.Align.RIGHT);
mCanvas.drawText(mStrings[TXT_LIVES] + ": " + mMarble.getLives(), mCanvasWidth - mTextPadding,
mHudTextY, mPaint);
// do we need to display the warning message to save the user from
// possibly dying instantly.
if (mWarning) {
mPaint.setColor(Color.BLUE);
mCanvas
.drawRect(0, mCanvasHalfHeight - 15, mCanvasWidth, mCanvasHalfHeight + 5,
mPaint);
mPaint.setColor(Color.WHITE);
mPaint.setTextAlign(Paint.Align.CENTER);
mCanvas.drawText(mStrings[TXT_TAP_SCREEN], mCanvasHalfWidth, mCanvasHalfHeight, mPaint);
}
}
/**
* Called from onDraw(), draws the game over screen.
*/
public void drawGameOver() {
mPaint.setColor(Color.BLACK);
mPaint.setTextAlign(Paint.Align.CENTER);
mCanvas.drawText(mStrings[TXT_GAME_OVER], mCanvasHalfWidth, mCanvasHalfHeight, mPaint);
mCanvas.drawText(mStrings[TXT_TOTAL_TIME] + ": " + (mTotalTime / 1000) + "s",
mCanvasHalfWidth, mCanvasHalfHeight + mPaint.getFontSpacing(), mPaint);
mCanvas.drawText(mStrings[TXT_GAME_OVER_MSG_A] + " " + (mlevel - 1) + " "
+ mStrings[TXT_GAME_OVER_MSG_B], mCanvasHalfWidth, mCanvasHalfHeight
+ (mPaint.getFontSpacing() * 2), mPaint);
mCanvas.drawText(mStrings[TXT_RESTART], mCanvasHalfWidth, mCanvasHeight
- (mPaint.getFontSpacing() * 3), mPaint);
}
/**
* Called from onDraw(), draws the game complete screen.
*/
public void drawGameComplete() {
mPaint.setColor(Color.BLACK);
mPaint.setTextAlign(Paint.Align.CENTER);
mCanvas.drawText(mStrings[GAME_COMPLETE], mCanvasHalfWidth, mCanvasHalfHeight, mPaint);
mCanvas.drawText(mStrings[TXT_TOTAL_TIME] + ": " + (mTotalTime / 1000) + "s",
mCanvasHalfWidth, mCanvasHalfHeight + mPaint.getFontSpacing(), mPaint);
mCanvas.drawText(mStrings[TXT_RESTART], mCanvasHalfWidth, mCanvasHeight
- (mPaint.getFontSpacing() * 3), mPaint);
}
/**
* Called from onDraw(), displays a message asking the user to return the
* device back to portrait mode.
*/
public void drawLandscapeMode() {
mPaint.setColor(Color.WHITE);
mPaint.setTextAlign(Paint.Align.CENTER);
mCanvas.drawRect(0, 0, mCanvasWidth, mCanvasHeight, mPaint);
mPaint.setColor(Color.BLACK);
mCanvas.drawText(mStrings[TXT_LANDSCAPE_MODE], mCanvasHalfWidth, mCanvasHalfHeight, mPaint);
}
/**
* Updates the current game state with a new state. At the moment this is
* very basic however if the game was to get more complicated the code
* required for changing game states could grow quickly.
*
* @param newState
* New game state
*/
public void switchGameState(int newState) {
mCurState = newState;
}
/**
* Register the accelerometer sensor so we can use it in-game.
*/
public void registerListener() {
mSensorManager.registerListener(mSensorAccelerometer, SensorManager.SENSOR_ACCELEROMETER,
SensorManager.SENSOR_DELAY_GAME);
}
/**
* Unregister the accelerometer sensor otherwise it will continue to operate
* and report values.
*/
public void unregisterListener() {
mSensorManager.unregisterListener(mSensorAccelerometer);
}
/**
* Clean up the custom view and exit the application.
*/
public void cleanUp() {
mMarble = null;
mMaze = null;
mStrings = null;
unregisterListener();
mActivity.finish();
}
}
Other Android examples (source code examples)Here is a short list of links related to this Android AmazedView.java source code file: |
| ... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.