最近开始接触Android游戏开发,刚开始有些无所适从,因为不知道该从何处入手,不知道Android游戏引擎有哪些,哪些好。
那么首先,便开始搜索Android搜索引擎有哪些。到网上一搜:诸如什么八大安卓游戏引擎,Android游戏分类总汇神马的。那么看了总结了下有以下一些(具体的就不细究了,大概分析下):
1.Angle:最新版是2010年的,加上文档少,样例少,直接就排除了。
2.Rokon:在以前貌似很流行的一款游戏引擎,但是后来停更了,所以同样排除了。
3.Andengine:Rokon停更之后最流行的游戏引擎。(本文也是专门针对于这款游戏引擎作介绍)
基于libGDX框架开发,使用OpenGL ES进行图形绘制,集成Box2D物理引擎。
优点:高效、开源、扩展丰富等等。
缺点:没有官方文档,网上文档教程较少,学习难度大(这点。。很尴尬,找到的教程还很多都是旧的,没有跟上更新,导致原模原样的代码Copy下来,还仍有一大堆不知道原因的错误),好在提供的大量的实例源码。
4.LGame:也是由于更新进度问题,也不再讨论。
5.Libgdx:这一款也是很流行的游戏引擎,Andengine包括很多游戏引擎都是基于它开发的,更新也跟着,官网文档也相当完备,也有大量实例源码。还有:高效、跨平台兼容性强等一系列优点。
6.Cocos2d:非常流行的一款,但是因为使用C++语言开发,也不进行讨论了。
最后再列举一些3D的:JPCT、Alien3d、Catcake。3D的网上推荐:Unity3d具体也不怎么样,没接触过。、
最后剩下优先考虑的就是Andengine和Libgdx,而这两款中哪个好呢?不是很清楚,据说(网上百度的):AndEngine作为游戏引擎虽然在功能上较Libgdx更为丰富也更人性化,但相比Libgdx的绘图渲染机能却逊色不少(粗读源码可以发现,Libgdx有较为完善的OpenGLES环境适应性,而AndEngine在这方面的投入明显不足)。事实上,市井也一直有流言说AndEngine引擎在不同手机机型上的表现并不稳定。
在安卓游戏引擎的种类,有了一定的了解之后,我不禁疑惑,为啥要使用游戏引擎,游戏引擎是个啥。官方的说法呢是:它是已编写好的可编辑游戏系统或者一些交互式实时图像应用程序的核心组件,游戏软件的主程序一款游戏的核心,游戏运行的基础......刚开始我觉得只要认识到,反正它是非常重要来的就对了,而具体的感受我可能等接触了以后才会对它产生一个更好的认识。
前面例举了那么多的游戏引擎,那么接下来针对几款游戏引擎的环境搭建(仅简单叙述下我搭建过的)进行一个了解,从而才能对游戏引擎进行使用,更好的了解。
首先介绍下Rokon的环境搭建,当然由于已经停更,所以只做一个了解就行,它的环境搭建非常简单,首先下载开发包,只需要其中的 [rokon_lib_x-y-z.zip]就行点击打开链接
然后创建项目,Java Build Path,把rokon-2-0-3.jar作为引用库添加到项目就行。这里提供一个简单的显示代码供参考,测试。点击打开链接。
再介绍下Libgdx的环境搭建,在github上下载(是的,项目已经放到了github上了),https://github.com/libgdx/libgdx/
下载完成后是这样的:
然后只需复制其中的gdx.jar和gdx-backend-android.jar,增加引用,然后将armeabi和armeabi-v7a两个文件夹复制到libs文件夹中即可。这里同样附上一小段测试代码点击打开链接
这里重点讲下Andengine的环境搭建:
1.下载AndEngine源码、example和几个扩展包
AndEngine:
https://github.com/nicolasgramlich/AndEngine
AndEngineExamples:
https://github.com/nicolasgramlich/AndEngineExamples
要用到的9个扩展包:
AndEngineLiveWallpaperExtension:
https://github.com/nicolasgramlich/AndEngineLiveWallpaperExtension
AndEngineTexturePackerExtension:
https://github.com/nicolasgramlich/AndEngineTexturePackerExtension
AndEngineTMXTiledMapExtension:
https://github.com/nicolasgramlich/AndEngineTMXTiledMapExtension
AndEngineMultiplayerExtension:
https://github.com/nicolasgramlich/AndEngineMultiplayerExtension
AndEnginePhysicsBox2DExtension:
https://github.com/nicolasgramlich/AndEnginePhysicsBox2DExtension
AndEngineScriptingExtension:
https://github.com/nicolasgramlich/AndEngineScriptingExtension
AndEngineSVGTextureRegionExtension:
https://github.com/nicolasgramlich/AndEngineSVGTextureRegionExtension
AndEngineMODPlayerExtension:
https://github.com/nicolasgramlich/AndEngineMODPlayerExtension
AndEngineAugmentedRealityExtension:
https://github.com/nicolasgramlich/AndEngineAugmentedRealityExtension
把上面的所有工程全部导入到workspace中。(注意:把AndEngine以及上述所有工程的target SDK改成你有的SDK版本)
然后你会发现,同样有许许多多的错误,然后将一些错的地方做一个修改:
BoundCameraExample中的220行:
final AnimatedSprite face = new AnimatedSprite(pX, pY, this.mBoxFaceTextureRegion, this.getVertexBufferObjectManager()).animate(100);
这里修改为:
final AnimatedSprite face = new AnimatedSprite(pX, pY, this.mBoxFaceTextureRegion, this.getVertexBufferObjectManager());
face.animate(100);
HullAlgorithmExample中的11行、168行、175行:
import org.andengine.entity.primitive.vbo.DrawMode;
this.mMesh = new Mesh(centerX, centerY, this.mMeshVertices, this.mMeshVertexCount, DrawMode.LINE_LOOP, this.getVertexBufferObjectManager(), DrawType.STATIC);
this.mHull = new Mesh(centerX, centerY, this.mHullVertices, this.mHullVertexCount, DrawMode.LINE_LOOP, this.getVertexBufferObjectManager(), DrawType.STATIC);
这里是import的导入的类不对,改成import org.andengine.entity.primitive.DrawMode;或者使用快捷键
alt ctrl o或者ctrl shift o完成导入。
SplitScreenExample中的179行:
改成和上面的BoundCameraExample中的220行的一样即可。final AnimatedSprite face = new AnimatedSprite(pX, pY, this.mBoxFaceTextureRegion, this.getVertexBufferObjectManager()).animate(100);
TextBreakExample中的106行:
this.mText = new Text(50, 40, this.mFont, "", 1000, new TextOptions(AutoWrap.LETTERS, AUTOWRAP_WIDTH, Text.LEADING_DEFAULT, HorizontalAlign.CENTER), vertexBufferObjectManager);
这里是TextOptions的参数有问题,修改成
this.mText = new Text(50, 40, this.mFont, "", 1000, new TextOptions(AutoWrap.LETTERS, AUTOWRAP_WIDTH, HorizontalAlign.CENTER,Text.LEADING_DEFAULT), vertexBufferObjectManager);
以上配置是从此转载:
三、对Andengine的简单了解
看Andengine的介绍:是使用(大多数游戏引擎也是基于此的)OpenGL ES进行图形绘制的(集成Box2D物理引擎方面在此就暂不叙述了),那么OpenGL ES是个呢,所以在此首先对OpenGL ES进行一个初步了解。
说OpenGL ES首先得先说明OpenGL(Open Graphics Library),即开放的图形库接口,然后是应用在图形工作站,个人PC....不扯这么多了,反正差不多,OpenGL是在上面这些地方可以用来进行图形处理的,后来差不多弄了个子集,OpenGL ES在小型设备上进行处理,而Android内置了。
然后做几个在Android应用中使用OpenGL ES的例子,总结下这个使用过程:
1.实现GLSurfaceView.Renderer实例(Android为OpenGL ES支持提供了GLSurfaceView,用这个组件显示3D图形,而它本身并不提供绘制3D图形的功能,而是由GLSurfaceView.Renderer来完成绘制,所以得去实现它咯)。
而实现Renderer这个接口必须实现以下三个方法:
abstract void onSurfaceCreated(GL10 gl, EGLConfig config):当GLSurfaceView被创建时回调该方法。
abstract void onSurfaceChanged(GL10 gl, int width, int height):当GLSurfaceView大小改变时回调该方法。
abstract void onDrawFrame(GL10 gl):Renderer对象调用该方法绘制GLSurfaceView的当前帧。
前面2个方法呢,一些配置内容七七八八都差不多,只是在需要应用纹理贴图的时候,需要在onSurfaceCreated中启用2D纹理贴图,然后装载纹理。
// 启用2D纹理贴图
gl.glEnable(GL10.GL_TEXTURE_2D);
// 装载纹理
loadTexture(gl);
重点呢是在第三个方法:onDrawFrame(GL10 gl)这个绘制过程呢大致如下
// 清除屏幕缓存和深度缓存
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
//第一步:启用顶点座标数据
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
//第二步:启用顶点颜色数据
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
//在应用贴图纹理时,需要启用贴图座标数组数据
// gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
// 设置当前矩阵堆栈为模型堆栈,
gl.glMatrixMode(GL10.GL_MODELVIEW);
// --------------------绘制一个图形---------------------
// 重置当前的模型视图矩阵
gl.glLoadIdentity();
gl.glTranslatef(-0.32f, 0.35f, -1.5f);
需要旋转的时候加上这个:glRotatef(float angle, float x, float y, float z),然后再最后每次增加旋转角度即可
//第三步:设置顶点的位置数据//应用纹理贴图时使用贴图坐标数据
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, triangleDataBuffer);
//第四步: 设置顶点的颜色数据//应用纹理贴图时执行纹理贴图
gl.glColorPointer(4, GL10.GL_FIXED, 0, triangleColorBuffer);
// 第五步:根据顶点数据绘制平面图形,在绘制三维空间上的多个点时使用glDrawElements方法进行绘制
gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);
.......
// 绘制结束
gl.glFinish();
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
2.在实现了Renderer后,在Activity中创建GLsurfaceView组件,并且制定Renderer对象,然后在Activity中绘制布局即可。
顺便在这里给到这些demo供参考:点击打开链接(来源于Android疯狂讲义)
效果展示:
在了解了一部分OpenGL ES后,那么接下来对Andengine做一个了解。Andengine没有官方文档,那么只能从dome里面来学习,那么首先我们先看看它的第一个dome做一个了解,把LineExample 单独拿出来分析分析:
(声明:推荐可以看下兔子的窝的博客,对例子的解析很详细以下部分内容也从中引用的:http://blog.csdn.net/chenglifan)
1.建一个Android项目,然后复制导入Andengine的jar包,就可以开始编程使用了。
2.把LineExamole的代码单独拿出来:
public class LineExample extends SimpleBaseGameActivity {
//SimpleBaseGameActivity子类
private static final long RANDOM_SEED = 1234567890;
//camera的宽、高
private static final int CAMERA_WIDTH = 720;
private static final int CAMERA_HEIGHT = 480;
//画的线的条数
private static final int LINE_COUNT = 100;
//必须重写三个方法public
//EngineOptions onCreateEngineOptions()
//public void onCreateResources()
//public Scene onCreateScene()
public EngineOptions onCreateEngineOptions() {
//创建初始化Camera
final Camera camera = new Camera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
//EngineOptions
//参数是否全屏、 屏幕的方向(固定横向、可变横向、固定纵向、可变纵向)、分辨率策略,实现繁多、视野
return new EngineOptions(true, ScreenOrientation.LANDSCAPE_FIXED, new RatioResolutionPolicy(CAMERA_WIDTH, CAMERA_HEIGHT), camera);
}
public void onCreateResources() {
}
public Scene onCreateScene() {
//初始化每秒帧
this.mEngine.registerUpdateHandler(new FPSLogger());
//创建一个Scene
final Scene scene = new Scene();
//设置Scence的背景色
scene.setBackground(new Background(0.09804f, 0.6274f, 0.8784f));
//初始化一个Random对象,种子为1234567890
final Random random = new Random(RANDOM_SEED);
final VertexBufferObjectManager vertexBufferObjectManager = this.getVertexBufferObjectManager();
for(int i = 0; i < LINE_COUNT; i++) {
//0-99循环,随机产生一个0-1之间单精度浮点型的随机数乘以...产生四个坐标最后构成直线的2个点
final float x1 = random.nextFloat() * CAMERA_WIDTH;
final float x2 = random.nextFloat() * CAMERA_WIDTH;
final float y1 = random.nextFloat() * CAMERA_HEIGHT;
final float y2 = random.nextFloat() * CAMERA_HEIGHT;
//长度
final float lineWidth = random.nextFloat() * 5;
//创建一个Line坐标,长度
final Line line = new Line(x1, y1, x2, y2, lineWidth, vertexBufferObjectManager);
//设置直线颜色,颜色随机产生
line.setColor(random.nextFloat(), random.nextFloat(), random.nextFloat());
//然后将之间加到Scene里面,最后返回
scene.attachChild(line);
}
return scene;
}
}
程序应该很好理解,在每一步,都进行了注释,这只是游戏引擎的使用。那么实际的内容是什么样的呢,继续往下分析:
一、首先我们这个Activity继承了SimpleBaseGameActivity这个类,那么这个类是神马呢,翻开Andengine源码找到它,先不管里面内容,再看SimpleBaseGameActivity又继承BaseGameActivity。BaseGameActivity这个类呢,是这个游戏引擎中很重要的一个基础类,它又实现了这个IGameInterface接口,并且实现了其中的一部分方法。
再回过头来看SimpleBaseGameActivity这个类,实现了三个方法
1.onCreateResources
2.onCreateScene
3.onPopulateScene
这三个方法呢,均来自IGameInterface接口,在BaseGameActivity没有实现,那么在SimpleBaseGameActivity中实现它了,而SimpleBaseGameActivity中又定义了2个抽象方法(onCreateResources、onCreateScene中调用了下面两个抽象方法):
1.protected abstract void onCreateResources();
2.protected abstract Scene onCreateScene();
而在BaseGameActivity类中还有一个IGameInterface接口中的onCreateEngineOptions方法木有实现,这就是为什么我们在继承SimpleBaseGameActivity类来使用游戏引擎必须实现3个方法的原因了。
二、再看下的加载过程:
Activity.onCreate
看到BaseGameActivity中的onCreate中:
1.this.mEngine = this.onCreateEngine(this.onCreateEngineOptions());
使用onCreateEngine初始化了一个Engine类的对象,而这方法的参数呢,正是onCreateEngineOptions()的返回值,前面我们已经实现了它。
2.onCreate中onSetContentView();方法构建了Activity,并显示了布局
protected void onSetContentView() {
this.mRenderSurfaceView = new RenderSurfaceView(this);
this.mRenderSurfaceView.setRenderer(this.mEngine, this);
this.setContentView(this.mRenderSurfaceView, BaseGameActivity.createSurfaceViewLayoutParams());
}
然后惊奇的发现,这个过程非常眼熟,没错就是前面OpenGL ES的进行开发的总结过程。
创建一个GLSurfaceView组件,而mRenderSurfaceView所属RenderSurfaceView类正好继承了GLSurfaceView!
利用GLSurfaceView的setRenderer()方法指定Renderer对象
而RenderSurfaceView这个类重载了这个方法,看看这个类里面的setRenderer()方法:
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);
}
创建了一个EngineRenderer对象,然后使用了GLSurfaceView的setRenderer()方法,而EngineRenderer正是实现了if (this.mGameCreated) {其中有调用onCreateGame()方法,而在这个方法里面实现了
this.onReloadResources();
if (this.mGamePaused && this.mGameCreated) {
this.onResumeGame();
}
} else {
if (this.mCreateGameCalled) {
this.mOnReloadResourcesScheduled = true;
} else {
this.mCreateGameCalled = true;
this.onCreateGame();
}
}
(2)onCreateEngineOptions --onCreate方法中首先要求用户指定EngineOptions配置信息,构建Engine和Acivity。
(3)onSetContentView --> onSurfaceCreated --> onReloadResources --> onCreateGame --这部分调用并没有要求我们实现任何东西,第一个是创建GLSurfaceView加入Activity中;第二个是GLSurfaceView创建完成的回调方法,调用后面两个方法;第三个是要求Engine重载资源;第四个中开始一组回调。
(4)onCreateResources --要求用户创建资源,完成后回调onCreateResourcesCallback。
(5)onCreateScene --要求用户创建屏幕,完成后回调onCreateSceneCallback。
(6)onPopulateScene --要求用户填充屏幕,完成后回调onPopulateSceneCallback。
(7)onGameCreated --所有创建过程完成。 onResumeGame --引擎开始运作
在了解了整个加载过程后,那么继续分析,使用这个游戏引擎的第一步
EngineOptions onCreateEngineOptions()方法~看里面,第一步创建了一个Camera,Camera是什么呢?用来干啥,往下看到是创建了一个EngineOptions对象,然后开始一系列的加载过程,而上一步的Camera也跟着传递过去了。
而这个Camera传递到哪去了呢?看到Engine类里面的构造方法,发现在创建这个对象时,把用户创建的Camera传递到了这赋给了Engine这个类中的Camera成员变量:
this.mCamera = pEngineOptions.getCamera();
而他用了这Camera干了什么?
1.设置大小
看到Engine中的这两个方法,
public void setSurfaceSize(final int pSurfaceWidth, final int pSurfaceHeight) {
this.mSurfaceWidth = pSurfaceWidth;
this.mSurfaceHeight = pSurfaceHeight;
this.onUpdateCameraSurface();
}
protected void onUpdateCameraSurface() {
this.mCamera.setSurfaceSize(0, 0, this.mSurfaceWidth, this.mSurfaceHeight);
}
第一个这个方法
在EngineRenderer这个类中的onSurfaceChanged方法中被调用,
public void onSurfaceChanged(final GL10 pGL, final int pWidth, final int pHeight) {
this.mEngine.setSurfaceSize(pWidth, pHeight);
.......
而第二个方法的setSurfaceSize调用了下面这个方法
public void setSurfaceSize(final int pSurfaceX, final int pSurfaceY, final int pSurfaceWidth, final int pSurfaceHeight) {
if(this.mSurfaceHeight == 0 && this.mSurfaceWidth == 0) {
this.onSurfaceSizeInitialized(pSurfaceX, pSurfaceY, pSurfaceWidth, pSurfaceHeight);
} else if(this.mSurfaceWidth != pSurfaceWidth || this.mSurfaceHeight != pSurfaceHeight) {
this.onSurfaceSizeChanged(this.mSurfaceX, this.mSurfaceY, this.mSurfaceWidth, this.mSurfaceHeight, pSurfaceX, pSurfaceY, pSurfaceWidth, pSurfaceHeight);
}
}
上面其中都方法最后都涉及到了:
this.mSurfaceX = pSurfaceX;
this.mSurfaceY = pSurfaceY;
this.mSurfaceWidth = pSurfaceWidth;
this.mSurfaceHeight = pSurfaceHeight;
所以得出了它的一个功能:绘制区域的大小。(但到底是不是呢,其实我也不知道。。。我也只是百度了下,然后按上面的顺便看了下源码。。大家随意感受下就行)
2.利用HUD类实际绘制游戏屏幕于手机之上
继续看到源码的Engine中,
而这个方法呢,估计就是绘制用的啦。
onCreateEngineOptions方法走完,一看onCreateResources()里面木有内容,万幸接下来到了protected Scene onCreateScene()方法,这个整个内容差不多就是创建一个Scene,然后随机在里面画100条颜色随机的直线。写到这内容太多,许多地方没弄清,加上有点混乱,就以后再总结吧,先初探到这。