Simple game works on tablet but sometimes lags on phone - android

I'm developing simple Android game, but I'm running into problems while testing it. When I run it on Lenovo Tab3 7 tablet (Android 5.0.1 ) or LG P880 phone (Android 4.0.3) it works fine. When I run it on Samsung S7 phone (Android 7.0) game usually runs fine. What I mean by this is that I can run it 10 times in a row with no problems, but sometimes game halts for 5-30 seconds or stops responding. This usually happens during starting of new Activity or very shortly after it.
Game has 4 Activities which use extended SurfaceView as layout. All SurfaceViews implement Runnable. Activities are: Splash screen (noHistory = "true" in Manifest), Menu, Difficulty choice and Game.
I use only mdpi drawables and scale them proportionally to all screen sizes. Bitmaps are loaded using BitmapFactory.decodeResource with BitmapFactory.Options inDensity = 1, inScaled = false.
When the problem occurs logcat shows only garbage collection. Sometimes game "pauses" (no taps are registered) for 5-30 seconds and resumes normally, sometimes it has to be restarted due to no response. I feel like game stops collecting input for some reason. Input is handled by overriding onTouchEvent and checking if ACTION_UP is within tapped image bounds. As I said, this happens only on S7 (I tried it on two phones), not on tablet or P880, so I'm thinking it might be something to do with Nougat or me forcing lower density on the phone.
So, since I'm running out of ideas what could be causing this and me being new to Android game development, does anyone know/have any idea where I should be looking for solution? Is there anything Nougat-specific I should be setting/checking? Does forcing pixel density affect device performance in any way?
Edit 1
globalApp
public class globalApp extends Application {
SoundPool soundPool;
SoundPool.Builder soundPoolBuilder;
AudioAttributes audioAttributes;
AudioAttributes.Builder audioAttributesBuilder;
int soundTap, soundCorrect, soundIncorrect, soundVictory, soundDefeat;
int soundBarrelVerySlow, soundBarrelSlow, soundBarrelNormal, soundBarrelFast, soundBarrelVeryFast;
#Override
public void onCreate() {
super.onCreate();
}
public void buildSoundPool(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
audioAttributesBuilder = new AudioAttributes.Builder();
audioAttributesBuilder.setUsage(AudioAttributes.USAGE_GAME);
audioAttributesBuilder.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION);
audioAttributes = audioAttributesBuilder.build();
soundPoolBuilder = new SoundPool.Builder();
soundPoolBuilder.setMaxStreams(2);
soundPoolBuilder.setAudioAttributes(audioAttributes);
soundPool = soundPoolBuilder.build();
}
else {
soundPool = new SoundPool(1, AudioManager.STREAM_MUSIC, 0);
}
}
public void loadSounds(){
soundBarrelVerySlow = soundPool.load(this,R.raw.very_slow_move, 1);
soundBarrelSlow = soundPool.load(this, R.raw.slow_move, 1);
soundBarrelNormal = soundPool.load(this, R.raw.slow_move, 1);
soundBarrelFast = soundPool.load(this,R.raw.fast_move, 1);
soundBarrelVeryFast = soundPool.load(this,R.raw.very_fast_move, 1);
soundTap = soundPool.load(this, R.raw.tap_sound, 1);
soundCorrect = soundPool.load(this, R.raw.correct, 1);
soundIncorrect = soundPool.load(this, R.raw.incorrect, 1);
soundVictory = soundPool.load(this, R.raw.victory, 1);
soundDefeat = soundPool.load(this, R.raw.defeat, 1);
}
public void playTap(){
soundPool.play(soundTap, 1, 1,1, 0, 1);
}
public void playCorrect(){
soundPool.play(soundCorrect, 1, 1,1, 0, 1);
}
public void playIncorrect(){
soundPool.play(soundIncorrect, 1, 1,1, 0, 1);
}
public void playVictory(){
soundPool.play(soundVictory, 1, 1,1, 0, 1);
}
public void playDefeat(){
soundPool.play(soundDefeat, 1, 1,1, 0, 1);
}
public void playBarrelVerySlow(){soundPool.play(soundBarrelVerySlow, 1, 1, 1, 0, 1);}
public void playBarrelSlow(){soundPool.play(soundBarrelSlow, 1, 1, 1, 0, 1);}
public void playBarrelNormal(){
soundPool.play(soundBarrelNormal, 1, 1,1, 0, 1);
}
public void playBarrelFast(){soundPool.play(soundBarrelFast, 1, 1, 1, 0, 1);}
public void playBarrelVeryFast(){soundPool.play(soundBarrelVeryFast, 1, 1, 1, 0, 1);}
}
MenuItem
public class MenuItem {
private Bitmap bmp;
private Context context;
private Rect sourceRect;
private RectF destRect;
private int srcWidth;
private int srcHeight;
private int destW, destH;
private int x, y;
private int screenH;
public MenuItem(Context ctx, String bmpName, int w, int x, int y, int sX, int sY){
context = ctx;
BitmapFactory.Options bmpFOptions = new BitmapFactory.Options();
bmpFOptions.inDensity = 1;
bmpFOptions.inScaled = false;
int res = context.getResources().getIdentifier(bmpName, "drawable", ctx.getPackageName());
bmp = BitmapFactory.decodeResource(ctx.getResources(), res, bmpFOptions);
srcWidth = w;
srcHeight = bmp.getHeight();
this.x = x;
this.y = y;
screenH = sY;
sourceRect = new Rect(0,0, srcWidth, srcHeight);
destRect = new RectF();
setProportionalDestinationRect(sX, sY);
}
private void setProportionalDestinationRect(int scrX, int scrY) {
if (scrX != 1024 || scrY != 552){
float propX = (float)scrX/1024;
float propY = (float)scrY/600;
// All drawables are designed for 1024x600 screen
// if device screen is different, scale image proportionally
destW = (int)(srcWidth * propX);
destH = (int) (srcHeight * propY);
x = (int) (x*propX);
y = (int) (y*propY);
}
else {
destW = srcWidth;
destH = srcHeight;
}
destRect.set(x,y, x+destW,y+destH);
}
public void update(){
}
public Bitmap getBmp() {
return bmp;
}
public void setBmp(Bitmap bmp) {
this.bmp = bmp;
}
public Rect getSourceRect() {
return sourceRect;
}
public void setSourceRect(Rect sourceRect) {
this.sourceRect = sourceRect;
}
public RectF getDestRect() {
return destRect;
}
public void setDestRect(RectF destRect) {
this.destRect = destRect;
}
public boolean contains(int x, int y){
if (destRect.left <= x && destRect.right >= x)
if (destRect.top <= y && destRect.bottom >= y)
return true;
return false;
}
public void setY(int y) {
this.y = y;
if (screenH != 552){
float propY = (float)screenH/600;
y = (int) (y*propY);
}
destRect.set(x,y, x+destW,y+destH);
}
}
MainActivity
public class MainActivity extends Activity {
private boolean backPressedOnce = false;
long backPressedTime = 0;
private MainActivitySurface mainActivitySurface;
globalApp app;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Setting full screen
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
View decorView = getWindow().getDecorView();
int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
decorView.setSystemUiVisibility(uiOptions);
int x = getIntent().getIntExtra("screenWidth", 500);
int y = getIntent().getIntExtra("screenHeight", 500);
app = (globalApp) getApplication();
app.buildSoundPool();
app.loadSounds();
mainActivitySurface = new MainActivitySurface(this, app, x, y);
mainActivitySurface.setParentActivity(MainActivity.this);
setContentView(mainActivitySurface);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 1001) {
if (resultCode == RESULT_OK) {
int result = data.getIntExtra("difficulty", 3);
mainActivitySurface.setResultDifficulty(result);
}
}
}
#Override
protected void onPause() {
super.onPause();
mainActivitySurface.pause();
}
#Override
protected void onResume() {
super.onResume();
backPressedOnce = false;
mainActivitySurface.resume();
}
#Override
public void onBackPressed() {
if (backPressedOnce && backPressedTime + 2000 > System.currentTimeMillis()) {
Process.killProcess(Process.myPid());
System.exit(1);
} else {
Toast.makeText(this, "Press back again to exit.", Toast.LENGTH_SHORT).show();
backPressedOnce = true;
}
backPressedTime = System.currentTimeMillis();
}
}
MainActivitySurface
public class MainActivitySurface extends SurfaceView implements Runnable {
private Context context;
private SurfaceHolder surfaceHolder;
private Canvas canvas;
private Thread thread = null;
volatile private boolean running = false;
private boolean surfaceCreated = false;
private Intent playIntent;
private Intent difficultyIntent;
// Screen size
private int screenWidth, screenHeight;
//Menu items
private MenuItem menuItemPlay, menuItemDifficulty, middleBarrel, bg;
private int difficulty = 3;
private Activity parentActivity;
private globalApp app;
public MainActivitySurface(Context ctx, globalApp a, int scrW, int scrH){
super(ctx);
context = ctx;
screenHeight = scrH;
screenWidth = scrW;
app = a;
surfaceHolder = getHolder();
surfaceHolder.addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceCreated(SurfaceHolder holder) {
surfaceCreated = true;
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
});
bg = new MenuItem(context, "main_activity_background_single", 1024, 0, 0, scrW, scrH);
menuItemPlay = new MenuItem(context, "menu_item_play_single", 233,(1024-233)/2,100, scrW, scrH);
menuItemDifficulty = new MenuItem(ctx, "menu_item_difficulty_single", 520,(1024 - 520)/2,400,scrW,scrH);
middleBarrel = new MenuItem(ctx, "middle_barrel_single", 323,(1024-323)/2,200,scrW,scrH);
playIntent = new Intent(context, GameActivity.class);
playIntent.putExtra("screenWidth", screenWidth);
playIntent.putExtra("screenHeight", screenHeight);
}
#Override
public void run() {
while (running){
draw();
}
}
private void draw() {
if(surfaceHolder.getSurface().isValid()){
canvas = surfaceHolder.lockCanvas();
canvas.drawBitmap(bg.getBmp(), bg.getSourceRect(), bg.getDestRect(), null);
canvas.drawBitmap(menuItemPlay.getBmp(), menuItemPlay.getSourceRect(), menuItemPlay.getDestRect(), null);
canvas.drawBitmap(menuItemDifficulty.getBmp(), menuItemDifficulty.getSourceRect(), menuItemDifficulty.getDestRect(), null);
canvas.drawBitmap(middleBarrel.getBmp(), middleBarrel.getSourceRect(), middleBarrel.getDestRect(), null);
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
public void resume(){
running = true;
thread = new Thread(this);
thread.start();
}
public void pause(){
running = false;
boolean retry = false;
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
e.printStackTrace();
Log.d("info", "MainActivitySurface: Error joining thread");
}
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction() & event.ACTION_MASK){
case MotionEvent.ACTION_UP:
if (menuItemPlay.contains((int) event.getX(), (int) event.getY())){
app.playTap();
parentActivity.startActivity(playIntent);
parentActivity.overridePendingTransition(0,0);
break;
}
if (menuItemDifficulty.contains((int) event.getX(), (int) event.getY())){
app.playTap();
difficultyIntent = new Intent(parentActivity, DifficultyActivity.class);
difficultyIntent.putExtra("screenWidth", screenWidth);
difficultyIntent.putExtra("screenHeight", screenHeight);
difficultyIntent.putExtra("difficulty", difficulty);
parentActivity.startActivityForResult(difficultyIntent, 1001);
parentActivity.overridePendingTransition(0, 0);
break;
}
}
return true;
}
public void setParentActivity(Activity act){
parentActivity = act;
}
public void setResultDifficulty(int diff){
difficulty = diff;
playIntent.putExtra("difficulty", difficulty);
}
}
DifficultyActivity
public class DifficultyActivity extends Activity {
private DifficultySurface surface;
private globalApp app;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Setting full screen
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
View decorView = getWindow().getDecorView();
int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
decorView.setSystemUiVisibility(uiOptions);
app = (globalApp) getApplication();
surface = new DifficultySurface(this, app, getIntent().getIntExtra("screenWidth", 500), getIntent().getIntExtra("screenHeight", 500));
setContentView(surface);
}
#Override
protected void onPause() {
super.onPause();
app.soundPool.release();
surface.pause();
overridePendingTransition(0, 0);
}
#Override
protected void onResume() {
super.onResume();
app.buildSoundPool();
app.loadSounds();
surface.resume();
}
}
DifficultySurface
public class DifficultySurface extends SurfaceView implements Runnable {
private SurfaceHolder surfaceHolder;
private Thread thread = null;
private Canvas canvas;
private Context context;
private globalApp app;
private boolean surfaceCreated = false;
private boolean running = false;
private MenuItem bgProp, arrowBarrel, okButton, diffVeryEasy, diffEasy, diffNormal, diffHard, diffVeryHard;
private int difficulty;
public DifficultySurface(Context ctx, globalApp a, int scrW, int scrH){
super(ctx);
context = ctx;
app = a;
surfaceHolder = getHolder();
surfaceHolder.addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceCreated(SurfaceHolder holder) {
surfaceCreated = true;
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
});
difficulty = ((Activity)context).getIntent().getIntExtra("difficulty", 3);
bgProp = new MenuItem(ctx, "difficulty_background", 1024, 0, 0, scrW, scrH);
diffVeryEasy = new MenuItem(ctx, "very_easy",796, 100, 100, scrW, scrH);
diffEasy = new MenuItem(ctx, "easy",796, 100, 200 , scrW, scrH);
diffNormal = new MenuItem(ctx, "normal",796, 100, 300, scrW, scrH);
diffHard = new MenuItem(ctx, "hard",796, 100, 400 , scrW, scrH);
diffVeryHard = new MenuItem(ctx, "very_hard",796, 100, 500, scrW, scrH);
okButton = new MenuItem(ctx, "ok_button", 100, 924, 500, scrW, scrH);
arrowBarrel = new MenuItem(ctx, "barrel_arrow", 100, 0, 100*difficulty, scrW, scrH);
}
#Override
public void run() {
while (running) {
if (surfaceCreated) {
update();
draw();
}
}
}
private void update() {
arrowBarrel.setY(difficulty*100);
}
private void draw() {
if (surfaceHolder.getSurface().isValid()){
canvas = surfaceHolder.lockCanvas();
canvas.drawBitmap(bgProp.getBmp(), bgProp.getSourceRect(), bgProp.getDestRect(), null);
canvas.drawBitmap(arrowBarrel.getBmp(), arrowBarrel.getSourceRect(), arrowBarrel.getDestRect(), null);
canvas.drawBitmap(diffVeryEasy.getBmp(), diffVeryEasy.getSourceRect(), diffVeryEasy.getDestRect(), null);
canvas.drawBitmap(diffEasy.getBmp(), diffEasy.getSourceRect(), diffEasy.getDestRect(), null);
canvas.drawBitmap(diffNormal.getBmp(), diffNormal.getSourceRect(), diffNormal.getDestRect(), null);
canvas.drawBitmap(diffHard.getBmp(), diffHard.getSourceRect(), diffHard.getDestRect(), null);
canvas.drawBitmap(diffVeryHard.getBmp(), diffVeryHard.getSourceRect(), diffVeryHard.getDestRect(), null);
canvas.drawBitmap(okButton.getBmp(), okButton.getSourceRect(), okButton.getDestRect(), null);
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction() & event.ACTION_MASK){
case MotionEvent.ACTION_UP:{
if (diffVeryEasy.contains((int) event.getX(), (int) event.getY())){
app.playTap();
difficulty = 1; }
if (diffEasy.contains((int) event.getX(), (int) event.getY())){
app.playTap();
difficulty = 2;
}
if (diffNormal.contains((int) event.getX(), (int) event.getY())){
app.playTap();
difficulty = 3;
}
if (diffHard.contains((int) event.getX(), (int) event.getY())){
app.playTap();
difficulty = 4;
}
if (diffVeryHard.contains((int) event.getX(), (int) event.getY())){
app.playTap();
difficulty = 5;
}
if (okButton.contains((int)event.getX(), (int) event.getY())){
app.playTap();
((Activity)context).getIntent().putExtra("difficulty", difficulty);
((Activity)context).setResult(Activity.RESULT_OK, ((Activity)context).getIntent());
((Activity)context).finish();
((Activity)context).overridePendingTransition(0, 0);
}
break;
}
}
return true;
}
public void pause(){
running = false;
boolean retry = true;
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
((Activity)context).overridePendingTransition(0, 0);
}
public void resume(){
running = true;
thread = new Thread(this);
thread.start();
}
}
GameActivity
public class GameActivity extends Activity {
private GameSurface surface;
private globalApp app;
private int difficulty;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Setting full screen
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
View decorView = getWindow().getDecorView();
int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
decorView.setSystemUiVisibility(uiOptions);
difficulty = getIntent().getIntExtra("difficulty", 3);
app = (globalApp) getApplication();
surface = new GameSurface(this, app, getIntent().getIntExtra("screenWidth", 500), getIntent().getIntExtra("screenHeight", 500), difficulty);
surface.setParentActivity(this);
setContentView(surface);
}
#Override
protected void onPause() {
super.onPause();
app.soundPool.release();
surface.pause();
}
#Override
protected void onPostResume() {
super.onPostResume();
app.buildSoundPool();
app.loadSounds();
surface.resume();
}
#Override
protected void onStop() {
super.onStop();
surface.stop();
}
#Override
public void onBackPressed() {
super.onBackPressed();
finish();
}
}
Game halting happens either when I start DificultyActivity (I tap one MenuItem objects but nothing happens) or when I start GameActivity (game still shows MainActivity + MainActivitySurface).
Android Monitor show less than 40MB of allocated memory, so bitmaps shouldn't be the problem in my opinion. I tried recycling all bitmaps but the problem was present (that's why I opted to use only mdpi drawables; at first I used all pixel densities but tried lowering resources in case that was causing halts).

It is hard to find the problem without looking at the code. There's nothing nougat-specific way of handling the resources.
But android N claims to have a better memory management and since you are complaining a lot of garbage collections, it may be one of the cause. Make sure to recycle the unused bitmaps. And use RGB_565 as the preferred bitmap config which requires half memory than RGB_8888.

I have solved my problem. After posting question I came across this. It seems we had the same problem. When I slowed down drawing speed (using thread.sleep) there were no more issues.
Thanks to those who helped me.

Related

Android app crashes upon attempt to switch to a different Activity

I am running into an issue in my app. When my game ends (when life == 0) I am attempting to switch to a game over screen by using a different activity. When the game ends, the app simply crashes. I have included the XML for the activity I am trying to switch from as well as indicating where the app crashes. If anyone could help out, that would be great! Thanks.
activity_game.XML:
SurfaceView I am trying to switch from once game ends:
public class SVGameView extends SurfaceView implements Runnable {
private SurfaceHolder holder;
Thread thread = null;
volatile boolean running = false;
static final long FPS = 30;
private Sprite sprite;
private long lastClick;
private Bitmap ball, gameOver;
//private int x = 200, y = 200;
private int scorePosX = 100;
private int scorePosY = 100;
private int countScore = 0;
private int life = 1;
public SVGameView(Context context) {
super(context);
thread = new Thread(this);
holder = getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
running = false;
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
running = true;
thread.start();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
}
});
ball = BitmapFactory.decodeResource(getResources(), R.drawable.ball2);
gameOver = BitmapFactory.decodeResource(getResources(),R.drawable.endscreen);
sprite = new Sprite(this, ball);
}
#Override
public void run() {
long ticksPS = 1000 / FPS;
long startTime;
long sleepTime;
while (running) {
Canvas c = null;
startTime = System.currentTimeMillis();
try {
c = getHolder().lockCanvas();
synchronized (getHolder()) {
update();
draw(c);
}
} finally {
if (c != null) {
getHolder().unlockCanvasAndPost(c);
}
}
sleepTime = ticksPS-(System.currentTimeMillis() - startTime);
try {
if (sleepTime > 0)
thread.sleep(sleepTime);
else
thread.sleep(10);
} catch (Exception e) {}
}
}
private void update(){
sprite.update();
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.WHITE);
Paint paint = new Paint();
canvas.drawPaint(paint);
paint.setColor(Color.WHITE);
paint.setTextSize(48);
canvas.drawText("Score: " + countScore, scorePosX, scorePosY, paint);
canvas.drawText("Lives: " + life, 500, 100, paint);
sprite.onDraw(canvas);
//Crashes here
if(life == 0) {
getContext().startActivity(new Intent(getContext(), SVGameOver.class));
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if(System.currentTimeMillis()-lastClick > 300){
lastClick = System.currentTimeMillis();
}
synchronized (getHolder()){
if(sprite.isHit(event.getX(), event.getY())){
countScore += 1;
sprite.increase();
}else{
life --;
}
}
return super.onTouchEvent(event);
}
}
Activity I am trying to reach once the game ends:
public class SVGameOver extends Activity {
private Bitmap gameOverScreen;
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game);
gameOverScreen = BitmapFactory.decodeResource(getResources(), R.drawable.endscreen);
}
protected void onDraw(Canvas canvas){
canvas.drawBitmap(gameOverScreen, 0,0,null);
}
}
I think logcat is asking you the right question: "have you declared this activity in your AndroidManifest.xml" ?
If you think you did it, It's highly probable you did it in a wrong way, most of the times that you think you added an Activity to the manifest but you are receiving this kind of crash, 99,9% of the time you declared it with a wrong namespace
Declare SVGameOver activity in your AndroidManifest.xml:
<activity
android:name="com.example.welcome.assignment2.SVGameOver">
...
</activity>

Game music doesnt stop in my android game when user press back button

In my android game when user press back button music is not stopped even when user close the game music still playing.
This is my main class for game. MediaPlayer mp1 is used to play music.
public class GameView extends SurfaceView{
Bitmap bmp,pause;
Bitmap background,kinfe,note1,appleimg,note2;
Bitmap run1;
Bitmap run2;
Bitmap run3;
Bitmap coin;
Bitmap exit;
private SurfaceHolder holder;
private int x = 0,y=0,z=0,delay=0,getx,gety,sound=1;
int show=0,sx,sy;
int cspeed=0,kspeed=0,gameover=0;
int score=0,health=100,reset=0;
int pausecount=0,volume,power=0,applerun=0,shieldrun=0;
#SuppressWarnings("deprecation")
#SuppressLint("NewApi")
public GameView(Context context)
{
super(context);
gameLoopThread = new GameLoop(this);
holder = getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
#SuppressWarnings("deprecation")
#Override
public void surfaceDestroyed(SurfaceHolder holder)
{
//for stoping the game
gameLoopThread.setRunning(false);
gameLoopThread.getThreadGroup().interrupt();
}
#SuppressLint("WrongCall")
#Override
public void surfaceCreated(SurfaceHolder holder)
{
//for starting the game
gameLoopThread.setRunning(true);
gameLoopThread.start();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format,int width, int height)
{
}
});
//getting the screen size
// Display display = getWindowManager().getDefaultDisplay();
// sx = display.getWidth();
// sy = display.getHeight();;
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
sx = size.x;
sy = size.y;
cspeed=sx/2;
kspeed=sx/2;
applerun=(3*sx/4);
shieldrun=sx/8;
background = BitmapFactory.decodeResource(getResources(), R.drawable.cold);
run1=BitmapFactory.decodeResource(getResources(), R.drawable.run1);
run2=BitmapFactory.decodeResource(getResources(), R.drawable.run2);
run3=BitmapFactory.decodeResource(getResources(), R.drawable.run3);
coin=BitmapFactory.decodeResource(getResources(), R.drawable.coin);
exit=BitmapFactory.decodeResource(getResources(), R.drawable.exit);
kinfe=BitmapFactory.decodeResource(getResources(), R.drawable.kinfe);
note1=BitmapFactory.decodeResource(getResources(), R.drawable.note1);
pause=BitmapFactory.decodeResource(getResources(), R.drawable.pause);
appleimg=BitmapFactory.decodeResource(getResources(), R.drawable.power);
note2=BitmapFactory.decodeResource(getResources(), R.drawable.note2);
exit=Bitmap.createScaledBitmap(exit, 25,25, true);
pause=Bitmap.createScaledBitmap(pause, 25,25, true);
appleimg=Bitmap.createScaledBitmap(appleimg, 25,25, true);
note2=Bitmap.createScaledBitmap(note2, sx,sy, true);
run1=Bitmap.createScaledBitmap(run1, sx/9,sy/7, true);
run2=Bitmap.createScaledBitmap(run2, sx/9,sy/7, true);
run3=Bitmap.createScaledBitmap(run3, sx/9,sy/7, true);
coin=Bitmap.createScaledBitmap(coin, sx/16,sy/24, true);
background=Bitmap.createScaledBitmap(background, 2*sx,sy, true);
//health dec
note1=Bitmap.createScaledBitmap(note1, sx,sy, true);
mp1=MediaPlayer.create(Game.this,R.raw.game);
jump=MediaPlayer.create(Game.this,R.raw.jump);
takecoin=MediaPlayer.create(Game.this,R.raw.cointake);
}
// on touch method
#Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction()==MotionEvent.ACTION_DOWN)
{
show=1;
getx=(int) event.getX();
gety=(int) event.getY();
//exit
if(getx<25&&gety<25)
{
//high score
SharedPreferences pref = getApplicationContext().getSharedPreferences("higher", MODE_PRIVATE);
Editor editor = pref.edit();
editor.putInt("score", score);
editor.commit();
System.exit(0);
}
// restart game
if(getx>91&&gety<25)
{
if(health<=0)
{
gameLoopThread.setPause(0);
health=100;
score=0;
}
}
//pause game
if((getx>(sx-25)&&gety<25&&pausecount==0))
{
gameLoopThread.setPause(1);
mp1.stop();
pausecount=1;
}
else if(getx>(sx-25)&&gety<25&&pausecount==1)
{
gameLoopThread.setPause(0);
mp1.start();
pausecount=0;
}
}
return true;
}
#SuppressLint("WrongCall")
#Override
protected void onDraw(Canvas canvas)
{
//volume
SharedPreferences pref = getApplicationContext().getSharedPreferences("higher", MODE_PRIVATE);
Editor editor = pref.edit();
volume=pref.getInt("vloume", 0);
if(volume==0)
{
sound=0;
}
canvas.drawColor(Color.BLACK);
//background moving
z=z-10;
if(z==-sx)
{
z=0;
canvas.drawBitmap(background, z, 0, null);
}
else
{
canvas.drawBitmap(background, z, 0, null);
}
//running player
x+=5;
if(x==20)
{
x=5;
}
if(show==0)
{
if(x%2==0)
{
canvas.drawBitmap(run3, sx/16, 15*sy/18, null);
}
else
{
canvas.drawBitmap(run1, sx/16, 15*sy/18, null);
}
//kinfe hit
if(kspeed==20)
{
kspeed=sx;
health-=25;
canvas.drawBitmap(note1, 0, 0, null);
}
//power take
if(applerun==30)
{
applerun=3*sx;
health+=25;
canvas.drawBitmap(note2, 0, 0, null);
}
}
//power
applerun=applerun-10;
canvas.drawBitmap(appleimg, applerun, 15*sy/18, null);
if(applerun<0)
{
applerun=3*sx/4;
}
//kinfe
kspeed=kspeed-20;
canvas.drawBitmap(kinfe, kspeed, 15*sy/18, null);
if(kspeed<0)
{
kspeed=sx;
}
// for jump
if(show==1)
{
if(sound==1)
{
jump.start();
}
canvas.drawBitmap(run2, sx/16, 4*sy/7, null);
//score
if(cspeed<=sx/8&&cspeed>=sx/16)
{
if(sound==1)
{
takecoin.start();
}
cspeed=sx/2;
score+=10;
}
// jump-hold
delay+=1;
if(delay==3)
{
show=0;
delay=0;
}
}
//for coins
cspeed=cspeed-5;
if(cspeed==-sx/2)
{
cspeed=sx/2;
canvas.drawBitmap(coin, cspeed, 3*sy/4, null);
}
else
{
canvas.drawBitmap(coin, cspeed, 3*sy/4, null);
}
//score
Paint paint = new Paint();
paint.setColor(Color.BLUE);
paint.setAntiAlias(true);
paint.setFakeBoldText(true);
paint.setTextSize(25);
paint.setTextAlign(Align.LEFT);
canvas.drawText("Score :"+score, 3*sx/4, 20, paint);
//exit
canvas.drawBitmap(exit, 0, 0, null);
if(sound==1)
{
mp1.start();
mp1.setLooping(true);
}
else
{
mp1.stop();
}
//health
Paint myPaint = new Paint();
myPaint.setColor(Color.RED);
myPaint.setStrokeWidth(10);
myPaint.setAntiAlias(true);
myPaint.setFakeBoldText(true);
paint.setTextSize(25);
canvas.drawText("Health :"+health, 0, (sy/8)-5, myPaint);
canvas.drawRect(0, sy/8, health, sy/8+10, myPaint);
//game over
if(health<=0)
{
gameover=1;
mp1.stop();
sound=0;
//high score
editor.putInt("score", score);
editor.commit();
myPaint.setColor(Color.BLACK);
myPaint.setFakeBoldText(true);
canvas.drawText("GAME OVER", sx/2, sy/2, myPaint);
canvas.drawText("YOUR SCORE : "+score, sx/2, sy/4, myPaint);
canvas.drawText("RESTART", 91, 25, myPaint);
gameLoopThread.setPause(1);
canvas.drawBitmap(background, sx, sy, null);
}
// restart
if(reset==1)
{
gameLoopThread.setPause(0);
health=100;
score=0;
mp1.start();
}
canvas.drawBitmap(pause, (sx-25), 0, null);
}
}
And this is the loop class for game.
public class GameLoop extends Thread {
private GameView view;
static final long FPS = 10;
private boolean running = false;
boolean isPaused;
public GameLoop(GameView view) {
this.view = view;
}
public void setRunning(boolean run) {
running = run;
}
public void setPause(int i)
{
synchronized (view.getHolder())
{
if(i==0)
{
isPaused=false;
}
if(i==1)
{
isPaused = true;
}
}
}
#SuppressLint("WrongCall")
#Override
public void run() {
long ticksPS = 100;
long startTime = 0;
long sleepTime;
while (running) {
//pause and resume
if (isPaused)
{
try
{
this.sleep(50);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
else
{
Canvas c = null;
startTime = System.currentTimeMillis();
try {
c = view.getHolder().lockCanvas();
synchronized (view.getHolder())
{
view.onDraw(c);
}
}
finally
{
if (c != null)
{
view.getHolder().unlockCanvasAndPost(c);
}
}
}
sleepTime = ticksPS-(System.currentTimeMillis() - startTime);
try {
if (sleepTime > 0)
sleep(sleepTime);
else
sleep(10);
}
catch (Exception e) {}
}
}}
I got the solution as I put my class GameView extends SurfaceView inside another class Game extends Activity that enables me to use onBackPressed() method .
#Override
public void onBackPressed()
{
mp1.stop();
Intent i=new Intent(this,MainActivity.class);
startActivity(i);
}
Thank you everyone for your best efforts to solve my problem.
You need stop the music manually, you can use in onStop() or onDestroy() for call mp1.stop(), or in onBackPressed().
or try use:
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
mp1.stop();
return super.onKeyDown(keyCode, event);
}
Subclassing SurfaceView, and overriding SurfaceView.onDraw(), rarely make sense.
Things go more smoothly if you structure your code as an Activity that has-a SurfaceView, because you need to handle onPause() and onResume(), and work in the handling of the Surface state callbacks. You need to provide the standard Android lifecycle methods if you want the app to work correctly when switching away and back, turning the device off and on with the power button, and so on.
The overridden onDraw() method draws on the View part of the SurfaceView, rather than the Surface, so unless you want an overlay it doesn't make sense. You may not notice a problem because it only gets called by the View hierarchy when something invalidates the View. All you need to do is change onDraw() to doDraw() and remove the #Override, so that it's only invoked when you invoke it explicitly. (If you do want to override onDraw(), then you should probably be using a custom view, rather than a SurfaceView.)
This article explains the interaction between the Activity and SurfaceView lifecycles. Grafika provides several examples of SurfaceView code. For example, the "multi-surface test" provides an example of an Activity using Canvas rendering to a SurfaceView (actually, more than one).

Is this the right way to detect a touch on a rectangle in LibGdx ? Does not seem to be working for me

This is the code for my gamescreen where i want burst my ballon when its touched .
orientation is portrait.
but it does not seem to work for me.
public class GameScreen implements Screen {
final BB game;
private BitmapFont font;
private static final int no_of_frames = 2;
Texture ballonFrames;
TextureRegion[] burstFrames = new TextureRegion[no_of_frames];
Animation burstAnimation;
Array<Rectangle> ballons;
TextureRegion currentFrame;
long lastBallonTime;
int ballonBursted;
OrthographicCamera camera;
int ballonMissed;
Sound ballonBursting;
public GameScreen(final BB gam) {
this.game = gam;
ballonFrames = new Texture(Gdx.files.internal("ballon_burst.png"));
font = new BitmapFont(Gdx.files.internal("font.fnt"), false);
ballonBursting = Gdx.audio.newSound(Gdx.files
.internal("BallonBursting.wav"));
TextureRegion[][] tmp = TextureRegion.split(ballonFrames,
ballonFrames.getWidth() / 2, ballonFrames.getHeight());
burstFrames[0] = tmp[0][0];
burstFrames[1] = tmp[0][1];
burstAnimation = new Animation(3.0f, burstFrames);
camera = new OrthographicCamera();
camera.setToOrtho(false, 800, 480);
ballons = new Array<Rectangle>();
spawnBallon();
}
private void spawnBallon() {
Rectangle ballon = new Rectangle();
ballon.x = MathUtils.random(0, 800 - 64); //
ballon.y = 0;
ballon.width = 40;
ballon.height = 80;
ballons.add(ballon);
lastBallonTime = TimeUtils.nanoTime();
}
private boolean ballonBursted(Rectangle ballon) {
Vector2 touch = new Vector2(Gdx.input.getX(), Gdx.input.getY());
if (ballon.contains(touch))
return true;
else
return false;
}
#Override
public void render(float delta) {
// TODO Auto-generated method stub
Gdx.gl.glClearColor(0, 0, 0.3f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
InputProcessor processor;
camera.update();
game.batch.setProjectionMatrix(camera.combined);
game.batch.begin();
font.draw(game.batch, "Ballon Bursted :" + ballonBursted, 0, 700);
font.draw(game.batch, "Ballon Missed:" + ballonMissed, 275, 700);
for (Rectangle ballon : ballons) {
game.batch.draw(burstFrames[0], ballon.x, ballon.y);
}
if (TimeUtils.nanoTime() - lastBallonTime > 1000000000) {
spawnBallon(); // a ballon every second
}
Iterator<Rectangle> iter = ballons.iterator();
while (iter.hasNext()) {
Rectangle ballon = iter.next();
ballon.y = ballon.y + 100 * Gdx.graphics.getDeltaTime();
if (ballonBursted(ballon) == true) {
ballonBursted++;
game.batch.draw(burstFrames[1], ballon.x, ballon.y);
ballonBursting.play();
iter.remove();
}
else if (ballon.y + 64 > 800) {
iter.remove();
ballonMissed++;
}
}
if (ballonMissed > 5) {
game.setScreen(new ScoreScreen(game, ballonBursted));
}
game.batch.end();
}
#Override
public void resize(int width, int height) {
}
#Override
public void show() {
}
#Override
public void hide() {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void dispose() {
ballonFrames.dispose();
ballonBursting.dispose();
game.batch.dispose();
}
I am using animation class of libgdx to change my image of ballon to the one where its bursted .
I am fairly new to libgdx and unable to figure out what wrong am i doing here .
Should i create a table and layout my ballon elements as actor?
Try something like this:
private boolean ballonBursted(Rectangle ballon) {
Vector3 touchPos = new Vector3(Gdx.input.getX(), Gdx.input.getY(), 0);
camera.unproject(touchPos);
if (ballon.contains(touchPos.x, touchPos.y))
return true;
else
return false;
}
please read this https://stackoverflow.com/a/18555705/2158970
If I understand it right you just want to know if the touchpoint is contained in the Rectangle ballon. Then you could use Rectangle#contains() method:
ballon.contains(Gdx.input.getX(), Gdx.input.getY());
see also the source code of Rectangle class

Move SurfaceView in Android

I'm developing a simple game like BSD robots. This game contains a rather large board (over 40 cells) and it doesn't look nice even at 10 inch tablet. My decision was to scroll (move) surface view and not to scale each figure which size is like finger spot.
I've read tons articles about surface view but I cannot understand how to move it?
My activity code:
package ru.onyanov.robots;
public class MainActivity extends Activity {
public BoardView board;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
requestWindowFeature(Window.FEATURE_NO_TITLE);
board = new BoardView(this);
setContentView(board);
}
}
Class Board:
package ru.onyanov.robots;
public class BoardView extends SurfaceView {
private GameThread mThread;
private boolean running = false;
public final int sizeX = 50;
public final int sizeY = 35;
public final int cellSize = 64;
private int robotCount = 4;
public ArrayList<Cell> cells = new ArrayList<Cell>();
private ArrayList<Robot> robots = new ArrayList<Robot>();
private Hero hero;
public Bitmap imageCell;
public Bitmap imageRobot;
public Bitmap imageHero;
public BoardView(Context context) {
super(context);
makeGraphic();
constructCells();
constructRobots();
hero = new Hero(this, 3, 2, imageHero);
mThread = new GameThread(this);
getHolder().addCallback(new SurfaceHolder.Callback() {
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
mThread.setRunning(false);
while (retry) {
try {
mThread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
public void surfaceCreated(SurfaceHolder holder) {
mThread.setRunning(true);
mThread.start();
}
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
}
});
}
private void makeGraphic() {
Bitmap cellImageSource = BitmapFactory.decodeResource(getResources(),
R.drawable.cell);
imageCell = Bitmap.createScaledBitmap(cellImageSource, cellSize,
cellSize, false);
imageRobot = BitmapFactory.decodeResource(getResources(),
R.drawable.robot);
imageHero = BitmapFactory.decodeResource(getResources(),
R.drawable.hero);
}
private void constructRobots() {
Robot robot;
Random rand = new Random();
for (int r = 0; r < robotCount; r++) {
int x = rand.nextInt(sizeX);
int y = rand.nextInt(sizeY);
robot = new Robot(this, x, y, imageRobot);
robots.add(robot);
}
return;
}
private void constructCells() {
Cell cell;
for (int y = 0; y < sizeY; y++) {
for (int x = 0; x < sizeX; x++) {
cell = new Cell(this, x, y, imageCell);
cells.add(cell);
}
}
return;
}
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.WHITE);
Cell cell;
for (int c = 0; c < cells.size(); c++) {
cell = cells.get(c);
cell.onDraw(canvas);
}
Robot robot;
for (int r = 0; r < robots.size(); r++) {
robot = robots.get(r);
robot.onDraw(canvas);
}
hero.onDraw(canvas);
}
public boolean onTouchEvent(MotionEvent e) {
int shotX = (int) e.getX();
int shotY = (int) e.getY();
if (e.getAction() == MotionEvent.ACTION_MOVE){
//TODO move board
showToast("move Board");
} else if (e.getAction() == MotionEvent.ACTION_UP) {
showToast("touchPoint: " + shotX + ", "+shotY);
hero.moveByDirection(shotX, shotY);
this.scrollTo(shotX, shotY);
}
return true;
}
public void showToast(String mes) {
Toast toast = Toast.makeText(getContext(), mes, Toast.LENGTH_LONG);
toast.show();
}
public class GameThread extends Thread {
private BoardView view;
public GameThread(BoardView view) {
this.view = view;
}
public void setRunning(boolean run) {
running = run;
}
public void run() {
while (running) {
Canvas canvas = null;
try {
canvas = view.getHolder().lockCanvas();
synchronized (view.getHolder()) {
onDraw(canvas);
}
canvas.scale(300, 300);
} catch (Exception e) {
} finally {
if (canvas != null) {
view.getHolder().unlockCanvasAndPost(canvas);
}
}
}
}
}
}
Some magic digits here are temporary. Now I just want to move BoardView;
The issue was to add x and y fields to SurfaceView, change them at onTouchEvent and draw all the elements in canvas with x- and y-offset. It's strange that I didn't recieved any answers...

Animations in Android

I'm new in Android and I want to do some animations. I'm trying to make my sprite sheet move automatically. But there is a problem with screen rendering. It leaves a trail while it is moving.Click here to see the screen shot
This is my code:
public class SampleAnimationActivity extends Activity {
/** Called when the activity is first created. */
Screen screen;
MapAnimation mapAnimation;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
screen = new Screen(this);
setContentView(screen);
}
#Override
protected void onDestroy() {
super.onDestroy();
}
#Override
protected void onPause() {
super.onPause();
}
#Override
protected void onResume() {
super.onResume();
}
public class Screen extends SurfaceView implements Callback{
private SurfaceHolder holder;
private MySurfaceViewThread mySurfaceViewThread;
private boolean isSurfaceCreated;
private Bitmap character, tiles;
public Screen(Context context) {
super(context);
initialize();
}
public void initialize(){
//Create a new SurfaceHolder and assign this class as its callback...
holder = getHolder();
holder.addCallback(this);
isSurfaceCreated = false;
character = BitmapFactory.decodeResource(getResources(),R.drawable.penguin_sprite);
tiles = BitmapFactory.decodeResource(getResources(), R.drawable.tile_sprites);
resume();
}
public void resume(){
//Create and start the graphics update thread.
if(mySurfaceViewThread == null){
mySurfaceViewThread = new MySurfaceViewThread();
if(isSurfaceCreated == true){
mySurfaceViewThread.start();
}
}
}
public void pause(){
//Kill the graphics update thread
if(mySurfaceViewThread != null){
mySurfaceViewThread.pause();
mySurfaceViewThread = null;
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
isSurfaceCreated = true;
if(mySurfaceViewThread != null){
mySurfaceViewThread.start();
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
isSurfaceCreated = false;
pause();
}
public class MySurfaceViewThread extends Thread{
private boolean isPaused;
private boolean characterLoaded, characterDrawn;
private SurfaceHolder surfaceHolder;
public MySurfaceViewThread(){
super();
isPaused = false;
characterLoaded = false;
surfaceHolder = holder;
characterDrawn = false;
}
public void run(){
//Repeat the drawing loop until the thread is stopped
while(!isPaused){
if(!surfaceHolder.getSurface().isValid()){
continue;
}
if(characterLoaded == false){
mapAnimation = new MapAnimation(screen, character);
characterLoaded = true;
}
Canvas canvas = surfaceHolder.lockCanvas();
mapAnimation.onDraw(canvas);
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
public void pause(){
}
public void onDraw(){
}
}
}
}
public class MapAnimation {
private Screen screen;
private Bitmap character;
private int width, height, xPosition, yPosition, xSpeed, ySpeed;
public MapAnimation(Screen screen, Bitmap character) {
this.screen = screen;
this.character = character;
this.width = character.getWidth();
this.height = character.getHeight();
xPosition = 0;
yPosition = 0;
xSpeed = 5;
ySpeed = 5;
}
public void updateCharacter(){
if(xPosition > screen.getWidth() - width - xSpeed){
xSpeed = 0;
ySpeed = 5;
}
if(yPosition > screen.getHeight() - height - ySpeed){
xSpeed = -5;
ySpeed = 0;
}
if(xPosition + xSpeed < 0){
xPosition=0;
xSpeed = 0;
ySpeed = -5;
}
if(yPosition+ySpeed < 0){
yPosition = 0;
xSpeed = 5;
ySpeed = 0;
}
xPosition += xSpeed;
yPosition += ySpeed;
}
public void onDraw(Canvas canvas){
updateCharacter();
Rect src = new Rect(0, 0,135,225);
Rect dst = new Rect(xPosition, yPosition, xPosition+width, yPosition+height);
canvas.drawBitmap(character, src, dst, null);
}
}
Your help will be deeply appreciated :)
I already solved my problem, I just need to add "drawColor(color.BLACk);" before calling mapAnimation.onDraw() method.

Categories

Resources