I'm making a game for android and i'm running into some problems with my thread when I exit my game and restart it again. I'm trying to interrupt the thread in the onPause function in MainActivity and resuming the thread in the start function in gamepanel with notify. It sometime works but it's not really pausing the game.
What is the proper way to implement the activity lifecycle while pausing a thread? Especially a game thread where game update functions shouldn't be called while the user is away.
public class MainActivity extends Activity {
private GamePanel gamepanel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
//removes title
requestWindowFeature(Window.FEATURE_NO_TITLE);
//full screen
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
gamepanel = new GamePanel(this);
setContentView(gamepanel);
SoundHandler.setContext(this);
}
#Override
protected void onPause() {
super.onPause();
gamepanel.getThread().interrupt();
}
#Override
protected void onStop() {
super.onStop();
}
#Override
protected void onRestart() {
super.onRestart();
}
#Override
protected void onResume() {
super.onResume();
}
#Override
protected void onDestroy() {
super.onDestroy();
boolean retry = true;
while (retry) {
try {
gamepanel.getThread().setRunning(false);
gamepanel.getThread().join();
} catch (InterruptedException e) {
e.printStackTrace();
}
retry = false;
}
}
}
Gamepanel that extends surfaceView:
#Override
public void surfaceDestroyed (SurfaceHolder holder){}
/**create surface.
*
* #param holder
*/
#Override
public void surfaceCreated (SurfaceHolder holder){
//we can safely start the game loop
System.out.println("CREATING SURFACE");
loadingscreen = false;
start();
}
public void start() {
if(!mGameIsRunning) {
thread.start();
thread.setRunning(true);
mGameIsRunning = true;
} else {
thread.onResume();
}
}
Thread class
public class MainThread extends Thread {
private SurfaceHolder surfaceHolder;
private GamePanel gamePanel;
public boolean running = false;
public static Canvas canvas;
private long startTime;
private long fps = 1/30;
public MainThread(SurfaceHolder surfaceHolder, GamePanel gamePanel) {
super();
this.surfaceHolder = surfaceHolder;
this.gamePanel = gamePanel;
startTime = System.nanoTime();
}
#Override
public void run()
{
while(running) {
if((System.nanoTime() - startTime) / 1000000000> fps) {
canvas = null;
//try locking the canvas for pixel editing
try {
canvas = surfaceHolder.lockCanvas();
synchronized (surfaceHolder) {
this.gamePanel.update();
this.gamePanel.draw(canvas);
this.gamePanel.checkSpeed();
}
} catch (Exception e) {
} finally {
if (canvas != null) {
try {
surfaceHolder.unlockCanvasAndPost(canvas);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
gamePanel.surfaceDestroyed(surfaceHolder);
}
public void onResume(){
synchronized(this){
this.notify();
}
}
public void setRunning(boolean b)
{
running=b;
}
public boolean getRunning(){
return running;
}
}
There is no memory barrier around boolean running you can synchronize or use private volatile boolean running;. Then you need to call setRunning(false) somewhere to stop it, which you do not seem to do. These reasons are probably why your thread isn't quitting.
But I don't believe it makes any sense to render the game in a separate thread anyway. If you can't update & render super fast anyway, (e.g. 1/60th second or better) then moving it to a thread isn't going to help.
Related
I am following a tutorial I found at http://www.androidauthority.com/android-game-java-785331/
I just wanted a basic tutorial to get me started making games on android.
Here is the MainActivity.java
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(new GameView(this));
}
}
Here is the Class Android Studio is claiming as abstract:
public class MainThread extends Thread {
private SurfaceHolder surfaceHolder;
private GameView gameView;
private boolean running;
public static Canvas canvas;
public MainThread(SurfaceHolder surfaceHolder, GameView gameView) {
super();
this.surfaceHolder = surfaceHolder;
this.gameView = gameView;
}
#Override
public void run() {
while (running) {
canvas = null;
try {
canvas = this.surfaceHolder.lockCanvas();
synchronized(surfaceHolder) {
this.gameView.update();
this.gameView.draw(canvas);
}
} catch (Exception e) {} finally {
if (canvas != null) {
try {
surfaceHolder.unlockCanvasAndPost(canvas);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
public void setRunning(boolean isRunning) {
running = isRunning;
}
}
And Here is the class that is attempting to instantiate the "abstract" class:
public class GameView extends SurfaceView implements SurfaceHolder.Callback {
public MainThread thread;
public GameView(Context context) {
super(context);
getHolder().addCallback(this);
thread = new MainThread(getHolder(), this);
setFocusable(true);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
thread.setRunning(true);
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
while (retry) {
try {
thread.setRunning(false);
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
retry = false;
}
}
public void update() {
}
}
I copied and pasted all the code from the tutorial. The only thing I can think of is the super class? or if there was a button I accidentally clicked when creating the class that made it abstract? I'm not sure what's wrong but I can't continue with the tutorial with errors. If anyone can see what's wrong or can maybe recommend another Android Studio Game tutorial I would very much appreciate it.
It's a problem with the naming. I had the exact same problem and merely changing it to 'GameThread' solved the problem
I have a thread that running into an activity. I don't want that the thread continuos running when the user click the home button or, for example, the user receive a call phone.
So I want pause the thread and resume it when the user re-opens the application.
I've tried with this:
protected void onPause() {
synchronized (thread) {
try {
thread.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
super.onPause();
}
protected void onResume() {
thread.notify();
super.onResume();
}
It stops the thread but don't resume it, the thread seems freezed.
I've also tried with the deprecated method Thread.suspend() and Thread.resume(), but in this case into Activity.onPause() the thread doesn't stop.
Anyone know the solution?
Use wait() and notifyAll() properly using a lock.
Sample code:
class YourRunnable implements Runnable {
private Object mPauseLock;
private boolean mPaused;
private boolean mFinished;
public YourRunnable() {
mPauseLock = new Object();
mPaused = false;
mFinished = false;
}
public void run() {
while (!mFinished) {
// Do stuff.
synchronized (mPauseLock) {
while (mPaused) {
try {
mPauseLock.wait();
} catch (InterruptedException e) {
}
}
}
}
}
/**
* Call this on pause.
*/
public void onPause() {
synchronized (mPauseLock) {
mPaused = true;
}
}
/**
* Call this on resume.
*/
public void onResume() {
synchronized (mPauseLock) {
mPaused = false;
mPauseLock.notifyAll();
}
}
}
Try the below code it will work
Thread thread=null;
OnResume()
public void onResume(){
super.onResume();
if(thread == null){
thread = new Thread()
{
#Override
public void run() {
try {
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
thread.start();
}
}
onPause()
#Override
public void onPause(){
super.onPause();
if(thread != null){
Thread moribund = thread;
thread = null;
moribund.interrupt();
}
}
For some reason, whenever I increase "score" in 1st class(in OnTouch method) it won't update in my 2nd class when I call drawText method.The score, however, increases when I touch the screen, but calling zc.getScore() method in 2nd class stil shows 100.
ZmijicaSV zmija;
private int score=100;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
zmija=new ZmijicaSV(this);
zmija.setOnTouchListener(this);
setContentView(zmija);
}
#Override
protected void onPause() {
super.onPause();
zmija.pause();
}
#Override
protected void onResume() {
super.onResume();
zmija.resume();
}
#Override
public boolean onTouch(View v, MotionEvent event) {
boolean up=zmija.sp.isUp();
boolean right=zmija.sp.isRight();
boolean down=zmija.sp.isDown();
boolean left=zmija.sp.isLeft();
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
if(up) {
zmija.sp.setUp(false);
zmija.sp.setRight(true);
update();
break;
}
if(right) {
zmija.sp.setRight(false);
zmija.sp.setDown(true);
update();
break;
}
if(down) {
zmija.sp.setDown(false);
zmija.sp.setLeft(true);
update();
break;
}
if(left) {
zmija.sp.setLeft(false);
zmija.sp.setUp(true);
update();
break;
}
}
return true;
}
public void update()
{
score++;
}
public int getScore()
{
return score;
}
.
Thread t=null;
Pravougaonik pr;
SurfaceHolder holder;
boolean isRunning=false;
SnakeParts sp;
ZmijicaCrtanje zc;
public ZmijicaSV(Context context) {
super(context);
holder=getHolder();
pr=new Pravougaonik(context);
sp=new SnakeParts();
zc=new ZmijicaCrtanje();
}
public void run()
{
Paint p=new Paint();
p.setColor(Color.BLUE);
p.setStyle(Paint.Style.FILL);
while(isRunning)
{
if(!holder.getSurface().isValid()) {
continue;
}
Canvas c=holder.lockCanvas();
c.drawColor(Color.BLACK);
sp.Draw(c);
pr.onDraw(c);
sp.update();
drawText(c);
holder.unlockCanvasAndPost(c);
try {
t.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void resume()
{
isRunning=true;
t=new Thread(this);
t.start();
}
public void pause()
{
isRunning=false;
while(true)
{
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
}
t=null;
}
public void drawText(Canvas canvas)
{
Paint paint=new Paint();
paint.setColor(Color.YELLOW);
paint.setTextSize(200);
canvas.drawText("Score:"+zc.getScore(),100,getHeight()-200,paint);
}
I can't know for sure, because you haven't included the name of the first class, but im quite sure the problem is that you are creating a new instance of the first class (probabaly ZmijicaCrtanje()) in your second class. This instance of ZmijicaCrtanje will be unaltered by the clicks registered in the first instance of ZmijicaCrtanje.
Two possible ways to fix this error are:
Make score a static variable.
Pass an instance of ZmijicaCrtanje to the constructor of the second class, ZmijicaSV, and set it equal to the global variable zc.
Good luck!
I would like to be able to detect collision from a class which uses thread in my activity.When collision is detected, new activity should be started.In my case, new activity is started only when collision is detected and I touch the screen.I want it to immediately start new activity without me having to touch it.I think the problem is in update() method(in activity class).I'm calling it in the wrong place.Any suggestions?
Thread class
private boolean Collision;
Thread t=null;
Pravougaonik pr;
SurfaceHolder holder;
boolean isRunning=false;
SnakeParts sp;
ZmijicaCrtanje zc;
public ZmijicaSV(Context context) {
super(context);
holder=getHolder();
pr=new Pravougaonik(context);
sp=new SnakeParts();
zc=new ZmijicaCrtanje();
Collision=false;
}
public void run()
{
Paint p=new Paint();
p.setColor(Color.BLUE);
p.setStyle(Paint.Style.FILL);
while(isRunning)
{
if(!holder.getSurface().isValid()) {
continue;
}
Canvas c=holder.lockCanvas();
c.drawColor(Color.BLACK);
sp.Draw(c);
pr.onDraw(c);
sp.update();
drawText(c);
holder.unlockCanvasAndPost(c);
try {
t.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void resume()
{
isRunning=true;
t=new Thread(this);
t.start();
}
public void pause()
{
isRunning=false;
while(true)
{
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
}
t=null;
}
public void drawText(Canvas canvas)
{
Paint paint=new Paint();
paint.setColor(Color.WHITE);
paint.setTextSize(50);
paint.setTypeface(Typeface.create(Typeface.DEFAULT,Typeface.BOLD));
canvas.drawText("Score:"+zc.getScore(),getWidth()-230,getHeight()-25,paint);
}
public boolean checkCollision()
{
if((sp.getHeadX()<-1)||(sp.getHeadX()>getWidth())||(sp.getHeadY()>getHeight())||(sp.getHeadY()<-1))
Collision=true;
return Collision;
}
}
Activity class
ZmijicaSV zmija;
private static int score=0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
zmija=new ZmijicaSV(this);
zmija.setOnTouchListener(this);
setContentView(zmija);
}
#Override
protected void onPause() {
super.onPause();
zmija.pause();
}
#Override
protected void onResume() {
super.onResume();
zmija.resume();
}
#Override
public boolean onTouch(View v, MotionEvent event) {
boolean up=zmija.sp.isUp();
boolean right=zmija.sp.isRight();
boolean down=zmija.sp.isDown();
boolean left=zmija.sp.isLeft();
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
if(up) {
zmija.sp.setUp(false);
zmija.sp.setRight(true);
update();
break;
}
if(right) {
zmija.sp.setRight(false);
zmija.sp.setDown(true);
update();
break;
}
if(down) {
zmija.sp.setDown(false);
zmija.sp.setLeft(true);
update();
break;
}
if(left) {
zmija.sp.setLeft(false);
zmija.sp.setUp(true);
update();
break;
}
}
return true;
}
public void update()
{
score++;
if(zmija.checkCollision())
startActivity(new Intent(ZmijicaCrtanje.this, GameOver.class));
}
public int getScore()
{
return score;
}
}
Even if I press the back button, my timer is still running and the next activity is executed after the specified time. How do I stop this?
Splash.java :
package com.ultimate.biggboss;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.widget.VideoView;
public class Splash extends Activity{
VideoView vid;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.splash);
vid = (VideoView)findViewById(R.id.video);
String urlpath="android.resource://" + getPackageName() + "/" + R.raw.logoanimate;
vid.setVideoURI(Uri.parse(urlpath));
vid.start();
Thread timer = new Thread(){
public void run(){
try{
sleep(4400);
} catch (InterruptedException e){
e.printStackTrace();
}finally{
Intent openHome = new Intent(Splash.this, main.class);
startActivity(openHome);
finish();
}
}
};
timer.start();
}
}
What code do I write and where in this class to stop the app as soon as I press the back button?
you can handle the back key press in the following way:
#Override
public void onBackPressed() {
// your code.
}
and before API 5 use this:
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
// your code
}
return super.onKeyDown(keyCode, event);
}
Inside this, you can do something like:
if(null != timer){
timer.cancel();
}
Note that the Thread's stop(), suspend() etc., methods have been
deprecated, the only way to safely terminate a thread is to have it
exit its run() method.
Try following:
public class MyThread extends Thread {
private long sleepTime;
private boolean stop;
public MyThread(long sleepTime) {
this.sleepTime = sleepTime;
stop = false;
}
public void run() {
do {
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
} while (!stop);
}
// soft stopping of thread
public synchronized void stopThread() {
stop = true;
}
}
To start the thread:
MyThread timer = new MyThread(4400);
timer.start();
To stop the thread:
timer.stopThread();
So you can handle it on pressing back button like:
#Override
public void onBackPressed() {
if(timer != null){
timer.stopThread();
timer = null;
}
}
You should interrupt the Thread
public void onBackPressed()
{
timer.interrupt();
this.finish();
}
I think you can use a flag to stop a thread, below is how i had done it:
public static int flag=0;//whenever flag=1, stop the thread. Flag should be a static global variable
#Override
public void onBackPressed() {
flag=1;
}
//My thread code was somewhat like this:
Thread timer = new Thread(){
public void run(){
try{
while(1)
{
if(flag==0)
//My code here
else
stopThread(this);
}
} catch (InterruptedException e){
e.printStackTrace();
}
}
};
timer.start();
The code for stopThread() is given below:
private synchronized void stopThread(Thread theThread)
{
if (theThread != null)
{
theThread = null;
}
}
This code worked for me. Hope it helps you too.