刚开始学习AndEngine,将源码整理好,跑了一次,大体上看了一下Demo,非常不错!今天就正式开始学习了!
网上大部分帖子都说,要使用AndEngine,首先需要一个Activity继承BaseGameActivity。而然,最新代码里面,又对BaseGameActivity进行了一层封装---SimpleBaseGameActivity。源码如下:
public abstract class SimpleBaseGameActivity extends BaseGameActivity { // =========================================================== // Constants // =========================================================== // =========================================================== // Fields // =========================================================== // =========================================================== // Constructors // =========================================================== // =========================================================== // Getter & Setter // =========================================================== // =========================================================== // Methods for/from SuperClass/Interfaces // =========================================================== protected abstract void onCreateResources(); protected abstract Scene onCreateScene(); @Override public final void onCreateResources(final OnCreateResourcesCallback pOnCreateResourcesCallback) throws Exception { this.onCreateResources(); pOnCreateResourcesCallback.onCreateResourcesFinished(); } @Override public final void onCreateScene(final OnCreateSceneCallback pOnCreateSceneCallback) throws Exception { final Scene scene = this.onCreateScene(); pOnCreateSceneCallback.onCreateSceneFinished(scene); } @Override public final void onPopulateScene(final Scene pScene, final OnPopulateSceneCallback pOnPopulateSceneCallback) throws Exception { pOnPopulateSceneCallback.onPopulateSceneFinished(); } // =========================================================== // Methods // =========================================================== // =========================================================== // Inner and Anonymous Classes // =========================================================== }
其类继承关系如图:
那么我们就一次来看看。首先看其超类BaseActivity:
public abstract class BaseActivity extends Activity { // =========================================================== // Constants // =========================================================== // =========================================================== // Fields // =========================================================== // =========================================================== // Constructors // =========================================================== // =========================================================== // Getter & Setter // =========================================================== // =========================================================== // Methods for/from SuperClass/Interfaces // =========================================================== // =========================================================== // Methods // =========================================================== public void toastOnUIThread(final CharSequence pText) { this.toastOnUIThread(pText, Toast.LENGTH_LONG); } public void toastOnUIThread(final CharSequence pText, final int pDuration) { if(Looper.getMainLooper().getThread() == Thread.currentThread()) { Toast.makeText(BaseActivity.this, pText, pDuration).show(); } else { this.runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(BaseActivity.this, pText, pDuration).show(); } }); } } /** * Performs a task in the background, showing a {@link ProgressDialog}, * while the {@link Callable} is being processed. * * @param <T> * @param pTitleResourceID * @param pMessageResourceID * @param pErrorMessageResourceID * @param pCallable * @param pCallback */ protected <T> void doAsync(final int pTitleResourceID, final int pMessageResourceID, final Callable<T> pCallable, final Callback<T> pCallback) { this.doAsync(pTitleResourceID, pMessageResourceID, pCallable, pCallback, null); } /** * Performs a task in the background, showing a indeterminate {@link ProgressDialog}, * while the {@link Callable} is being processed. * * @param <T> * @param pTitleResourceID * @param pMessageResourceID * @param pErrorMessageResourceID * @param pCallable * @param pCallback * @param pExceptionCallback */ protected <T> void doAsync(final int pTitleResourceID, final int pMessageResourceID, final Callable<T> pCallable, final Callback<T> pCallback, final Callback<Exception> pExceptionCallback) { ActivityUtils.doAsync(this, pTitleResourceID, pMessageResourceID, pCallable, pCallback, pExceptionCallback); } /** * Performs a task in the background, showing a {@link ProgressDialog} with an ProgressBar, * while the {@link AsyncCallable} is being processed. * * @param <T> * @param pTitleResourceID * @param pMessageResourceID * @param pErrorMessageResourceID * @param pAsyncCallable * @param pCallback */ protected <T> void doProgressAsync(final int pTitleResourceID, final int pIconResourceID, final ProgressCallable<T> pCallable, final Callback<T> pCallback) { this.doProgressAsync(pTitleResourceID, pIconResourceID, pCallable, pCallback, null); } /** * Performs a task in the background, showing a {@link ProgressDialog} with a ProgressBar, * while the {@link AsyncCallable} is being processed. * * @param <T> * @param pTitleResourceID * @param pMessageResourceID * @param pErrorMessageResourceID * @param pAsyncCallable * @param pCallback * @param pExceptionCallback */ protected <T> void doProgressAsync(final int pTitleResourceID, final int pIconResourceID, final ProgressCallable<T> pCallable, final Callback<T> pCallback, final Callback<Exception> pExceptionCallback) { ActivityUtils.doProgressAsync(this, pTitleResourceID, pIconResourceID, pCallable, pCallback, pExceptionCallback); } /** * Performs a task in the background, showing an indeterminate {@link ProgressDialog}, * while the {@link AsyncCallable} is being processed. * * @param <T> * @param pTitleResourceID * @param pMessageResourceID * @param pErrorMessageResourceID * @param pAsyncCallable * @param pCallback * @param pExceptionCallback */ protected <T> void doAsync(final int pTitleResourceID, final int pMessageResourceID, final AsyncCallable<T> pAsyncCallable, final Callback<T> pCallback, final Callback<Exception> pExceptionCallback) { ActivityUtils.doAsync(this, pTitleResourceID, pMessageResourceID, pAsyncCallable, pCallback, pExceptionCallback); } // =========================================================== // Inner and Anonymous Classes // =========================================================== }
看到代码,其实里面的东西是比较少的,大致归纳起来就俩个方面:一个封装的Toast(主要是让toast永远都在UI线程中)!再者就是几个doAsync()方法(主要就是封装好了一个异步任务),其具体实现在ActivityUtils中:
public static <T> void doAsync(final Context pContext, final CharSequence pTitle, final CharSequence pMessage, final Callable<T> pCallable, final Callback<T> pCallback, final Callback<Exception> pExceptionCallback, final boolean pCancelable) { new AsyncTask<Void, Void, T>() { private ProgressDialog mPD; private Exception mException = null; @Override public void onPreExecute() { this.mPD = ProgressDialog.show(pContext, pTitle, pMessage, true, pCancelable); if(pCancelable) { this.mPD.setOnCancelListener(new OnCancelListener() { @Override public void onCancel(final DialogInterface pDialogInterface) { pExceptionCallback.onCallback(new CancelledException()); pDialogInterface.dismiss(); } }); } super.onPreExecute(); } @Override public T doInBackground(final Void... params) { try { return pCallable.call(); } catch (final Exception e) { this.mException = e; } return null; } @Override public void onPostExecute(final T result) { try { this.mPD.dismiss(); } catch (final Exception e) { Debug.e("Error", e); } if(this.isCancelled()) { this.mException = new CancelledException(); } if(this.mException == null) { pCallback.onCallback(result); } else { if(pExceptionCallback == null) { Debug.e("Error", this.mException); } else { pExceptionCallback.onCallback(this.mException); } } super.onPostExecute(result); } }.execute((Void[]) null); }
接着,我们看下其子类BaseGameActivity,由于代码较多,就不贴了。
@Override protected void onCreate(final Bundle pSavedInstanceState) { if(BuildConfig.DEBUG) { Debug.d(this.getClass().getSimpleName() + ".onCreate" + " @(Thread: '" + Thread.currentThread().getName() + "')"); } super.onCreate(pSavedInstanceState); this.mGamePaused = true; this.mEngine = this.onCreateEngine(this.onCreateEngineOptions());this.mEngine.startUpdateThread();this.applyEngineOptions();this.onSetContentView();}
这里面很清楚看到调用几个方法,我们依次来看看!
this.mEngine = this.onCreateEngine(this.onCreateEngineOptions());嘿嘿,mEngine看到就高兴了!这个就是AndEngine中的engine对象了,而且,当activity运行起来的时候,就会尝试去构造一个AndEngine对象,其实现方式在:
@Override public Engine onCreateEngine(final EngineOptions pEngineOptions) { return new Engine(pEngineOptions); }可以看到,其实就只构造了一个Engine,但是,我们不能忽略的是其参数中EngineOptions pEngineOptions,那我们来看看这个参数是如何来的?
this.onCreateEngineOptions()是这样来的,那么这个this指的是什么呢?----IGameInterface!
但是在BaseGameActivity中是没有实现这个借口的,那意思就是说,他的子类就必须实现这个方法了,所以导致其子类SimpleBaseGameActivity中就必须实现这个方法!而且,程序最开始加载的就是这个方法。然后就是开启引擎的线程了,这个以后再研究。
紧接着就是,applyEngineOptions(),我们来看看其实现方式:private void applyEngineOptions() { final EngineOptions engineOptions = this.mEngine.getEngineOptions(); if(engineOptions.isFullscreen()) { ActivityUtils.requestFullscreen(this); } if(engineOptions.getAudioOptions().needsMusic() || engineOptions.getAudioOptions().needsSound()) { this.setVolumeControlStream(AudioManager.STREAM_MUSIC); } switch(engineOptions.getScreenOrientation()) { case LANDSCAPE_FIXED: this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); break; case LANDSCAPE_SENSOR: if(SystemUtils.SDK_VERSION_GINGERBREAD_OR_LATER) { this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE); } else { Debug.w(ScreenOrientation.class.getSimpleName() + "." + ScreenOrientation.LANDSCAPE_SENSOR + " is not supported on this device. Falling back to " + ScreenOrientation.class.getSimpleName() + "." + ScreenOrientation.LANDSCAPE_FIXED); this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); } break; case PORTRAIT_FIXED: this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); break; case PORTRAIT_SENSOR: if(SystemUtils.SDK_VERSION_GINGERBREAD_OR_LATER) { this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT); } else { Debug.w(ScreenOrientation.class.getSimpleName() + "." + ScreenOrientation.PORTRAIT_SENSOR + " is not supported on this device. Falling back to " + ScreenOrientation.class.getSimpleName() + "." + ScreenOrientation.PORTRAIT_FIXED); this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } break; } }这个主要干的就是,应用引擎的配置,如是否全屏、是否开启声音、图像布局方式等。
再来就是,this.onSetContentView();我们看看其实现:
protected void onSetContentView() { this.mRenderSurfaceView = new RenderSurfaceView(this); this.mRenderSurfaceView.setRenderer(this.mEngine, this); this.setContentView(this.mRenderSurfaceView, BaseGameActivity.createSurfaceViewLayoutParams()); }虽然,从这来看其实就是设置显示的视图。看起来很简单,但是我们要注意:
this.mRenderSurfaceView = new RenderSurfaceView(this); this.mRenderSurfaceView.setRenderer(this.mEngine, this);这俩行代码,这俩行真是太重要了,整个引擎的渲染、运行都是由这俩行开始的,我们来具体看下。
this.mRenderSurfaceView = new RenderSurfaceView(this);构造一个RenderSurfaceView对象。可以看到RenderSurfaceView其实又是对OpenGL GLSurfaceView的一层封装。重点来了,我们看
this.mRenderSurfaceView.setRenderer(this.mEngine, this);其实现是:
public void setRenderer(final Engine pEngine, final IRendererListener pRendererListener) { if(this.mConfigChooser == null) { final boolean multiSampling = pEngine.getEngineOptions().getRenderOptions().isMultiSampling(); this.mConfigChooser = new ConfigChooser(multiSampling); } this.setEGLConfigChooser(this.mConfigChooser); this.setOnTouchListener(pEngine); this.mEngineRenderer = new EngineRenderer(pEngine, this.mConfigChooser, pRendererListener); this.setRenderer(this.mEngineRenderer); }嘿嘿,这里面的this,原来是IRendererListener这个,源代码如下:
public interface IRendererListener { // =========================================================== // Constants // =========================================================== public void onSurfaceCreated(final GLState pGlState); public void onSurfaceChanged(final GLState pGlState, final int pWidth, final int pHeight); // =========================================================== // Methods // =========================================================== }看名字就应该能猜出个大致意思了,待会,我们在BaseGameActivity中是如何实现这个接口的。现在还是继续看setRender里面的东西,
this.mEngineRenderer = new EngineRenderer(pEngine, this.mConfigChooser, pRendererListener); this.setRenderer(this.mEngineRenderer);这个,构造了EngineRender,我们跟进去看下:
public class EngineRenderer implements GLSurfaceView.Renderer { // =========================================================== // Constants // =========================================================== // =========================================================== // Fields // =========================================================== final Engine mEngine; final ConfigChooser mConfigChooser; final boolean mMultiSampling; final IRendererListener mRendererListener; final GLState mGLState; // =========================================================== // Constructors // =========================================================== public EngineRenderer(final Engine pEngine, final ConfigChooser pConfigChooser, final IRendererListener pRendererListener) { this.mEngine = pEngine; this.mConfigChooser = pConfigChooser; this.mRendererListener = pRendererListener; this.mGLState = new GLState(); this.mMultiSampling = this.mEngine.getEngineOptions().getRenderOptions().isMultiSampling(); } // =========================================================== // Getter & Setter // =========================================================== // =========================================================== // Methods for/from SuperClass/Interfaces // =========================================================== @Override public void onSurfaceCreated(final GL10 pGL, final EGLConfig pEGLConfig) { synchronized(GLState.class) { final RenderOptions renderOptions = this.mEngine.getEngineOptions().getRenderOptions(); this.mGLState.reset(renderOptions, this.mConfigChooser, pEGLConfig); // TODO Check if available and make available through EngineOptions-RenderOptions // GLES20.glEnable(GLES20.GL_POLYGON_SMOOTH); // GLES20.glHint(GLES20.GL_POLYGON_SMOOTH_HINT, GLES20.GL_NICEST); // GLES20.glEnable(GLES20.GL_LINE_SMOOTH); // GLES20.glHint(GLES20.GL_LINE_SMOOTH_HINT, GLES20.GL_NICEST); // GLES20.glEnable(GLES20.GL_POINT_SMOOTH); // GLES20.glHint(GLES20.GL_POINT_SMOOTH_HINT, GLES20.GL_NICEST); this.mGLState.disableDepthTest(); this.mGLState.enableBlend(); this.mGLState.setDitherEnabled(renderOptions.isDithering()); /* Enabling culling doesn't really make sense, because triangles are never drawn 'backwards' on purpose. */ // this.mGLState.enableCulling(); // GLES20.glFrontFace(GLES20.GL_CCW); // GLES20.glCullFace(GLES20.GL_BACK); if(this.mRendererListener != null) { this.mRendererListener.onSurfaceCreated(this.mGLState); } } } @Override public void onSurfaceChanged(final GL10 pGL, final int pWidth, final int pHeight) { this.mEngine.setSurfaceSize(pWidth, pHeight); GLES20.glViewport(0, 0, pWidth, pHeight); this.mGLState.loadProjectionGLMatrixIdentity(); if(this.mRendererListener != null) { this.mRendererListener.onSurfaceChanged(this.mGLState, pWidth, pHeight); } } @Override public void onDrawFrame(final GL10 pGL) { synchronized(GLState.class) { if (this.mMultiSampling && this.mConfigChooser.isCoverageMultiSampling()) { final int GL_COVERAGE_BUFFER_BIT_NV = 0x8000; GLES20.glClear(GL_COVERAGE_BUFFER_BIT_NV); } try { this.mEngine.onDrawFrame(this.mGLState); } catch (final InterruptedException e) { Debug.e("GLThread interrupted!", e); } } } // =========================================================== // Methods // =========================================================== // =========================================================== // Inner and Anonymous Classes // =========================================================== }我们看到,它里面是回调了IRendererListener的俩个方法的,至于,如何回调,回调机制如何,那就和OpenGL相关了,不讨论!
看到这里面,我们大致知道了,当oncreate的时候,会去创建引擎,然后,引擎创建render,然后等render创建之后,又回调到BaseGameActivity中!现在,我们回到BaseGameActivity中,看看其回调的实现:
@Override public synchronized void onSurfaceCreated(final GLState pGLState) { if(BuildConfig.DEBUG) { Debug.d(this.getClass().getSimpleName() + ".onSurfaceCreated" + " @(Thread: '" + Thread.currentThread().getName() + "')"); } if(this.mGameCreated) { this.onReloadResources(); if(this.mGamePaused && this.mGameCreated) { this.onResumeGame(); } } else { if(this.mCreateGameCalled) { this.mOnReloadResourcesScheduled = true; } else { this.mCreateGameCalled = true; this.onCreateGame(); } } } @Override public synchronized void onSurfaceChanged(final GLState pGLState, final int pWidth, final int pHeight) { if(BuildConfig.DEBUG) { Debug.d(this.getClass().getSimpleName() + ".onSurfaceChanged(Width=" + pWidth + ", Height=" + pHeight + ")" + " @(Thread: '" + Thread.currentThread().getName() + "')"); } }我们,具体看
public synchronized void onSurfaceCreated(final GLState pGLState)它首先会调用onCreateGame(),其实现代码就省略了,我们可以看到里面就执行了一句:
this.onCreateResources(onCreateResourcesCallback);而这个onCreateResources也是IGameInterface中的一个方法,其具体实现就是:
@Override public final void onCreateResources(final OnCreateResourcesCallback pOnCreateResourcesCallback) throws Exception { this.onCreateResources(); pOnCreateResourcesCallback.onCreateResourcesFinished(); }其中:
protected abstract void onCreateResources();那么,其子类就必须实现这个方法,这个方法就是load我们游戏中可能会用到的资源,当资源load完成之后,就回调到BaseGameActivity中的onCreateGame()去,根据源码将执行:
final OnCreateResourcesCallback onCreateResourcesCallback = new OnCreateResourcesCallback() { @Override public void onCreateResourcesFinished() { try { if(BuildConfig.DEBUG) { Debug.d(BaseGameActivity.this.getClass().getSimpleName() + ".onCreateScene" + " @(Thread: '" + Thread.currentThread().getName() + "')"); } BaseGameActivity.this.onCreateScene(onCreateSceneCallback); } catch(final Throwable pThrowable) { Debug.e(BaseGameActivity.this.getClass().getSimpleName() + ".onCreateScene failed." + " @(Thread: '" + Thread.currentThread().getName() + "')", pThrowable); } } };同样,onCreateScene也是IGameInterface中的一个方法,其实现同样也是在BaseGameActivity的子类,SimpleBaseGameActivity中实现的:
@Override public final void onCreateScene(final OnCreateSceneCallback pOnCreateSceneCallback) throws Exception { final Scene scene = this.onCreateScene(); pOnCreateSceneCallback.onCreateSceneFinished(scene); }而,onCreateScene也是抽象方法,需在其子类中实现!通过分享到这,我们就可以大致得到AndEngine的大致流程了:
onCreateEngineOptions() ------onCreateResources()--------onCreateScene()
当onCreateScene()执行完毕之后,又会想上面那样回调,然后执行onPopulateScene(),然后回调执行onGameCreated();这样整个游戏框架就跑起来了!
其他的就不具体说了,感觉有点多,说不清了!但是有点需要注意的是:
BaseGameActivity中,防着Activity的生命周期,写了游戏的生命周期,onGameCreated()类似的,根据网上找的前辈的经验,尽量使用AndEngine的声明为好!支持,完毕,有点乱!
总体来说,我们使用AndEngine的时候,只需要继承SimpleBaseGameActivity并实现onCreateEngineOptions() ------onCreateResources()--------onCreateScene()就可以了,其执行顺序为onCreateEngineOptions() ------onCreateResources()--------onCreateScene()!