2012-03-05 131 views
0

我在玩簡單遊戲時遇到了一些麻煩,特別是瞭解線程部分如何工作。我明白,有一個主要的遊戲循環,負責更新和繪製所有內容。我認爲,我已經做了一個體面的工作,創造不同的國家,更新和繪圖,並保持體面的FPS。創建主遊戲線程並讓遊戲運行

但是,我不太明白,爲什麼我的線程無法工作。在添加所有的狀態和暫停/開始/等之前,我沒有任何問題讓它運行並完美工作。添加它們之後,我在創建線程時得到一個NullPointerException。以下是我的活動類和surfaceview類。

給遊戲的概述:白色圓圈生成屏幕外(右側),並且它們以恆定的速度向左側移動。當你點擊它們時,使用粒子創建爆炸。如果他們擊中了左側壁,他們消失了,你的健康是1,對於你點擊每圈中減去,您totalscore由1

上升任何幫助非常讚賞。我只是在學習android幾天,而且在那之前我的java完全可以勝任。

所有進口都在那裏(只是沒有C/P他們)。

我也道歉了壓痕,不知道如何解決在這個網站上。

錯誤來自以下活動類中的以下行: mPummelThread = mPummelView.getThread();

UPDATE:嘗試以下兩個建議,但仍收到完全相同的錯誤。

給出一個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類。

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

你能不能也告訴我們您收到的錯誤? – Ruuhkis 2012-03-05 07:24:05

+0

請發佈您正在接收的空指針的堆棧跟蹤。 – edthethird 2012-03-05 16:55:06

回答

0

mPummelView是在點零調用mPummelView.getThread()。直到你的(稍後)setContentView()才存在。

+0

即使切換語句後,我也收到同樣的錯誤? – Kinru 2012-03-05 17:39:26

0

你忘了初始化mPummelView:此行之後

mPummelView = new PummelView(this); 

mPummelThread = mPummelView.getThread(); 
mPummelThread.setState(PummelThread.STATE_READY); 

setContentView(mPummelView); 
+0

仍然沒有工作。同樣的確切錯誤。 – Kinru 2012-03-05 22:32:09

+0

@Kinru這很奇怪,'mPummelThread = mPummelView.getThread()'仍然是導致NPE的線?唯一會導致該堆棧跟蹤的NPE是空mPummelView。 – fsommar 2012-03-10 11:30:31

+0

是的,我收到了同一條錯誤。 – Kinru 2012-03-12 15:54:22

0

呼叫setContentView(new PummelView(this));,看看它是否工作

getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, 
        WindowManager.LayoutParams.FLAG_FULLSCREEN); 
+0

我剛剛意識到這個職位的年齡有多大 – rdawg71481 2013-06-05 02:04:58

+0

如果解決方案可以幫助未來的某人,那麼實際年齡並不重要:) – Nunser 2013-06-05 02:23:47

+0

好點我希望它能幫助未來的某個人 – rdawg71481 2013-06-05 02:49:14