I am trying to do my first 2d game in android studio, just for fun and experience, but I am having a major problem. What I want to do is start from a title screen with two buttons, 'Play Game' and 'High Score' (it's obvious what these buttons do. Where my issue lies is when I press the 'Play Game' button. I want it to take me to the game screen, but instead, the app crashes, saying it could not load. Any ideas what's going on? Thanks.
package com.example.cory2.bugsmasher;
import android.app.Activity;
import android.content.Intent;
import android.media.MediaPlayer;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
public class TitleActivity extends Activity {
Button play, score;
MediaPlayer bgrnd, music ;
static MainView v;
@Override
protected void onCreate(Bundle savedInstanceState) {
v = new MainView(this);
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
//setContentView(R.layout.activity_title);
play=(Button)findViewById(R.id.game);
score=(Button)findViewById(R.id.score);
bgrnd=MediaPlayer.create(this, R.raw.insects);
music = MediaPlayer.create(this, R.raw.music);
score.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(TitleActivity.this, HighScore.class));
music.stop();
bgrnd.stop();
}
});
play.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
music.stop();
bgrnd.stop();
startActivity(new Intent(TitleActivity.this, MainView.class));
}
});
bgrnd.setLooping(true);
bgrnd.start();
music.setLooping(true);
music.start();
}
public void onPause() {
super.onPause();
music.stop();
Log.i("ProjectLogging", "Pausing the game");
}
public void onResume() {
super.onResume();
}
}
package com.example.cory2.bugsmasher;
import android.graphics.Bitmap;
import android.media.SoundPool;
public class Assets {
static Bitmap background;
static Bitmap foodbar;
static Bitmap roach1;
static Bitmap roach2;
static Bitmap roach3;
// States of the Game Screen
enum GameState {
GettingReady, // play "get ready" sound and start timer, goto next state
Starting, // when 3 seconds have elapsed, goto next state
Running, // play the game, when livesLeft == 0 goto next state
GameEnding, // show game over message
GameOver, // game is over, wait for any Touch and go back to title activity screen
};
static GameState state; // current state of the game
static float gameTimer; // in seconds
static int livesLeft; // 0-3
static SoundPool soundPool;
static int sound_getready;
static int sound_squish;
static int sound_thump;
static Bug bug; // try using an array of bugs instead of only 1 bug (so you can have more than 1 on screen at a time)
}
package com.example.cory2.bugsmasher;
import android.content.Context;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Build;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class MainView extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder holder = null;
Context context;
private MainThread t = null;
// Constructor
@SuppressWarnings("deprecation")
public MainView (Context context) {
super(context);
// Save context
this.context = context;
// Retrieve the SurfaceHolder instance associated with this SurfaceView.
holder = getHolder();
// Initialize variables
this.context = context;
Assets.state = Assets.GameState.GettingReady;
Assets.livesLeft = 3;
// Load the sound effects
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
Assets.soundPool = new SoundPool(10, AudioManager.STREAM_MUSIC, 0);
}
else {
AudioAttributes attributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_GAME)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.build();
Assets.soundPool = new SoundPool.Builder()
.setAudioAttributes(attributes)
.build();
}
Assets.sound_getready = Assets.soundPool.load(context, R.raw.getready, 1);
Assets.sound_squish = Assets.soundPool.load(context, R.raw.squish1, 1);
Assets.sound_thump = Assets.soundPool.load(context, R.raw.smack, 1);
// Specify this class (MainView) as the class that implements the three callback methods required by SurfaceHolder.Callback.
holder.addCallback(this);
}
public void pause ()
{
t.setRunning(false);
while (true) {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
}
t = null;
}
public void resume ()
{
}
@Override
public boolean onTouchEvent(MotionEvent event)
{
float x, y;
int action = event.getAction();
x = event.getX();
y = event.getY();
// if (action==MotionEvent.ACTION_MOVE) {
// }
// if (action==MotionEvent.ACTION_DOWN){
// }
if (action == MotionEvent.ACTION_UP) {
if (t != null)
t.setXY ((int)x, (int)y);
}
return true; // to indicate we have handled this event
}
@Override
public void surfaceCreated (SurfaceHolder holder) {
// Create and start a drawing thread whose Runnable object is defined by this class (MainView)
if (t == null) {
t = new MainThread(holder, context);
t.setRunning(true);
t.start();
setFocusable(true); // make sure we get events
}
}
// Neither of these two methods are used in this example, however, their definitions are required because SurfaceHolder.Callback was implemented
@Override public void surfaceChanged(SurfaceHolder sh, int f, int w, int h) {}
@Override public void surfaceDestroyed(SurfaceHolder sh) {}
}
package com.example.cory2.bugsmasher;
import android.graphics.Canvas;
public class Bug {
// States of a Bug
enum BugState {
Dead,
ComingBackToLife,
Alive, // in the game
DrawDead, // draw dead body on screen
};
BugState state; // current state of bug
int x,y; // location on screen (in screen coordinates)
double speed; // speed of bug (in pixels per second)
// All times are in seconds
float timeToBirth; // # seconds till birth
float startBirthTimer; // starting timestamp when decide to be born
float deathTime; // time of death
float animateTimer; // used to move and animate the bug
// Bug starts not alive
public Bug () {
state = BugState.Dead;
}
// Bug birth processing
public void birth (Canvas canvas) {
// Bring a bug to life?
if (state == BugState.Dead) {
// Set it to coming alive
state = BugState.ComingBackToLife;
// Set a random number of seconds before it comes to life
timeToBirth = (float)Math.random () * 5;
// Note the current time
startBirthTimer = System.nanoTime() / 1000000000f;
}
// Check if bug is alive yet
else if (state == BugState.ComingBackToLife) {
float curTime = System.nanoTime() / 1000000000f;
// Has birth timer expired?
if (curTime - startBirthTimer >= timeToBirth) {
// If so, then bring bug to life
state = BugState.Alive;
// Set bug starting location at top of screen
x = (int)(Math.random() * canvas.getWidth());
// Keep entire bug on screen
if (x < Assets.roach1.getWidth()/2)
x = Assets.roach1.getWidth()/2;
else if (x > canvas.getWidth() - Assets.roach1.getWidth()/2)
x = canvas.getWidth() - Assets.roach1.getWidth()/2;
y = 0;
// Set speed of this bug
speed = canvas.getHeight() / 4; // no faster than 1/4 a screen per second
// subtract a random amount off of this so some bugs are a little slower
// ADD CODE HERE
// Record timestamp of this bug being born
animateTimer = curTime;
}
}
}
// Bug movement processing
public void move (Canvas canvas) {
// Make sure this bug is alive
if (state == BugState.Alive) {
// Get elapsed time since last call here
float curTime = System.nanoTime() / 1000000000f;
float elapsedTime = curTime - animateTimer;
animateTimer = curTime;
// Compute the amount of pixels to move (vertically down the screen)
y += (speed * elapsedTime);
// Draw bug on screen
canvas.drawBitmap(Assets.roach1, x-Assets.roach1.getWidth()/2, y-Assets.roach1.getHeight()/2, null);
// ADD CODE HERE - Draw each frame of animation as appropriate - don't just draw 1 frame
// Has it reached the bottom of the screen?
if (y >= canvas.getHeight()) {
// Kill the bug
state = BugState.Dead;
// Subtract 1 life
Assets.livesLeft--;
}
}
}
// Process touch to see if kills bug - return true if bug killed
public boolean touched (Canvas canvas, int touchx, int touchy) {
boolean touched = false;
// Make sure this bug is alive
if (state == BugState.Alive) {
// Compute distance between touch and center of bug
float dis = (float)(Math.sqrt ((touchx - x) * (touchx - x) + (touchy - y) * (touchy - y)));
// Is this close enough for a kill?
if (dis <= Assets.roach1.getWidth()*0.5f) {
state = BugState.DrawDead; // need to draw dead body on screen for a while
touched = true;
// Record time of death
deathTime = System.nanoTime() / 1000000000f;
}
}
return (touched);
}
// Draw dead bug body on screen, if needed
public void drawDead (Canvas canvas) {
if (state == BugState.DrawDead) {
canvas.drawBitmap(Assets.roach3, x-Assets.roach1.getWidth()/2, y-Assets.roach1.getHeight()/2, null);
// Get time since death
float curTime = System.nanoTime() / 1000000000f;
float timeSinceDeath = curTime - deathTime;
// Drawn dead body long enough (4 seconds) ?
if (timeSinceDeath > 4)
state = BugState.Dead;
}
}
}
package com.example.cory2.bugsmasher;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.view.SurfaceHolder;
import android.widget.Toast;
public class MainThread extends Thread implements Runnable{
private SurfaceHolder holder;
private Handler handler; // required for running code in the UI thread
private boolean isRunning = false;
Context context;
Paint paint;
int touchx, touchy; // x,y of touch event
boolean touched; // true if touch happened
boolean data_initialized;
private static final Object lock = new Object();
public MainThread (SurfaceHolder surfaceHolder, Context context) {
holder = surfaceHolder;
this.context = context;
handler = new Handler();
data_initialized = false;
touched = false;
}
public void setRunning(boolean b) {
isRunning = b; // no need to synchronize this since this is the only line of code to writes this variable
}
// Set the touch event x,y location and flag indicating a touch has happened
public void setXY (int x, int y) {
synchronized (lock) {
touchx = x;
touchy = y;
this.touched = true;
}
}
@Override
public void run() {
while (isRunning) {
// Lock the canvas before drawing
Canvas canvas = holder.lockCanvas();
if (canvas != null) {
// Perform drawing operations on the canvas
render(canvas);
// After drawing, unlock the canvas and display it
holder.unlockCanvasAndPost (canvas);
}
}
}
// Loads graphics, etc. used in game
private void loadData (Canvas canvas) {
Bitmap bmp;
int newWidth, newHeight;
float scaleFactor;
// Create a paint object for drawing vector graphics
paint = new Paint();
// Load score bar
// ADD CODE HERE
// Load food bar
bmp = BitmapFactory.decodeResource (context.getResources(), R.drawable.food);
// Compute size of bitmap needed (suppose want height = 10% of screen height)
newHeight = (int)(canvas.getHeight() * 0.1f);
// Scale it to a new size
Assets.foodbar = Bitmap.createScaledBitmap (bmp, canvas.getWidth(), newHeight, false);
// Delete the original
bmp = null;
// Load roach1
bmp = BitmapFactory.decodeResource (context.getResources(), R.drawable.s1);
newWidth = (int)(canvas.getWidth() * 0.2f);
// What was the scaling factor to get to this?
scaleFactor = (float)newWidth / bmp.getWidth();
// Compute the new height
newHeight = (int)(bmp.getHeight() * scaleFactor);
// Scale it to a new size
Assets.roach1 = Bitmap.createScaledBitmap (bmp, newWidth, newHeight, false);
// Delete the original
bmp = null;
bmp = BitmapFactory.decodeResource (context.getResources(), R.drawable.s2);
// Compute size of bitmap needed (suppose want width = 20% of screen width)
newWidth = (int)(canvas.getWidth() * 0.2f);
// What was the scaling factor to get to this?
scaleFactor = (float)newWidth / bmp.getWidth();
// Compute the new height
newHeight = (int)(bmp.getHeight() * scaleFactor);
// Scale it to a new size
Assets.roach1 = Bitmap.createScaledBitmap (bmp, newWidth, newHeight, false);
// Delete the original
bmp = null;
// Load roach3 (dead bug)
bmp = BitmapFactory.decodeResource (context.getResources(), R.drawable.s3);
// Compute size of bitmap needed (suppose want width = 20% of screen width)
newWidth = (int)(canvas.getWidth() * 0.2f);
// What was the scaling factor to get to this?
scaleFactor = (float)newWidth / bmp.getWidth();
// Compute the new height
newHeight = (int)(bmp.getHeight() * scaleFactor);
// Scale it to a new size
Assets.roach3 = Bitmap.createScaledBitmap (bmp, newWidth, newHeight, false);
// Delete the original
bmp = null;
// Create a bug
Assets.bug = new Bug();
}
// Load specific background screen
private void loadBackground (Canvas canvas, int resId) {
// Load background
Bitmap bmp = BitmapFactory.decodeResource (context.getResources(), resId);
// Scale it to fill entire canvas
Assets.background = Bitmap.createScaledBitmap (bmp, canvas.getWidth(), canvas.getHeight(), false);
// Delete the original
bmp = null;
}
private void render (Canvas canvas) {
int i, x, y;
if (! data_initialized) {
loadData(canvas);
data_initialized = true;
}
switch (Assets.state) {
case GettingReady:
loadBackground (canvas, R.drawable.picnic);
// Draw the background screen
canvas.drawBitmap (Assets.background, 0, 0, null);
// Play a sound effect
Assets.soundPool.play(Assets.sound_getready, 1, 1, 1, 0, 1);
// Start a timer
Assets.gameTimer = System.nanoTime() / 1000000000f;
// Go to next state
Assets.state = Assets.GameState.Starting;
break;
case Starting:
// Draw the background screen
canvas.drawBitmap (Assets.background, 0, 0, null);
// Has 3 seconds elapsed?
float currentTime = System.nanoTime() / 1000000000f;
if (currentTime - Assets.gameTimer >= 3)
// Goto next state
Assets.state = Assets.GameState.Running;
break;
case Running:
// Draw the background screen
canvas.drawBitmap (Assets.background, 0, 0, null);
// Draw the score bar at top of screen
// ADD CODE HERE
// Draw the foodbar at bottom of screen
canvas.drawBitmap (Assets.foodbar, 0, canvas.getHeight()-Assets.foodbar.getHeight(), null);
// Draw one circle for each life at top right corner of screen
// Let circle radius be 5% of width of screen
int radius = (int)(canvas.getWidth() * 0.05f);
int spacing = 8; // spacing in between circles
x = canvas.getWidth() - radius - spacing; // coordinates for rightmost circle to draw
y = radius + spacing;
for (i=0; i<Assets.livesLeft; i++) {
paint.setColor(Color.GREEN);
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(x, y, radius, paint);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(x, y, radius, paint);
// Reposition to draw the next circle to the left
x -= (radius*2 + spacing);
}
// Process a touch
if (touched) {
// Set touch flag to false since we are processing this touch now
touched = false;
// See if this touch killed a bug
boolean bugKilled = Assets.bug.touched(canvas, touchx, touchy);
if (bugKilled)
Assets.soundPool.play(Assets.sound_squish, 1, 1, 1, 0, 1);
else
Assets.soundPool.play(Assets.sound_thump, 1, 1, 1, 0, 1);
}
// Draw dead bugs on screen
Assets.bug.drawDead(canvas);
// Move bugs on screen
Assets.bug.move(canvas);
// Bring a dead bug to life?
Assets.bug.birth(canvas);
// ADD MORE CODE HERE TO PLAY GAME
// Are no lives left?
if (Assets.livesLeft == 0)
// Goto next state
Assets.state = Assets.GameState.GameEnding;
break;
case GameEnding:
// Show a game over message
handler.post(new Runnable() {
public void run() {
Toast.makeText(context, "Game Over", Toast.LENGTH_SHORT).show();
}
});
// Goto next state
Assets.state = Assets.GameState.GameOver;
break;
case GameOver:
// Fill the entire canvas' bitmap with 'black'
canvas.drawColor(Color.BLACK);
break;
}
}
}
package com.example.cory2.bugsmasher;
import android.app.Activity;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;
public class MainActivity extends Activity {
MainView v;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Disable the title
//requestWindowFeature (Window.FEATURE_NO_TITLE); // use the styles.xml file to set no title bar
// Make full screen
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
// Start the view
v = new MainView(this);
setContentView(v);
}
@Override
protected void onPause () {
super.onPause();
v.pause();
}
@Override
protected void onResume () {
super.onResume();
v.resume();
}
public void onBackPressed(){
Intent other = new Intent(MainActivity.this, TitleActivity.class);
startActivity(other);
}
}