3-A Basic Game Loop

到目前为止,我们已经理解了游戏的结构。即使只是简单的知道了接收输入,更新内部状态,然后渲染到屏幕上,当然也会播放声音或者震动一下

 

让我们保持简单,看下面的图表

android上面任何事情都是发生在Activity内部,Activity会新建一个view,view是一切事情发生的地方,像触摸和最后的图片显示。把activity想成桌子,而view是一张白纸让我们画东西,我们用铅笔画图,化学反应都是在纸上的,产生了图片,同样也适用于Activity和View,像下图所示

 

让我们打开DroidzActivity.java,看这行代码

1setContentView(R.layout.main);

只是Activity在创建时候把R.layout.view赋给了它,我们的例子中 在开始时候就赋值了

让我们创建了一个新的view,一个View是一个简单的类,提供了事件处理,和可见的矩形供我们画,最简单的是扩展Android自己的Surfaceview,我们也需要实现surface.callback来获得surface改变的路径,例如当view被销毁或者设备的方向改变了

MainGamePanel.java

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     public MainGamePanel(Context context) {  

13         super(context);  

14         // adding the callback (this) to the surface holder to intercept events  

15         getHolder().addCallback(this);  

16         // make the GamePanel focusable so it can handle events  

17         setFocusable(true);  

18     }  

19    

20     @Override 

21     public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {  

22     }  

23    

24     @Override 

25     public void surfaceCreated(SurfaceHolder holder) {  

26     }  

27    

28     @Override 

29     public void surfaceDestroyed(SurfaceHolder holder) {  

30     }  

31    

32     @Override 

33     public boolean onTouchEvent(MotionEvent event) {  

34         return super.onTouchEvent(event);  

35     }  

36    

37     @Override 

38     protected void onDraw(Canvas canvas) {  

39     }  

40 } 

 

上面的代码只是重写了我们感兴趣的方法,除了15和17行,其他都没什么特别的

1getHolder().addCallback(this);

这行代码把当前类设置成了在实际surface上发生的事件需要处理的handler

1setFocusable(true);

上面的代码使 game panel获得焦点,为了能够获得事件。我们加入了回调并使得view获得焦点,我们不会丢失这个view

20行后面重写的方法会被用到,但是现在让他们为空

让我们建立一个线程,它会是我们实际的游戏循环

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行声明了线程是私有的

1private MainThread thread;

19行我们实例化了这个线程

1thread = newMainThread();

In the surfaceCreated method we set the running flag to true and we start up the thread (lines 30 and 31). By the time the this method is called the surface is already created and the game loop can be safely started.

Take a look at the surfaceDestroyed method.

在surfacecreated的方法中 我们设置了running为true,然后我们让线程开始运行(30和31行)。当surface被创建的时候,游戏循环可以安全的开始运行了

看一下surfaceDestroy方法

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

改变在构造函数中的方法

1thread = newMainThread(getHolder(),this);

我们传递了当前的holder和panel,所以线程可以访问他们,我们稍后会新建一个游戏更新的函数在game panel中,在线程中也会触发它,但是现在我们保持现状

TAG 常量是android log函数的第一个参数

打log的笔记

打开log的视图,

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 优雅的关闭程序

代码大家可以去这里下载

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值