刚开始学习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();}这里面很清楚看到调用几个方法,我们依次来看看!
嘿嘿,mEngine看到就高兴了!这个就是AndEngine中的engine对象了,而且,当activity运行起来的时候,就会尝试去构造一个AndEngine对象,其实现方式在:this.mEngine = this.onCreateEngine(this.onCreateEngineOptions());
@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);
这俩行代码,这俩行真是太重要了,整个引擎的渲染、运行都是由这俩行开始的,我们来具体看下。
构造一个RenderSurfaceView对象。可以看到RenderSurfaceView其实又是对OpenGL GLSurfaceView的一层封装。重点来了,我们看this.mRenderSurfaceView = new RenderSurfaceView(this);
其实现是: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里面的东西,
这个,构造了EngineRender,我们跟进去看下:this.mEngineRenderer = new EngineRenderer(pEngine, this.mConfigChooser, pRendererListener); this.setRenderer(this.mEngineRenderer);
我们看到,它里面是回调了IRendererListener的俩个方法的,至于,如何回调,回调机制如何,那就和OpenGL相关了,不讨论!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 // =========================================================== }
看到这里面,我们大致知道了,当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() + "')"); } }
我们,具体看
它首先会调用onCreateGame(),其实现代码就省略了,我们可以看到里面就执行了一句:public synchronized void onSurfaceCreated(final GLState pGLState)
this.onCreateResources(onCreateResourcesCallback);
而这个onCreateResources也是IGameInterface中的一个方法,其具体实现就是:
其中:@Override public final void onCreateResources(final OnCreateResourcesCallback pOnCreateResourcesCallback) throws Exception { this.onCreateResources(); pOnCreateResourcesCallback.onCreateResourcesFinished(); }
那么,其子类就必须实现这个方法,这个方法就是load我们游戏中可能会用到的资源,当资源load完成之后,就回调到BaseGameActivity中的onCreateGame()去,根据源码将执行:protected abstract void onCreateResources();
同样,onCreateScene也是IGameInterface中的一个方法,其实现同样也是在BaseGameActivity的子类,SimpleBaseGameActivity中实现的: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); } } };
@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()!