使用LGame开发游戏,一开始都会把CP大神(http://blog.csdn.net/cping1982)的sample拿来跑一下,看看示例代码。不过对我们这种菜鸟来说,第一个疑问就是:这玩意是怎么跑起来的?本文就来摸索这个问题。说的不准确的地方,希望有人前来丢砖~~(当然前提是有人来看)。
我们先从Main类的onMain()方法开始。一个典型的onMain()是这样的:
publicvoid onMain() {
this.maxScreen(480,320);
this.initialization(true,LMode.Fill);
this.setScreen(new MyScreen());
this.setShowFPS(true);
this.showScreen();
}
我们知道LGame中,Main是继承自LGameAndroid2DActivity,而LGameAndroid2DActivity继承自Activity;对android应用来说,我们看到的内容都是在view中绘制的,通过Activity类的setContentView(View view)方法来把view加载到Activity中。(为什么要加载到Activity中?这个嘛,去google一下android入门知识咯~)。LGame分两种实现方案,一种是canvas,一种是openGL es。目前大部分机型都已经支持openGL ES硬件加速,所以我们使用openGL ES方案。这里补充一下必要的背景知识,android中openGL ES的绘制方案是通过指定一个渲染器Renderer,实现GLSurfaceView.Renderer的onDrawFrame方法进行。这个待会再仔细说。
现在就来找View。注意onMain()方法中的this.initialization(true,LMode.Fill)这句。往下挖一层,到LGameAndroid2DActivity,调用几次重载的initialization方法后,我们看到这么一句代码:this.gameView = newLGameAndroid2DView(LGameAndroid2DActivity.this,mode, fullScreen, landscape);
同时在这个类中我们找到下面这个方法:
publicvoid showScreen() {
setContentView(frameLayout);
...... //不需要关心的东东我就不贴出来了
}
是的,这是我们在上面的onMain当中看到的那个this.showScreen()所调用的东东。注意到setContentView方法中的参数frameLayout,这个frameLayout就是我们所要找的那个绘制屏幕的view(好吧,其实它是一个ViewGroup,不过绘制操作是调用内部每一个View的绘制....有点晕的话,还是老办法,google一下入门知识~~<真不厚道>)。它通过addView方法把gameView添加到内部。于是我们目光转向gameview.在LGameAndroid2DView的构造方法中我们看到:
this.surfaceView= createGLSurfaceView(activity);
挖掘下去,createGLSurfaceView方法内部我们看到:
LGameTools.Playview = new LGameTools.Play(activity);
......
view.setRenderer(this);
surfaceView = view;
LGameTools.Play类:final static class Play extendsGLSurfaceView,额,原来它所定义的view就是我们要找的GLSurfaceView。下面的就好理解了,view.setRenderer(this),因为LGameAndroid2DView实现了Renderer接口,所以LGameAndroid2DView实际上就是上面提到的渲染器。我们在做游戏开发时的绘制操作,就是在这个渲染器中进行的。在setRenderer方法调用的时候,系统将新建一个线程,该线程每帧调用一次渲染器中的onDrawFrame方法,持续地绘制我们看到的东西~好吧,看下代码就不晕了。
我们挖一下view.setRenderer(this)的源码(额,这个要用git去下载android的源码,具体怎么下载,那个嘛你懂得,google~~在这里我会把关键的部分贴出来)。
GLSurfaceView类中的setRenderer方法是这样的:
public void setRenderer(Renderer renderer){
......//闲杂人等我就不贴出来了
mGLThread = new GLThread(renderer);
mGLThread.start();
}
好了我没骗你吧,蓝色的那句代码就新建了一个线程用于渲染。来,接着挖一下它,这个GLThread是个线程,是GLSurfaceView里面定义的一个内部类(内部类就是,,敢说不懂你就shi定了):
class GLThread extends Thread {
......
public void run() {
......
guardedRun();
......
}
闲杂人等我都没贴出来,用了省略号代替。线程的run方法中我们关心的只有一句: guardedRun();
是的,再挖下去:
private void guardedRun() throwsInterruptedException {
mRenderer.onDrawFrame(gl);
}
mRenderer的定义
private Renderer mRenderer;
好了,到这里豁然开朗了,一切神奇的起源,就在这个onDrawFrame方法里面。马不停蹄赶回LGameAndroid2DView.java,直接看onDrawFrame方法。这个方法还是比较复杂,我们暂时先关注其中的两句:
public voidonDrawFrame(javax.microedition.khronos.opengles.GL10 gl10) {
......
process.runTimer(timerContext);
......
process.draw(gl);
......
}
先挖process.runTimer(timerContext),打开LProcess(这个类以后再议)类的runTimer方法,里面有一句
currentControl.runTimer(context);这个currentControl其实就是当前的screen(好吧,回头去看一下最开始的onMain方法,有一句setScreen里面的参***screen就是我们所实现的screen,也就是这里我们看到的currentControl),再挖下去,点开screen类的runTimer方法:
public void runTimer(final LTimerContext timer) {
......
if (fristPaintFlag) {
fristOrder.update(timer);
}
if (secondPaintFlag) {
secondOrder.update(timer);
}
if (lastPaintFlag) {
lastOrder.update(timer);
}
......
}
看到这几个update方法,我们已经接近真相了。点进去看:
void update(LTimerContext c) {
switch (type) {
case DRAW_USER:
screen.alter(c);
break;
case DRAW_SPRITE:
spriteRun = (sprites != null && sprites.size()> 0);
if (spriteRun) {
sprites.update(c.timeSinceLastUpdate);
}
break;
case DRAW_DESKTOP:
desktopRun = (desktop != null && desktop.size()> 0);
if (desktopRun) {
desktop.update(c.timeSinceLastUpdate);
}
break;
}
}
screen.alter(c) :这里调用的就是screen的alert方法,主要用来执行整个游戏的逻辑操作。
sprites.update(c.timeSinceLastUpdate):这个很明显了,更新精灵的状态。
desktop.update(c.timeSinceLastUpdate):这个作用是更新屏幕上的各种组件的状态,比如按钮、进度条等等。
上面的三个操作并不进行绘制。LGame的绘制和逻辑的执行是分开在不同的方法的,这样游戏逻辑和绘制操作可以得到最大限度的分离。下面我们来讲绘制的部分。
我们在一条分岔路口走的好远了,现在回到上面那句红色的process.draw(gl);(终于知道为什么把它弄成红色了吧,怕你找不到)。
还是老套剧情,挖下去,打开LProcess类的draw方法,看到里面有一句currentControl.createUI(g).刚才说了,currentControl就是当前screen,这里就是调用了screen的createUI,再打开这个方法,我把重点部分贴出来:
public synchronized void createUI(GLEx g) {
......
if (fristPaintFlag) {
fristOrder.paint(g);
}
if (secondPaintFlag) {
secondOrder.paint(g);
}
if (lastPaintFlag) {
lastOrder.paint(g);
}
......
}
这里有三个PaintOrder对象,分别调用各自的paint方法。PaintOrder是screen类的一个内部类(好纠结),在paint方法当中进行对屏幕的绘制。我们打开这个方法看看(次要部分我先把它们省略号代替~):
void paint(GLEx g) {
switch (type) {
case DRAW_USER:
screen.draw(g);
break;
case DRAW_SPRITE:
if (spriteRun) {
sprites.createUI(g); ......
break;
case DRAW_DESKTOP:
......
desktop.createUI(g);
......
}
}
真相就出来了,所谓的paint,其实就是调用了screen的draw方法绘制屏幕;调用sprite和desktop的createUI方法绘制精灵和其他组件。这三个方法和上面提到的三个update遥相呼应~~一个处理逻辑,一个处理绘制。
好了,这么长的帖子看的头晕。总结一下,基于LGame的游戏要怎么跑起来?开发者需要实现自己的MyScreen,一般继承自screen类就可以了。实现的时候把逻辑执行语句写在alert方法中,把绘制语句写在draw方法中。调用的来龙去脉:LGameAndroid2DView.java中的onDrawFrame-->LProcess类的runTimer方法和draw方法-->你的Screen类中的alert方法和draw方法。
好了,现在打开CP大神的那些sample,你该知道要怎么去下手去依葫芦画瓢了吧(不过CP的有些sample直接把逻辑都放在draw方法当中,比如那个ACT_OpenGLES,,汗,估计是为了快速发出来~大家还是尽量不要在draw里面写逻辑~)。
第一篇博文,写的有点啰嗦,内容有不对的地方请各位赏脸丢个砖头~~~下次见。