|
/*
* Copyright (C) 2009 The Android Open Source Project
*
* 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.
*
*/
// Android JET demonstration code:
// All inline comments related to the use of the JetPlayer class are preceded by "JET info:"
package com.example.android.jetboy;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.media.JetPlayer;
import android.media.JetPlayer.OnJetEventListener;
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.Button;
import android.widget.TextView;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;
import java.util.concurrent.ConcurrentLinkedQueue;
public class JetBoyView extends SurfaceView implements SurfaceHolder.Callback {
// the number of asteroids that must be destroyed
public static final int mSuccessThreshold = 50;
// used to calculate level for mutes and trigger clip
public int mHitStreak = 0;
// total number asteroids you need to hit.
public int mHitTotal = 0;
// which music bed is currently playing?
public int mCurrentBed = 0;
// a lazy graphic fudge for the initial title splash
private Bitmap mTitleBG;
private Bitmap mTitleBG2;
/**
* Base class for any external event passed to the JetBoyThread. This can
* include user input, system events, network input, etc.
*/
class GameEvent {
public GameEvent() {
eventTime = System.currentTimeMillis();
}
long eventTime;
}
/**
* A GameEvent subclass for key based user input. Values are those used by
* the standard onKey
*/
class KeyGameEvent extends GameEvent {
/**
* Simple constructor to make populating this event easier.
*/
public KeyGameEvent(int keyCode, boolean up, KeyEvent msg) {
this.keyCode = keyCode;
this.msg = msg;
this.up = up;
}
public int keyCode;
public KeyEvent msg;
public boolean up;
}
/**
* A GameEvent subclass for events from the JetPlayer.
*/
class JetGameEvent extends GameEvent {
/**
* Simple constructor to make populating this event easier.
*/
public JetGameEvent(JetPlayer player, short segment, byte track, byte channel,
byte controller, byte value) {
this.player = player;
this.segment = segment;
this.track = track;
this.channel = channel;
this.controller = controller;
this.value = value;
}
public JetPlayer player;
public short segment;
public byte track;
public byte channel;
public byte controller;
public byte value;
}
// JET info: the JetBoyThread receives all the events from the JET player
// JET info: through the OnJetEventListener interface.
class JetBoyThread extends Thread implements OnJetEventListener {
/**
* State-tracking constants.
*/
public static final int STATE_START = -1;
public static final int STATE_PLAY = 0;
public static final int STATE_LOSE = 1;
public static final int STATE_PAUSE = 2;
public static final int STATE_RUNNING = 3;
// how many frames per beat? The basic animation can be changed for
// instance to 3/4 by changing this to 3.
// untested is the impact on other parts of game logic for non 4/4 time.
private static final int ANIMATION_FRAMES_PER_BEAT = 4;
public boolean mInitialized = false;
/** Queue for GameEvents */
protected ConcurrentLinkedQueue<GameEvent> mEventQueue = new ConcurrentLinkedQueue<GameEvent>();
/** Context for processKey to maintain state accross frames * */
protected Object mKeyContext = null;
// the timer display in seconds
public int mTimerLimit;
// used for internal timing logic.
public final int TIMER_LIMIT = 72;
// string value for timer display
private String mTimerValue = "1:12";
// start, play, running, lose are the states we use
public int mState;
// has laser been fired and for how long?
// user for fx logic on laser fire
boolean mLaserOn = false;
long mLaserFireTime = 0;
/** The drawable to use as the far background of the animation canvas */
private Bitmap mBackgroundImageFar;
/** The drawable to use as the close background of the animation canvas */
private Bitmap mBackgroundImageNear;
// JET info: event IDs within the JET file.
// JET info: in this game 80 is used for sending asteroid across the screen
// JET info: 82 is used as game time for 1/4 note beat.
private final byte NEW_ASTEROID_EVENT = 80;
private final byte TIMER_EVENT = 82;
// used to track beat for synch of mute/unmute actions
private int mBeatCount = 1;
// our intrepid space boy
private Bitmap[] mShipFlying = new Bitmap[4];
// the twinkly bit
private Bitmap[] mBeam = new Bitmap[4];
// the things you are trying to hit
private Bitmap[] mAsteroids = new Bitmap[12];
// hit animation
private Bitmap[] mExplosions = new Bitmap[4];
private Bitmap mTimerShell;
private Bitmap mLaserShot;
// used to save the beat event system time.
private long mLastBeatTime;
private long mPassedTime;
// how much do we move the asteroids per beat?
private int mPixelMoveX = 25;
// the asteroid send events are generated from the Jet File.
// but which land they start in is random.
private Random mRandom = new Random();
// JET info: the star of our show, a reference to the JetPlayer object.
private JetPlayer mJet = null;
private boolean mJetPlaying = false;
/** Message handler used by thread to interact with TextView */
private Handler mHandler;
/** Handle to the surface manager object we interact with */
private SurfaceHolder mSurfaceHolder;
/** Handle to the application context, used to e.g. fetch Drawables. */
private Context mContext;
/** Indicate whether the surface has been created & is ready to draw */
private boolean mRun = false;
// updates the screen clock. Also used for tempo timing.
private Timer mTimer = null;
private TimerTask mTimerTask = null;
// one second - used to update timer
private int mTaskIntervalInMillis = 1000;
/**
* Current height of the surface/canvas.
*
* @see #setSurfaceSize
*/
private int mCanvasHeight = 1;
/**
* Current width of the surface/canvas.
*
* @see #setSurfaceSize
*/
private int mCanvasWidth = 1;
// used to track the picture to draw for ship animation
private int mShipIndex = 0;
// stores all of the asteroid objects in order
private Vector<Asteroid> mDangerWillRobinson;
private Vector<Explosion> mExplosion;
// right to left scroll tracker for near and far BG
private int mBGFarMoveX = 0;
private int mBGNearMoveX = 0;
// how far up (close to top) jet boy can fly
private int mJetBoyYMin = 40;
private int mJetBoyX = 0;
private int mJetBoyY = 0;
// this is the pixel position of the laser beam guide.
private int mAsteroidMoveLimitX = 110;
// how far up asteroid can be painted
private int mAsteroidMinY = 40;
Resources mRes;
// array to store the mute masks that are applied during game play to respond to
// the player's hit streaks
private boolean muteMask[][] = new boolean[9][32];
/**
* This is the constructor for the main worker bee
*
* @param surfaceHolder
* @param context
* @param handler
*/
public JetBoyThread(SurfaceHolder surfaceHolder, Context context, Handler handler) {
mSurfaceHolder = surfaceHolder;
mHandler = handler;
mContext = context;
mRes = context.getResources();
// JET info: this are the mute arrays associated with the music beds in the
// JET info: JET file
for (int ii = 0; ii < 8; ii++) {
for (int xx = 0; xx < 32; xx++) {
muteMask[ii][xx] = true;
}
}
muteMask[0][2] = false;
muteMask[0][3] = false;
muteMask[0][4] = false;
muteMask[0][5] = false;
muteMask[1][2] = false;
muteMask[1][3] = false;
muteMask[1][4] = false;
muteMask[1][5] = false;
muteMask[1][8] = false;
muteMask[1][9] = false;
muteMask[2][2] = false;
muteMask[2][3] = false;
muteMask[2][6] = false;
muteMask[2][7] = false;
muteMask[2][8] = false;
muteMask[2][9] = false;
muteMask[3][2] = false;
muteMask[3][3] = false;
muteMask[3][6] = false;
muteMask[3][11] = false;
muteMask[3][12] = false;
muteMask[4][2] = false;
muteMask[4][3] = false;
muteMask[4][10] = false;
muteMask[4][11] = false;
muteMask[4][12] = false;
muteMask[4][13] = false;
muteMask[5][2] = false;
muteMask[5][3] = false;
muteMask[5][10] = false;
muteMask[5][12] = false;
muteMask[5][15] = false;
muteMask[5][17] = false;
muteMask[6][2] = false;
muteMask[6][3] = false;
muteMask[6][14] = false;
muteMask[6][15] = false;
muteMask[6][16] = false;
muteMask[6][17] = false;
muteMask[7][2] = false;
muteMask[7][3] = false;
muteMask[7][6] = false;
muteMask[7][14] = false;
muteMask[7][15] = false;
muteMask[7][16] = false;
muteMask[7][17] = false;
muteMask[7][18] = false;
// set all tracks to play
for (int xx = 0; xx < 32; xx++) {
muteMask[8][xx] = false;
}
// always set state to start, ensure we come in from front door if
// app gets tucked into background
mState = STATE_START;
setInitialGameState();
mTitleBG = BitmapFactory.decodeResource(mRes, R.drawable.title_hori);
// load background image as a Bitmap instead of a Drawable b/c
// we don't need to transform it and it's faster to draw this
// way...thanks lunar lander :)
// two background since we want them moving at different speeds
mBackgroundImageFar = BitmapFactory.decodeResource(mRes, R.drawable.background_a);
mLaserShot = BitmapFactory.decodeResource(mRes, R.drawable.laser);
mBackgroundImageNear = BitmapFactory.decodeResource(mRes, R.drawable.background_b);
mShipFlying[0] = BitmapFactory.decodeResource(mRes, R.drawable.ship2_1);
mShipFlying[1] = BitmapFactory.decodeResource(mRes, R.drawable.ship2_2);
mShipFlying[2] = BitmapFactory.decodeResource(mRes, R.drawable.ship2_3);
mShipFlying[3] = BitmapFactory.decodeResource(mRes, R.drawable.ship2_4);
mBeam[0] = BitmapFactory.decodeResource(mRes, R.drawable.intbeam_1);
mBeam[1] = BitmapFactory.decodeResource(mRes, R.drawable.intbeam_2);
mBeam[2] = BitmapFactory.decodeResource(mRes, R.drawable.intbeam_3);
mBeam[3] = BitmapFactory.decodeResource(mRes, R.drawable.intbeam_4);
mTimerShell = BitmapFactory.decodeResource(mRes, R.drawable.int_timer);
// I wanted them to rotate in a certain way
// so I loaded them backwards from the way created.
mAsteroids[11] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid01);
mAsteroids[10] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid02);
mAsteroids[9] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid03);
mAsteroids[8] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid04);
mAsteroids[7] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid05);
mAsteroids[6] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid06);
mAsteroids[5] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid07);
mAsteroids[4] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid08);
mAsteroids[3] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid09);
mAsteroids[2] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid10);
mAsteroids[1] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid11);
mAsteroids[0] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid12);
mExplosions[0] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid_explode1);
mExplosions[1] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid_explode2);
mExplosions[2] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid_explode3);
mExplosions[3] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid_explode4);
}
/**
* Does the grunt work of setting up initial jet requirements
*/
private void initializeJetPlayer() {
// JET info: let's create our JetPlayer instance using the factory.
// JET info: if we already had one, the same singleton is returned.
mJet = JetPlayer.getJetPlayer();
mJetPlaying = false;
// JET info: make sure we flush the queue,
// JET info: otherwise left over events from previous gameplay can hang around.
// JET info: ok, here we don't really need that but if you ever reuse a JetPlayer
// JET info: instance, clear the queue before reusing it, this will also clear any
// JET info: trigger clips that have been triggered but not played yet.
mJet.clearQueue();
// JET info: we are going to receive in this example all the JET callbacks
// JET info: inthis animation thread object.
mJet.setEventListener(this);
Log.d(TAG, "opening jet file");
// JET info: load the actual JET content the game will be playing,
// JET info: it's stored as a raw resource in our APK, and is labeled "level1"
mJet.loadJetFile(mContext.getResources().openRawResourceFd(R.raw.level1));
// JET info: if our JET file was stored on the sdcard for instance, we would have used
// JET info: mJet.loadJetFile("/sdcard/level1.jet");
Log.d(TAG, "opening jet file DONE");
mCurrentBed = 0;
byte sSegmentID = 0;
Log.d(TAG, " start queuing jet file");
// JET info: now we're all set to prepare queuing the JET audio segments for the game.
// JET info: in this example, the game uses segment 0 for the duration of the game play,
// JET info: and plays segment 1 several times as the "outro" music, so we're going to
// JET info: queue everything upfront, but with more complex JET compositions, we could
// JET info: also queue the segments during the game play.
// JET info: this is the main game play music
// JET info: it is located at segment 0
// JET info: it uses the first DLS lib in the .jet resource, which is at index 0
// JET info: index -1 means no DLS
mJet.queueJetSegment(0, 0, 0, 0, 0, sSegmentID);
// JET info: end game music, loop 4 times normal pitch
mJet.queueJetSegment(1, 0, 4, 0, 0, sSegmentID);
// JET info: end game music loop 4 times up an octave
mJet.queueJetSegment(1, 0, 4, 1, 0, sSegmentID);
// JET info: set the mute mask as designed for the beginning of the game, when the
// JET info: the player hasn't scored yet.
mJet.setMuteArray(muteMask[0], true);
Log.d(TAG, " start queuing jet file DONE");
}
private void doDraw(Canvas canvas) {
|
|