65.9K
CodeProject 正在变化。 阅读更多。
Home

简单的 Android 球类游戏

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.75/5 (41投票s)

2011 年 4 月 29 日

CPOL

4分钟阅读

viewsIcon

558322

downloadIcon

67585

关于使用 AndEngine 为 Android 创建精美球类游戏的教程。

引言

本教程将介绍如何在 Android 操作系统上开发一款弹球游戏。它将引导你使用 AndEngine(免费的 Android 2D 开源游戏引擎)来开发 Android 游戏。要理解本教程,你需要对 AndEngine 和 Java 编程有一定的基本了解。如果你对 AndEngine 不熟悉,请查阅此 链接。本教程的目标是教你如何制作自己的 Android 游戏。

游戏

这是一个经典的怀旧游戏创意,以现代的方式实现。你将通过加速度计传感器控制弹球,目标是收集所有“钻石”,躲避敌方弹球,并安全到达终点。

pic1.jpg

pic2.jpg

pic3.jpg

pic4.jpg

开始需要什么?

要开始开发你自己的 Android 游戏,你需要 SDK 和游戏引擎(这将使你的生活轻松很多!)。我使用的是 Eclipse SDK 和一个名为 AndEngine 的游戏引擎。你想知道如何将 Eclipse SDK 和 AndEngine 结合起来设置吗?点击此链接查看“AndEngine 入门”教程。

开始编码吧!

让我们来看看代码隐藏了什么!请注意,我没有在此处粘贴完整的源代码。我只粘贴了一些代码片段。你可以下载完整的项目以查看完整的源代码。

首先,我们将简要浏览一下 main 类。

public class GameLogicController extends BaseGameActivity 
	implements IAccelerometerListener{
                 public PlayerProfileManager playerProfileManager; 
                 public LevelController levelController;
                 private Camera camera;
                 protected PhysicsWorld mPhysicsWorld;
                 public Texture mTexture;
                 public TextureRegion enemyTextureRegion;
                 private float mGravityX;
                 private float mGravityY;
                 private final Vector2 mTempVector = new Vector2();
                 public TiledTextureRegion mCircleFaceTextureRegion;
                 private RepeatingSpriteBackground mGrassBackground;
                 private Sound mGameOverSound;
                 ...
}

GameLogicController 是我们游戏的基础类,BaseGameActivity 是 AndEngine 的基础类,而 IAccelerometerListener 是用于获取手机加速度计传感器数据的接口。PlayerProfileManagerLevelController 只是你稍后会了解的一些游戏类。

@Override
public Engine onLoadEngine() {
	...

	levelController.mCameraWidth = 460;
	levelController.mCameraHeight = 320;
	camera = new Camera(0, 0, levelController.mCameraWidth, 
			levelController.mCameraHeight);
	return new Engine(new EngineOptions(true, ScreenOrientation.LANDSCAPE, 
	new RatioResolutionPolicy(levelController.mCameraWidth, 
	levelController.mCameraHeight), camera).setNeedsSound(true));  
}

onLoadEngine 方法会创建一个新的游戏引擎。首先,我们需要创建一个 Camera 来声明屏幕分辨率。一个重要且非常有用的特性是,Camera 的分辨率与手机分辨率无关。这将为你节省大量关于图形位置的工作!例如,我们声明了 460x320 的屏幕分辨率。你无需关心手机的分辨率,因为你将始终使用 AndEngine 的分辨率(460x320),AndEngine 会自动将 AndEngine 分辨率重新计算为实际的手机分辨率。下一步是创建一个新的 Engine。你只需要声明屏幕方向(横向或纵向)、屏幕分辨率和自定义选项。我们只使用了一个自定义选项,用于启用声音。

public Scene newGameLevelScene(int levelId){
	Scene scene = new Scene(2);
	this.mPhysicsWorld = new FixedStepPhysicsWorld(60, new Vector2(0, 0), false);
	levelController.setScene(scene);
	levelController.setmPhysicsWorld(mPhysicsWorld);
	levelController.createFrame();
	levelController.loadLevel(levelId);
	this.enableAccelerometerSensor(this);
	scene.registerUpdateHandler(this.mPhysicsWorld);
	return scene;
}

在这里,你可以看到四件非常重要的事情。首先,我们需要创建一个新的显示 SceneFixedStepPhysicWorldFixedStepPhysicWorld 是一个 2D 真实世界物理模型的模拟。它用于计算重力、碰撞检测等。让我们聚焦于 PhysicWorld。它需要三个参数。第一个是帧率(每秒屏幕应该刷新多少次),第二个是重力向量,第三个是我们是否允许 PhysicWorld 睡眠。最后,我们需要设置 enableAccelerometerSensorregisterUpdateHandler

@Override
public void onAccelerometerChanged(AccelerometerData pAccelerometerData) {
		this.mGravityX = pAccelerometerData.getY() * 2;
		this.mGravityY = pAccelerometerData.getX() * 2;
		if(this.mGravityX > con)
			this.mGravityX = con;
		if(this.mGravityY > con)
			this.mGravityY = con;
		if(this.mGravityX < con * (-1))
			this.mGravityX = con * (-1);
		if(this.mGravityY < con * (-1))
			this.mGravityY = con * (-1);
		this.mTempVector.set(this.mGravityX, this.mGravityY);
		this.mPhysicsWorld.setGravity(this.mTempVector);
	}

newGameScene 方法中,我们启用了 accelometerSensor,它会控制 onAccelerometerChanged 方法。每当加速度计发生变化时,就会调用此方法。

protected Scene createQuitScene() {
		
	Scene scene = new Scene(2);
	scene.setBackground(this.mMenuBackground);
	Sprite buttonLevel = new Sprite(100, 100, 250, 70, this.mMenuOkTextureRegion)
	{
		@Override
		public boolean onAreaTouched
		(TouchEvent pSceneTouchEvent,float pTouchAreaLocalX, 
		float pTouchAreaLocalY)
		{
			if(checkTouchTime())
				GameLogicController.getInstance().finish();
			
			return true;
		}
	};
		
	scene.registerTouchArea(buttonLevel);
	scene.getTopLayer().addEntity(buttonLevel);
	...
} 

Sprite 只是场景中的一个图形。更有趣的是 onAreaTouched 方法。它是一个回调函数,每当有人按下按钮(精灵)时,就会调用此方法。别忘了注册触摸区域(registerTouchArea)并将按钮(addEntity)添加到场景中!

我们已经完成了 main 类的编写,现在可以转到 LevelController 类了。

public class LevelController   {
        private ArrayList<Shape> enemyList;
	private ArrayList<Shape> goodsList;
	private ArrayList<Shape> endPointList;
        ...

public void createPlayer(TiledTextureRegion mCircleFaceTextureRegion, int x, int y){
	//mPlayer = new Player(x, y, mCameraHeight/10,mCameraHeight/10,
	mCircleFaceTextureRegion, this);
	mPlayer = new Player(x, y, 30,30,mCircleFaceTextureRegion, this);
	FixtureDef FIXTURE = PhysicsFactory.createFixtureDef(1, 0.5f, 0.5f);
	Body body;
	body = PhysicsFactory.createCircleBody(mPhysicsWorld, mPlayer, 
	BodyType.DynamicBody, FIXTURE);
	
	mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector
	(mPlayer, body, true, true, false, false));
	scene.getTopLayer().addEntity(mPlayer);		
     }
...
}

LevelController 类负责加载关卡和控制关卡事件。enemyListgoodsListendPointList 这三个 ArrayList 用于存储敌方弹球、物品(钻石)和终点(终点线)。createPlayer 方法会在 x,y 位置创建一个新的玩家,大小为 30x30,PhysicFactory.CreateFixtureDef 创建一个新的物理对象。物理对象应该被注册到 mPhysicsWorld 以进行碰撞检测。

public void callbackCollisionGoods(int i){
		Shape goodShape = goodsList.get(i);
		scene.getBottomLayer().removeEntity(goodShape);
		goodsList.remove(i);
	}

如果玩家收集了钻石,就会调用 callbackCollisionGoods。在此方法中,我们从 goodsList 中移除钻石,并从 scene 中移除 goodShape

此类中的其他方法非常相似,我将不再详细描述。

下一个类是 PlayerProfileManager。在这个类中,除了 WriteSettings 方法外,你只会找到基本的 Java 代码。

private void WriteSettings() {
	String FILENAME = "settings2";
	FileOutputStream fos = null;
	DataOutputStream dos;

	try {
		fos = gameLogicController.openFileOutput(FILENAME, Context.MODE_PRIVATE);
	} catch (FileNotFoundException e) {			
	}
	try {
		dos=new DataOutputStream(fos);
		
		dos.writeInt(unlockedLevelId);	
	} catch (IOException e) {
	}
	try {
		fos.close();
	} catch (IOException e) {
	}
}

WriteSettings 方法中,我们将关卡信息保存到手机中。这需要打开、写入和关闭文件。实际上,我们只保存了一个数字——最后一个解锁关卡的 ID。例如,如果关卡 ID 是 5,则表示关卡 1、2、3、4 和 5 都已解锁。

最后介绍的类是 Player

public class Player extends AnimatedSprite   {
...
@Override
	protected void onManagedUpdate(final float pSecondsElapsed) {
		super.onManagedUpdate(pSecondsElapsed);
		onBeforePositionChanged();
	}

private boolean onBeforePositionChanged(){
		
	//speed up
	if(frameCount < 2){
		frameCount++;
		return true;
	}
	frameCount = 0;
		
	int enemyListSize = levelController.getEnemyList().size();
	for(int i = 0; i < enemyListSize; i++)
		if(this.collidesWith(levelController.getEnemyList().get(i)))
		{
			levelController.callbackCollisionEnemy();
			return false;
		}
	for(int i = 0; i < levelController.getGoodsList().size(); i++)
		if(this.collidesWith(levelController.getGoodsList().get(i)))
		{
			levelController.callbackCollisionGoods(i);
			return false;
		}

	for(int i = 0; i < levelController.getEndPointList().size(); i++)
		if(this.collidesWith(levelController.getEndPointList().get(i)))
		{
			levelController.callbackCollisionWithEndPoint();
			return false;
		}
	return true;
    }
...
} 

Player 类继承自 AnimatedSprite。Sprite 和 AnimatedSprite 之间只有一个区别——AnimatedSprite 由更多图片组成(动画),而 Sprite 只由一张图片组成。OnManagedUpdate 是每隔“x”毫秒执行的回调。OnBeforePositionChanged 会检查玩家是否与敌人、钻石或终点发生碰撞。如果发生碰撞,则会执行相应的回调。

Android 游戏开发

我希望你看到 Android 游戏开发并不难。通过一些练习,你可以开发出你梦想中的 Android 游戏。教程的最后,我想感谢你的关注。这是我的第一篇文章,如果你能给我任何反馈,我将非常高兴。

历史

  • 2011 年 4 月 29 日 - 文章上传
© . All rights reserved.