现在的位置: 首页 > 综合 > 正文

AndEngine—-初识AndEngine

2013年10月04日 ⁄ 综合 ⁄ 共 17587字 ⁄ 字号 评论关闭

刚开始学习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()!

抱歉!评论已关闭.