真希望有一天,一个游戏框架不再具有平台相关性,这样对开发者来说是件幸事,想想吧,你开发的游戏能不经修改,就能在win phone,ios,android等多平台上运行,用户量一下就增长,money自然也不菲。
所以就让我们进入android原生代码的世界吧。
与我们常用的window桌面程序类似,android中也有窗体的概念,这个窗体叫做 Activity 。
窗体是一个容器,好比自家的玻璃窗,我小的时候我还给自家的窗户贴过窗花,你可以把色彩斑斓的图片往窗户上一贴,既保护隐私,又为你的家居环境增色不少。在android中作为容器的窗体(Activity),我们也会给窗户贴上窗花,这个窗花可能是一个列表,一个文本输入框,一个按钮等。
看下面代码:注意:作者假定你已经掌握了基本的android程序开发,这也是你阅读本教程的进入条件。
- public class HelloWorldActivity extends Activity
- {
- Button button;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- button = new Button(this);
- button.setText( "Hello World!" );
- setContentView(button);
- }
- }
上面的代码中我们为一个Activity窗体贴上了一个按钮,并且按钮上写着“hello world”,
注意到 setContentView(button),就是这句代码将窗花(按钮)贴入窗体了。
那么对于游戏开发,我们怎么把游戏动画贴入到窗体里呢?何况游戏还是一系列循环更新的动画。
其实同样是使用 setContentView,代码片段如下
- SurfaceView mySurfaceView;
- ....
- @Override
- public void onCreate(Bundle savedInstanceState) {
- .......
- setContentView(mySurfaceView);
- ......
- }
- ....
SurfaceView实际上好比一个贴入窗体的画布,我们可以通过这个画布一张一张的绘图,实现我们上一讲所述的游戏循环,在画布上一张一张的绘图,但是SurfaceView只能方便的实现2d动画,如果是3d世界,那就得靠自己实现一个3d引擎了,难道你打算自己动手写一个opengl或者direct3d吗?
不用重新发明车轮了,何况游戏的流畅运行可离不开硬件加速,为什么不直接使用opengl es?
opengl es甚至为我们封装好了游戏循环,不用自己去写循环的线程了,此外对于图片放大缩小,旋转等等都能硬件加速,这样游戏才能流畅起来,别忘记了游戏的帧数直接关系到画面是否卡顿。
实现转换只需要我们将 SurfaceView 换为 GLSurfaceView,这是专门为opengl es 封装的画布,代码片段修改如下:
- GLSurfaceView mySurfaceView;
- ....
- @Override
- public void onCreate(Bundle savedInstanceState) {
- .......
- setContentView(mySurfaceView);
- ......
- }
- ....
- GLSurfaceView mySurfaceView;
- ....
- @Override
- public void onCreate(Bundle savedInstanceState) {
- .......
- mySurfaceView=new GLSurfaceView(this);
- setContentView(mySurfaceView);
- ......
- }
- ....
new GLSurfaceView(this)
接下来就是通过GLSurfaceView进行动画绘制了,这就需要调用GLSurfaceView提供的setRenderer方法
- GLSurfaceView mySurfaceView;
- Renderer myRender;
- ....
- @Override
- public void onCreate(Bundle savedInstanceState) {
- .......
- mySurfaceView=new GLSurfaceView(this);
- mySurfaceView.setRenderer(myRender);
- setContentView(mySurfaceView);
- ......
- }
- ....
mySurfaceView.setRenderer(myRender);
那么myRender是什么呢?
需要传入myRender它是Renderer接口,Renderer定义如下:
- interface Renderer {
- public void onSurfaceCreated(GL10 gl, EGLConfig config);
- public void onSurfaceChanged(GL10 gl, int width, int height);
- public void onDrawFrame(GL10 gl);
- }
onDrawFrame意思是每画一帧,正好对应我们上一节中说到的游戏循环(while),onDrawFrame方法每被调用一次,类似于while循环里面的代码执行一次,这样一来,我们可以实现如下onDrawFrame方法。
- public void onDrawFrame(GL10 gl)
- {
- input();
- logic();
- render();
- }
对比上一讲中的游戏循环框架:
while(isGameRunning)
{
input();
logic();
render();
}
注意onDrawFrame还提供了一个传入参数gl,这个参数正好就是我们使用opengl es的入口,通过对传入的gl进行调用,我们可以充分利用opengl es提供的各项功能来实现画面绘制。
- public void onDrawFrame(GL10 gl)
- {
- ...
- gl.XXXX();
- ...
- }
最后,再次提醒,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方法。事实上每一帧的动画都在这个方法里产生,因此这里就是实现游戏循环的地方。