四.android中的游戏循环

  真希望有一天,一个游戏框架不再具有平台相关性,这样对开发者来说是件幸事,想想吧,你开发的游戏能不经修改,就能在win phone,ios,android等多平台上运行,用户量一下就增长,money自然也不菲。


       有人会说不是已经有了吗?html5+js做的游戏框架是未来的希望之光,或许有那么一天吧,但是首先必须解决通过浏览器运行低劣的效率,以及是开源代码保护知识产权的问题(html5和js都是文本),此外浏览器考虑到安全性也不能随便调用本机资源,这样必然对游戏本身有很多限制......

        

        所以就让我们进入android原生代码的世界吧。

       

         与我们常用的window桌面程序类似,android中也有窗体的概念,这个窗体叫做 Activity 。

         窗体是一个容器,好比自家的玻璃窗,我小的时候我还给自家的窗户贴过窗花,你可以把色彩斑斓的图片往窗户上一贴,既保护隐私,又为你的家居环境增色不少。

          在android中作为容器的窗体(Activity),我们也会给窗户贴上窗花,这个窗花可能是一个列表,一个文本输入框,一个按钮等。 

 看下面代码:注意:作者假定你已经掌握了基本的android程序开发,这也是你阅读本教程的进入条件。

  1. public class HelloWorldActivity extends Activity  
  2. {  
  3. Button button;  
  4. @Override  
  5. public void onCreate(Bundle savedInstanceState) {  
  6. super.onCreate(savedInstanceState);  
  7. button = new Button(this);  
  8. button.setText( "Hello World!" );  
  9. setContentView(button);  
  10. }  
  11. }  

上面的代码中我们为一个Activity窗体贴上了一个按钮,并且按钮上写着“hello world”,

注意到  setContentView(button),就是这句代码将窗花(按钮)贴入窗体了。

那么对于游戏开发,我们怎么把游戏动画贴入到窗体里呢?何况游戏还是一系列循环更新的动画。

其实同样是使用 setContentView,代码片段如下

  1. SurfaceView mySurfaceView;  
  2. ....  
  3. @Override  
  4. public void onCreate(Bundle savedInstanceState) {  
  5. .......  
  6.  setContentView(mySurfaceView);  
  7. ......  
  8. }  
  9. ....

SurfaceView实际上好比一个贴入窗体的画布,我们可以通过这个画布一张一张的绘图,实现我们上一讲所述的游戏循环,在画布上一张一张的绘图,但是SurfaceView只能方便的实现2d动画,如果是3d世界,那就得靠自己实现一个3d引擎了,难道你打算自己动手写一个opengl或者direct3d吗?  

不用重新发明车轮了,何况游戏的流畅运行可离不开硬件加速,为什么不直接使用opengl es? 

opengl es甚至为我们封装好了游戏循环,不用自己去写循环的线程了,此外对于图片放大缩小,旋转等等都能硬件加速,这样游戏才能流畅起来,别忘记了游戏的帧数直接关系到画面是否卡顿。 

实现转换只需要我们将 SurfaceView  换为 GLSurfaceView,这是专门为opengl es 封装的画布,代码片段修改如下: 

  1. GLSurfaceView mySurfaceView;  
  2. ....  
  3. @Override  
  4. public void onCreate(Bundle savedInstanceState) {  
  5. .......  
  6.  setContentView(mySurfaceView);  
  7. ......  
  8. }  
  9. ....  
这样看来,游戏动画绘制其实是只要通过 GLSurfaceView 这个画布就能绘制了,接下里我们首先得把代码中的mySurfaceView构造出来,继续修改代码:
  1. GLSurfaceView mySurfaceView;  
  2. ....  
  3. @Override  
  4. public void onCreate(Bundle savedInstanceState) {  
  5. .......  
  6.  mySurfaceView=new GLSurfaceView(this);  
  7.  setContentView(mySurfaceView);  
  8. ......  
  9. }  
  10. .... 
注意,构造函数把当前的activity作为参数,传入

new GLSurfaceView(this

接下来就是通过GLSurfaceView进行动画绘制了,这就需要调用GLSurfaceView提供的setRenderer方法

  1. GLSurfaceView mySurfaceView;  
  2. Renderer myRender;  
  3. ....  
  4. @Override  
  5. public void onCreate(Bundle savedInstanceState) {  
  6. .......  
  7.  mySurfaceView=new GLSurfaceView(this);  
  8.  mySurfaceView.setRenderer(myRender);  
  9.  setContentView(mySurfaceView);  
  10. ......  
  11. }  
  12. ....
上面的代码中多了这样一句:

mySurfaceView.setRenderer(myRender);  

那么myRender是什么呢? 

需要传入myRender它是Renderer接口,Renderer定义如下: 

  1. interface Renderer {  
  2. public void onSurfaceCreated(GL10 gl, EGLConfig config);  
  3. public void onSurfaceChanged(GL10 gl, int width, int height);  
  4. public void onDrawFrame(GL10 gl);  
其实只要编写一个实现Renderer 接口的类,并传给GLSurfaceView(就是调用GLSurfaceView.setRenderer方法) ,也就实现了游戏循环,简化了开发过程。

onDrawFrame意思是每画一帧,正好对应我们上一节中说到的游戏循环(while),onDrawFrame方法每被调用一次,类似于while循环里面的代码执行一次,这样一来,我们可以实现如下onDrawFrame方法。 

  1. public void onDrawFrame(GL10 gl)  
  2. {  
  3. input();  
  4. logic();  
  5. render();  
  6. }  

对比上一讲中的游戏循环框架:

while(isGameRunning)
{
  input();
  logic();
  render();
}


注意onDrawFrame还提供了一个传入参数gl,这个参数正好就是我们使用opengl es的入口,通过对传入的gl进行调用,我们可以充分利用opengl es提供的各项功能来实现画面绘制。  

  1. public void onDrawFrame(GL10 gl)  
  2. {  
  3. ...  
  4. gl.XXXX();  
  5. ...  

最后,再次提醒,onDrawFrame(GL10 gl)是一直的被系统循环调用,运行完一次,接着又运行下一次,周而复始,这样就不用自己编写游戏循环线程了,每运行一次就产生一帧画面,1秒中能运行多次就产生了动画效果了。


那么让我们组合所有的代码吧:

package game.framework;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.app.Activity;
import android.opengl.*;
import android.opengl.GLSurfaceView.*;
import android.os.Bundle;
import android.util.Log;

public class AgameActivity extends Activity {
	GLSurfaceView mySurfaceView;  
	Renderer myRender; 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mySurfaceView=new GLSurfaceView(this);
        myRender=new GameRender();
        mySurfaceView.setRenderer(myRender);  
        setContentView(mySurfaceView);  
      
    }
    static class GameRender implements Renderer
    {
        
		public void onDrawFrame(GL10 gl) {
			// TODO Auto-generated method stub
			Log.w("frame", String.valueOf(System.currentTimeMillis()));
		}

		public void onSurfaceChanged(GL10 gl, int width, int height) {
			// TODO Auto-generated method stub
			
		}

		public void onSurfaceCreated(GL10 gl, EGLConfig config) {
			// TODO Auto-generated method stub
			
		}
    	
    }
}

内部类GameRender是对Renderer接口的实现,这样我们就实现了画师,就能让画师(GameRender)在画布(mySurfaceView)上作画。
程序运行后,我们会在logCat窗口看到一系列跳动的数字,数字对应当前的毫秒数,通过这些变动的数字至少我们能证明onDrawFrame(GL10 gl)
方法确实在一刻不停的反复调用着。


做一个总结陈词吧:  

首先游戏的运行需要窗体,窗体就是Activity,但是Activity只是一个容器罢了,我们还得给窗体贴上画布(Activity.setContentView),这个画布就是GLSurfaceView ,有了画布就可以作画了,这个时候需要一个画师来作画(GLSurfaceView.setRenderer),这个画师就是传入的Renderer接口,但是这个画师只是傀儡罢了,我们需要实现这个接口给画师注入灵魂, 那么就必须实现onDrawFrame等方法,到最后我们恍然大悟了,原来开发一个游戏最主要就是实现onDrawFrame方法。事实上每一帧的动画都在这个方法里产生,因此这里就是实现游戏循环的地方。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值