我的简单游戏遇到了一些麻烦,特别是了解线程部分的工作原理。我知道有一个主要的游戏循环,负责更新和绘制一切。我认为我在创建不同的状态,更新和绘图以及维护体面的FPS方面做得不错。
但是,我不太明白,为什么我的线程不起作用。在添加所有状态和暂停/启动/等之前,我没有遇到让它运行和完美工作的问题。添加它们之后,我在线程创建时得到NullPointerException。下面是我的活动类和surfaceview类。
概述游戏:在屏幕(右侧)生成白色圆圈,它们以恒定速度向左移动。点按它们时,会使用粒子创建爆炸。如果它们击中左侧墙壁,它们就会消失,您的健康状况会减去1.对于您点击的每个圆圈,您的总数会上升1。
非常感谢任何帮助。我只学习了几天的机器人,之前我的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)
答案 0 :(得分:0)
mPummelView在您调用mPummelView.getThread()时为空。它直到您的(稍后)setContentView()。
才存在答案 1 :(得分:0)
您忘记初始化mPummelView:
mPummelView = new PummelView(this);
mPummelThread = mPummelView.getThread();
mPummelThread.setState(PummelThread.STATE_READY);
setContentView(mPummelView);
答案 2 :(得分:0)
在此行之后调用setContentView(new PummelView(this));
并查看其是否有效
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);