У меня возникли проблемы с моей простой игрой, особенно с пониманием того, как работает потоковая часть.Я понимаю, что есть основной игровой цикл, который заботится об обновлении и прорисовке всего.Я думаю , что я проделал достойную работу по созданию различных состояний, обновлению и рисованию, а также поддержанию достойного FPS.
Я не совсем понимаю, почему мой поток не будет работать.До добавления всех состояний и паузы / запуска / и т.д. у меня не было проблем с тем, чтобы он работал и работал идеально.После их добавления я получаю исключение NullPointerException при создании потока.Ниже приведены мой класс активности и класс поверхности.
Чтобы дать обзор игры: белые круги генерируются за пределами экрана (справа), и они движутся с постоянной скоростью влево.Когда вы нажимаете на них, взрыв создается с использованием частиц.Если они ударяются о левую боковую стенку, они исчезают, и ваше здоровье вычитается на 1. Для каждого касания круга ваш общий балл увеличивается на 1.
ЛЮБАЯ помощь очень ценится.Я изучал android только несколько дней, и мой java до этого был в лучшем случае компетентен.
Там есть все импортные данные (просто их не было C / P).
Я также извиняюсьдля отступа, не уверен, как исправить это на этом сайте.
Ошибка из этой строки в классе активности ниже: mPummelThread = mPummelView.getThread ();
ОБНОВЛЕНИЕ: пробовал обаниже приведены предложения, но все равно появляется точно такая же ошибка.
Предоставляет исключение 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();
}
}
^ Класс активности.
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
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)