3-A Basic Game Loop
到目前为止,我们已经理解了游戏的结构。即使只是简单的知道了接收输入,更新内部状态,然后渲染到屏幕上,当然也会播放声音或者震动一下
让我们保持简单,看下面的图表
android上面任何事情都是发生在Activity内部,Activity会新建一个view,view是一切事情发生的地方,像触摸和最后的图片显示。把activity想成桌子,而view是一张白纸让我们画东西,我们用铅笔画图,化学反应都是在纸上的,产生了图片,同样也适用于Activity和View,像下图所示
让我们打开DroidzActivity.java,看这行代码
上面的代码只是重写了我们感兴趣的方法,除了15和17行,其他都没什么特别的
01 package net.obviam.droidz; 02 03 public class MainThread extends Thread { 04 05 // flag to hold game state 06 private boolean running; 07 public void setRunning(boolean running) { 08 this.running = running; 09 } 10 11 @Override 12 public void run() { 13 while (running) { 14 // update game state 15 // render state to the screen 16 } 17 } 18你看到它没有做太多事情,重写了run方法,当running标识为true时,做了个无限循环
当前的进程没有实例化,当屏幕加载的时候,我们来让它运行
看一下修改过的MainGamePanel类
01 package net.obviam.droidz; 02 03 import android.content.Context; 04 import android.graphics.Canvas; 05 import android.view.MotionEvent; 06 import android.view.SurfaceHolder; 07 import android.view.SurfaceView; 08 09 public class MainGamePanel extends SurfaceView implements 10 SurfaceHolder.Callback { 11 12 private MainThread thread; 13 14 public MainGamePanel(Context context) { 15 super(context); 16 getHolder().addCallback(this); 17 18 // create the game loop thread 19 thread = new MainThread(); 20 21 setFocusable(true); 22 } 23 24 @Override 25 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 26 } 27 28 @Override 29 public void surfaceCreated(SurfaceHolder holder) { 30 thread.setRunning(true); 31 thread.start(); 32 } 33 34 @Override 35 public void surfaceDestroyed(SurfaceHolder holder) { 36 boolean retry = true; 37 while (retry) { 38 try { 39 thread.join(); 40 retry = false; 41 } catch (InterruptedException e) { 42 // try again shutting down the thread 43 } 44 } 45 } 46 47 @Override 48 public boolean onTouchEvent(MotionEvent event) { 49 return super.onTouchEvent(event); 50 } 51 52 @Override 53 protected void onDraw(Canvas canvas) { 54 } 55 }
我们加入了下面代码行
12行声明了线程是私有的
01 public void surfaceDestroyed(SurfaceHolder holder) { 02 // tell the thread to shut down and wait for it to finish 03 // this is a clean shutdown 04 boolean retry = true; 05 while (retry) { 06 try { 07 thread.join(); 08 retry = false; 09 } catch (InterruptedException e) { 10 // try again shutting down the thread 11 } 12 } 13 }这个方法在surface被销毁前被调用,这里不是设置running标识的地方,但是这里的代码保证了线程被干净的关闭了,我们只是阻塞线程,等待它的死亡
如果在模拟器运行,我们啥也看不这,但是我们可以打一些log来测试,不要担心,后面会告诉你是怎么回事的,更多的请参考Android site.
加入和屏幕的交互我们会关闭程序当我们按屏幕下方的时候,如果我们点别的地方,我们只是记录坐标位置
在mainThread类中 我们加入如下行
1 private SurfaceHolder surfaceHolder; 2 private MainGamePanel gamePanel; 3 public MainThread(SurfaceHolder surfaceHolder, MainGamePanel gamePanel) { 4 super(); 5 this.surfaceHolder = surfaceHolder; 6 this.gamePanel = gamePanel; 7 }
我们声明了变量gamepanel和surfaceHolder,在构造函数中实例化了他们
我们拥有他们是非常重要的,不仅仅是gamepanel,因为我们需要锁住surface来画图,而这个操作只能通过surfaceHolder
改变在构造函数中的方法
Now you should see the LogCat view. This is nothing more than a console where you can follow Android’s log. It’s a great tool as you can filter for logs containing a specific text or logs with a certain tag which is quite useful.
回到代码,MainThread.java现在是这样了
01 package net.obviam.droidz; 02 03 import android.util.Log; 04 import android.view.SurfaceHolder; 05 06 public class MainThread extends Thread { 07 08 private static final String TAG = MainThread.class.getSimpleName(); 09 10 private SurfaceHolder surfaceHolder; 11 private MainGamePanel gamePanel; 12 private boolean running; 13 public void setRunning(boolean running) { 14 this.running = running; 15 } 16 17 public MainThread(SurfaceHolder surfaceHolder, MainGamePanel gamePanel) { 18 super(); 19 this.surfaceHolder = surfaceHolder; 20 this.gamePanel = gamePanel; 21 } 22 23 @Override 24 public void run() { 25 long tickCount = 0L; 26 Log.d(TAG, "Starting game loop"); 27 while (running) { 28 tickCount++; 29 // update game state 30 // render state to the screen 31 } 32 Log.d(TAG, "Game loop executed " + tickCount + " times"); 33 } 34 }
在8行 我们定义了 log的tag
在run函数中我们定义tickcount,当游戏循环被执行,它会加1
让我们回到MainGamePanel.java类,我们修改OnTOuchEvent,来处理触摸消息
01 public boolean onTouchEvent(MotionEvent event) { 02 if (event.getAction() == MotionEvent.ACTION_DOWN) { 03 if (event.getY() > getHeight() - 50) { 04 thread.setRunning(false); 05 ((Activity)getContext()).finish(); 06 } else { 07 Log.d(TAG, "Coords: x=" + event.getX() + ",y=" + event.getY()); 08 } 09 } 10 return super.onTouchEvent(event); 11 }同样也修改了DroidzActivity
01 package net.obviam.droidz; 02 03 import android.app.Activity; 04 import android.os.Bundle; 05 import android.util.Log; 06 import android.view.Window; 07 import android.view.WindowManager; 08 09 public class DroidzActivity extends Activity { 10 /** Called when the activity is first created. */ 11 12 private static final String TAG = DroidzActivity.class.getSimpleName(); 13 14 @Override 15 public void onCreate(Bundle savedInstanceState) { 16 super.onCreate(savedInstanceState); 17 // requesting to turn the title OFF 18 requestWindowFeature(Window.FEATURE_NO_TITLE); 19 // making it full screen 20 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); 21 // set our MainGamePanel as the View 22 setContentView(new MainGamePanel(this)); 23 Log.d(TAG, "View added"); 24 } 25 26 @Override 27 protected void onDestroy() { 28 Log.d(TAG, "Destroying..."); 29 super.onDestroy(); 30 } 31 32 @Override 33 protected void onStop() { 34 Log.d(TAG, "Stopping..."); 35 super.onStop(); 36 } 37 }运行程序,在程序上部点击几下,然后点击下部,程序应该退出

LogCat
目前做的事情
1 创建了一个全屏程序
2 有一个独立的线程控制程序
3 解析基本手势 例如触摸手势
4 优雅的关闭程序
代码大家可以去这里下载