android


Creating a main game thread and getting game to run


I'm having some trouble with my simple game, specifically with understanding how the threading part works. I understand that there is the main game loop which takes care of updating and drawing everything. I think that I have done a decent job of creating different states, of updating and drawing, and of maintaining decent FPS.
I do not quite understand, however, why my thread won't work. Before adding all of the states and the pause/start/etc I had no problem with getting it to run and work perfectly. After adding them, I get a NullPointerException on the thread creation. Below is my activity class and the surfaceview class.
To give an overview of the game: white circles are generated off screen (to the right) and they move at a constant speed to the left. When you tap them, an explosion is created using particles. If they hit the left side wall, they disappear and your health is subtracted by 1. For each circle you tap, your totalscore goes up by 1.
ANY help is extremely appreciated. I have only been learning android for a few days and my java before that was competent at best.
All imports are there (just didn't C/P them).
I also apologize for the indentation, not sure how to fix it on this site.
The error is from this line in the activity class below:
mPummelThread = mPummelView.getThread();
UPDATE: Tried both suggestions below, but still receive the exact same error.
Gives a NullPointerException
public class PummelActivity extends Activity {
/** Called when the activity is first created. */
private static final String TAG = PummelActivity.class.getSimpleName();
private static final int MENU_START = 0;
private static final int MENU_PAUSE = 1;
private static final int MENU_RESUME = 2;
private static final int MENU_STOP = 3;
private PummelView mPummelView;
private PummelThread mPummelThread;
/**#Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
menu.add(0, MENU_START, 0, R.string.menu_start);
menu.add(0, MENU_PAUSE, 0, R.string.menu_pause);
menu.add(0, MENU_RESUME, 0, R.string.menu_resume);
menu.add(0, MENU_STOP, 0, R.string.menu_stop);
}*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
mPummelThread = mPummelView.getThread();
mPummelThread.setState(PummelThread.STATE_READY);
setContentView(new PummelView(this));
Log.d(TAG, "View added");
}
#Override
protected void onPause() {
super.onPause();
mPummelView.getThread().pause();
}
#Override
protected void onStop() {
Log.d(TAG, "Stopping...");
super.onStop();
}
}
^Activity class.
public class PummelView extends SurfaceView implements SurfaceHolder.Callback {
private static final String TAG = PummelView.class.getSimpleName();
private static final int EXPLOSION_SIZE = 100;
private PummelThread thread;
private ArrayList<Pummel> pummels = new ArrayList<Pummel>();
private ArrayList<Explosion> explosion = new ArrayList<Explosion>();
private int totalScore;
private String mScore = "Total Score = 0";
private int totalHp = 100;
private String mHP = "HP: 100";
public PummelView(Context context) {
super(context);
SurfaceHolder holder = getHolder();
holder.addCallback(this);
for (int i = 0; i < 3; i++) {
pummels.add(i, new Pummel(BitmapFactory.decodeResource(getResources(), R.drawable.bullet), 850, (int) (Math.random() * 200) + 80));
}
thread = new PummelThread(getHolder(), this);
setFocusable(true);
}
#Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
if (!hasWindowFocus) {
thread.pause();
}
}
public PummelThread getThread() {
return thread;
}
public void setScore(String score) {
mScore = score;
}
public void setHP(String hp) {
mHP = hp;
}
public void displayHp(Canvas canvas, String hp) {
if (canvas != null) {
Paint paint = new Paint();
paint.setARGB(255, 255, 255, 255);
canvas.drawText(hp, 10, 20, paint);
}
}
public void displayScore(Canvas canvas, String score) {
if (canvas != null) {
Paint paint = new Paint();
paint.setARGB(255, 255, 255, 255);
canvas.drawText(score, this.getWidth() / 2 - 50, 20, paint);
}
}
private void render(Canvas canvas) {
canvas.drawColor(Color.BLACK);
for (int i = 0; i < pummels.size(); i++) {
pummels.get(i).draw(canvas);
}
if (explosion != null) {
for (int j = 0; j < explosion.size(); j++) {
explosion.get(j).draw(canvas);
}
}
displayHp(canvas, mHP);
displayScore(canvas, mScore);
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
public void surfaceCreated(SurfaceHolder holder) {
thread.setRunning(true);
thread.start();
}
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
thread.setRunning(false);
while(retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
class PummelThread extends Thread {
public static final int STATE_LOSE = 1;
public static final int STATE_PAUSE = 2;
public static final int STATE_READY = 3;
public static final int STATE_RUNNING = 4;
private final static int MAX_FPS = 50;
private final static int MAX_FRAME_SKIPS = 5;
private final static int FRAME_PERIOD = 1000/ MAX_FPS;
private boolean mRunning;
private SurfaceHolder mSurfaceHolder;
private PummelView mGamePanel;
private Handler mHandler;
private int mMode;
private long beginTime;
private long timeDiff;
private int sleepTime = 0;
private int framesSkipped;
public PummelThread(SurfaceHolder surfaceHolder, PummelView gamePanel) {
super();
mSurfaceHolder = surfaceHolder;
mGamePanel = gamePanel;
}
public void setRunning(boolean running) {
mRunning = running;
}
public void doStart() {
synchronized (mSurfaceHolder) {
setState(STATE_RUNNING);
}
}
public void pause() {
synchronized (mSurfaceHolder) {
if (mMode == STATE_RUNNING) {
setState(STATE_PAUSE);
}
}
}
public void unpause() {
// Move the real time clock up to now
synchronized (mSurfaceHolder) {
timeDiff = System.currentTimeMillis() + 100;
}
setState(STATE_RUNNING);
}
public void setState(int mode) {
synchronized (mSurfaceHolder) {
setState(mode, null);
}
}
public void setState(int mode, CharSequence message) {
/*
* This method optionally can cause a text message to be displayed
* to the user when the mode changes. Since the View that actually
* renders that text is part of the main View hierarchy and not
* owned by this thread, we can't touch the state of that View.
* Instead we use a Message + Handler to relay commands to the main
* thread, which updates the user-text View.
*/
synchronized (mSurfaceHolder) {
mMode = mode;
if (mMode == STATE_RUNNING) {
Message msg = mHandler.obtainMessage();
Bundle b = new Bundle();
b.putString("text", "");
b.putInt("viz", View.INVISIBLE);
msg.setData(b);
mHandler.sendMessage(msg);
} else {
CharSequence str = "";
if (mMode == STATE_READY) {
str = (CharSequence) getTag(R.string.mode_ready);
}
else if (mMode == STATE_PAUSE) {
str = (CharSequence) getTag(R.string.mode_pause);
}
else if (mMode == STATE_LOSE) {
str = (CharSequence) getTag(R.string.mode_lose);
}
if (message != null) {
str = message + "\n" + str;
}
Message msg = mHandler.obtainMessage();
Bundle b = new Bundle();
b.putString("text", str.toString());
b.putInt("viz", View.VISIBLE);
msg.setData(b);
mHandler.sendMessage(msg);
}
}
}
public boolean screenTouch(MotionEvent event) {
boolean okStart = false;
if (event.getAction() == MotionEvent.ACTION_DOWN) {
okStart = true;
}
if (okStart && (mMode == STATE_READY || mMode == STATE_LOSE)) {
doStart();
return true;
} else if (okStart && mMode == STATE_PAUSE) {
unpause();
return true;
} else if (mMode == STATE_RUNNING) {
onTouchEvent(event);
}
return false;
}
public boolean onTouchEvent(MotionEvent event) {
if (mMode == STATE_RUNNING) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
for (int i = 0; i < pummels.size(); i++) {
pummels.get(i).handleActionDown((int) event.getX(), (int) event.getY());
if (pummels.get(i).isTouched()) {
totalScore++;
pummels.remove(i);
explosion.add(new Explosion(EXPLOSION_SIZE, (int) event.getX(), (int) event.getY()));
pummels.add(i, new Pummel(BitmapFactory.decodeResource(getResources(), R.drawable.bullet), getWidth() * 2, (int) (Math.random() * 200) + 80));
pummels.add(i, new Pummel(BitmapFactory.decodeResource(getResources(), R.drawable.bullet), getWidth() * 2, (int) (Math.random() * 200) + 80));
}
}
}
}
setScore("Total Score: " + String.valueOf(totalScore));
return true;
}
#Override
public void run() {
Log.d(TAG, "Starting game loop");
while(mRunning) {
Canvas c = null;
try {
c = mSurfaceHolder.lockCanvas();
synchronized (mSurfaceHolder) {
beginTime = System.currentTimeMillis();
framesSkipped = 0;
updatePhysics();
mGamePanel.render(c);
timeDiff = System.currentTimeMillis() - beginTime;
sleepTime = (int)(FRAME_PERIOD - timeDiff);
if (sleepTime > 0) {
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
}
}
while (sleepTime <= 0 && framesSkipped < MAX_FRAME_SKIPS) {
updatePhysics();
sleepTime += FRAME_PERIOD;
framesSkipped++;
}
if (framesSkipped > 0) {
Log.d(TAG, "Skipped:" + framesSkipped);
}
}
} finally {
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
public void updatePhysics() {
int pWidth = BitmapFactory.decodeResource(getResources(), R.drawable.bullet).getWidth();
for (int i = 0; i < pummels.size(); i++) {
//checks collision with left side wall
//changes direction if it collides
pummels.get(i).update();
if (pummels.get(i).getSpeed().getxDirection() == Speed.DIRECTION_LEFT && pummels.get(i).getX() - pWidth / 2 <= 0) {
totalHp--;
setHP("HP: " + String.valueOf(totalHp));
if (totalHp == 0) {
setState(STATE_LOSE);
break;
}
pummels.remove(i);
}
if (pummels.size() == 0) {
for (int j = 0; j < 10; j++) {
pummels.add(j, new Pummel(BitmapFactory.decodeResource(getResources(), R.drawable.bullet), 850, (int) (Math.random() * 200) + 80));
}
}
if (explosion != null) {
for (int j = 0; j < explosion.size(); j++) {
explosion.get(j).update(getHolder().getSurfaceFrame());
}
}
}
}
}
}
^SurfaceView class
03-05 11:49:10.283: E/AndroidRuntime(281): FATAL EXCEPTION: main
03-05 11:49:10.283: E/AndroidRuntime(281): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.joel.pummel/com.joel.pummel.PummelActivity}: java.lang.NullPointerException
03-05 11:49:10.283: E/AndroidRuntime(281): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2663)
03-05 11:49:10.283: E/AndroidRuntime(281): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2679)
03-05 11:49:10.283: E/AndroidRuntime(281): at android.app.ActivityThread.access$2300(ActivityThread.java:125)
03-05 11:49:10.283: E/AndroidRuntime(281): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2033)
03-05 11:49:10.283: E/AndroidRuntime(281): at android.os.Handler.dispatchMessage(Handler.java:99)
03-05 11:49:10.283: E/AndroidRuntime(281): at android.os.Looper.loop(Looper.java:123)
03-05 11:49:10.283: E/AndroidRuntime(281): at android.app.ActivityThread.main(ActivityThread.java:4627)
03-05 11:49:10.283: E/AndroidRuntime(281): at java.lang.reflect.Method.invokeNative(Native Method)
03-05 11:49:10.283: E/AndroidRuntime(281): at java.lang.reflect.Method.invoke(Method.java:521)
03-05 11:49:10.283: E/AndroidRuntime(281): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
03-05 11:49:10.283: E/AndroidRuntime(281): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
03-05 11:49:10.283: E/AndroidRuntime(281): at dalvik.system.NativeStart.main(Native Method)
03-05 11:49:10.283: E/AndroidRuntime(281): Caused by: java.lang.NullPointerException
03-05 11:49:10.283: E/AndroidRuntime(281): at com.joel.pummel.PummelActivity.onCreate(PummelActivity.java:46)
03-05 11:49:10.283: E/AndroidRuntime(281): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
03-05 11:49:10.283: E/AndroidRuntime(281): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2627)
mPummelView is null at the point you call mPummelView.getThread(). It doesn't exist until your (later) setContentView().
You have forgotten to initialize your mPummelView:
mPummelView = new PummelView(this);
mPummelThread = mPummelView.getThread();
mPummelThread.setState(PummelThread.STATE_READY);
setContentView(mPummelView);
call setContentView(new PummelView(this)); after this line and see if it works
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);

Related Links

Push notification click not open my desire activity
SAVE RESTORE VIEW: fields not displayed
How to mute Notification Sound in Android
getResourceId method of TypedArray
How can i maintain the position of listview when user comes back to the activity by pressing back button?
Can I develop Android TV app without ADT-1?
Can't use debugger with NotificationListenerService
Android - Add views to item in a ListView
Image getting captured only in first fragment
ProgressDialog causes app to crash in android
Building android jar library with smaller targets
How to show second alertDialog window depending on the decision in the first alertDialog?
Android Simple Data Sharing
error on adding rout to map view in titanium
How to get AndroidManifest property with gradle
java.lang.NoClassDefFoundError: com.google.firebase.FirebaseOptions exception in API 19

Categories

HOME
notepad++
codenvy
angular2-material
apache-spark-mllib
spring-data-mongodb
cakephp-3.4
vxml
x264
boto3
discrete-mathematics
p2p
octave
siddhi
google-form
intercom
shared-libraries
selectize.js
bitnami
linear-algebra
alpacajs
mvc-mini-profiler
firefox-addon
intershop
dimensional-modeling
asp.net-mvc-viewmodel
jmp
sar
ios10.2
object-storage
flask-sqlalchemy
fable-f#
pylons
jasmine-node
has-and-belongs-to-many
ceylon
user-defined-functions
twiml
aws-codecommit
postback
spring-security4
hard-drive
github3.py
getlasterror
fiware-cygnus
hivemq
sim-card
google-data-api
trigonometry
install.packages
pom.xml
headless
payload
java-websocket
jtree
bootstrap-tags-input
juice-ui
chromium-os
pure-css
mapxtreme
hadoop-2.7.2
dup
pushbots
video-player
toolkit
rpart
jdk1.7
viola-jones
keyup
alloy-ui
j-security-check
cakephp-2.7
phpldapadmin
mass-assignment
iso-prolog
teaspoon
file-not-found
mongohq
nevron
video-codecs
argouml
pstack
subproject
window-decoration
kqueue
cross-database
ikimagebrowserview
u2netdk
automationelement
webdev.webserver
nhprof
photolibrary
metaweblog
resharper-6.0
burndowncharts
change-management

Resources

Mobile Apps Dev
Database Users
javascript
java
csharp
php
android
MS Developer
developer works
python
ios
c
html
jquery
RDBMS discuss
Cloud Virtualization
Database Dev&Adm
javascript
java
csharp
php
python
android
jquery
ruby
ios
html
Mobile App
Mobile App
Mobile App