牛骨文教育服务平台(让学习变的简单)

做游戏是个很好玩的事情,由于兴趣我最近也在学习如何用android做游戏。顺便它选择了植物大战僵尸来当我的嵌入式作业 。熬了两天两夜终于把它完成了。这个过程从学习调研到写代码的各种心塞到完工,花了很多时间,也花了很多精力,但觉得是值得的。本文并不是讲解植物大战僵尸是怎么做的,而是讲解CoCos2d_android常用的基础知识,当然只要知道这些知识也就能够能完成这个游戏,最后会把代码贴出。先看下效果图:



一.Cocos2d_android介绍

Cocos2d是一个大家庭,包括Cocos2d-iphone,Cocos2d-x,Cocos2d-javascript等。而在国内,Cocos2d-x则相对领先。在中国的2D手机游戏开发中,Cocos2d-x引擎的份额超过70%。不同家庭成员之间只是语言不同,而实现的接口名称都相同。所以只要学习一个,其它的就都比较好理解了。本文讲的是Coscos2d_android,因为它是用java实现的,所以对我来说学起来比较快。

二.Cocos2d_android架构

如上图,Cocos2d这游戏引擎主要由图形引擎(Graphic),声音引擎(Audio),物理引擎(Box2d),脚本库以及相关语言等组成。我们重点关注开发过程中需要注意的。先看一张Cocos2d_android的代码结构图:

对于Cocos2d_android只要关注四个部分, CCDirector(导演),CCScene(场景),CCLayout(幕布)以及CCSprite(精灵)。我们可以把它当成在拍电影,顾名思义可以看出它们的作用:

CCDirector:电影中的导演,肯定是负责整部电影拍摄的,它有三个功能,管理CCScene,开线程执行SurfaceView中的绘制行为,设置游戏属性。

CCScene:电影中的场景,当然包括人和背景。可以理解它是根View,layer都必须建立在它之上,有点类似activity与fragment的关系。

CCLayer:场景中的部分图层,离用户最近的一层。游戏过程中始终只有一个layer能获得焦点。每个动作都必须建立在layer上。

CCSprite:精灵,这个可以理解为activity中的一个控件。就是最小的一部分了。平时控制最多的也就是它,所以要重点关注。

三.四大组成部分用法

其它先不说,首先你要用Cocos2d_android,你就应该先把包导进来(可以在我的工程下的libs中找到)。接下来讲解下上面讲的四部分如何在代码里面用。首先是CCDirector,直接看MainActivity中CCDirector的设置代码:

protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		CCGLSurfaceView surfaceView = new CCGLSurfaceView(this);
		setContentView(surfaceView);
		director = CCDirector.sharedDirector();
		director.attachInView(surfaceView);//开线程
		director.setScreenSize(480, 320);
		director.setDeviceOrientation(CCDirector.kCCDeviceOrientationLandscapeLeft);//横屏
		director.setDisplayFPS(true);//不显示帧率
		CCScene scene = CCScene.node();
		scene.addChild(new WelComeLayer());
		//导演管理场景
		director.runWithScene(scene);
	}

	@Override
	protected void onResume() {
		director.onResume();
		super.onResume();
	}

	@Override
	protected void onPause() {
		director.onPause();
		super.onPause();
	}

	@Override
	protected void onDestroy() {
		director.end();
		super.onDestroy();
	}

CCGLSurfaceView继承着SurfaceView,由此可见游戏引擎是用SurfaceView做的。CCDirector是一个单例,调用sharedDirector来获取它,保证全局只有一个CCDirector。初始化完后就要开始工作了,首先attachInView与surfaceView连接,感兴趣的可以进去看下attachInView源码,它跟我们平时使用surfaceView很相似,也是开了个线程,然后在幕布上画图像。这里主要是讲使用,就不翻源码了。setDeviceOrientation设置屏幕横屏。setDisplayFPS设置为true时就会在游戏左下角显示帧率,这个只是在开发时使用。最后很关键的就是初始化一个CCScene,并调用runWithScene把场景加进去。这样CCDirector的初始工作就结束了。

接下来看CCScene,它是根View,初始时经过runWithScene加入到CCDirector中,不同游戏界面可以理解为不同layer,切换layer可以理解为切换activity一样,而切换的代码如下,基本可以看成是固定格式的。

    CCScene scene = CCScene.node();
scene.addChild(new FightLayer());
CCFadeTransition transition = CCFadeTransition.transition(0.5F, scene);
	//替换场景
	CCDirector.sharedDirector().replaceScene(transition);

transition是一个切换动画,CCFadeTransitiion只是切换动画的一种,具体可以察看api。

好,我们要玩游戏就要有界面,界面画在哪上面?xml,还是activity。那么接下来这位成员就派上它的用场了。layer需要我们自己来实现,它要继承自CCLayer,如以下是自定义的layer:

public class WelComeLayer extends CCLayer {
	public WelComeLayer(){
		init();
	}}

只要把你自己的实现写在init()方法中就可以了。什么时候被调用就要看什么时候它被addChild到scene中。layer中就可以处理很多事情,包括地图的加载,音乐的播放,精灵的移动等。这些后面会细讲。最后就是精灵啦。

CCSprite是用的最多的,一个僵尸可以是一个精灵,一张图片一段文字都可以是一个精灵。精灵的加载如下:

choseContainer = CCSprite.sprite("fight_chose.png");
		choseContainer.setAnchorPoint(0, 1);
		choseContainer.setPosition(0, cgSize.height);
		this.addChild(choseContainer,0,1);

这里是把assets下的一张图片当作精灵,setAnchorPoint是设置锚点,(0,1)就是图片的左上角,相当于图片钉在左上角上,移动旋转时它都为中心。setPosition不用说,就是设置精灵的位置啦。然后调用layer的addChild就把它加载进来了。这里它有三个参数,第一个是精灵,第二个是它的层数,0是最底层的,如果想把它置于上层,就给它设置一个值,值越大层数越高,就越不会被覆盖。最后一个参数是tag,设置了它就可以在其它地方通过tag来获取这个精灵,类似于findViewById(),获取代码如下:

CCSprite c = layer.getChildByTag(1);

注意上面的layer必须是你定义精灵所在的layer中。那么如何在layer中监听点击事件呢,看以下代码:

//设置物体的触发事件
@Override
public boolean ccTouchesBegan(MotionEvent event) {
     // TODO Auto-generated method stub
     CCSprite sprite =  (CCSprite) this.getChildByTag( TAG_X);
     //转成opengl下的坐标点
     CGPoint cgPoint = this.convertTouchToNodeSpace(event);
     boolean flag = CGRect.containsPoint(sprite.getBoundingBox(), cgPoint);
     if(flag){
            //设置透明度
           sprite.setOpacity(100);
            //设置是否可见
           sprite.setVisible( false);
            //删除自己
           sprite.removeSelf();
     }
     return super.ccTouchesBegan(event);
}

类似于OnEventTouch(),这里有一个知识点,就是android平时的习惯是把屏幕左上角当坐标原点,向下的y正,向右是x正。但是在Cocos2d中不一样,你必须把它转成openGl的习惯,也就是左下角是坐标,向上是y正,向右是x正。转化也有api,如上面的convertTouchToNodeSpace()就是把android的point转成OpenGL下的Point。

四,加载资源文件

讲到这里,我假设你之前的都己经明白了,也能在不同界面上显示不同的sprite了,那接下来我们就要对游戏场景进行优化,首先光一些精灵肯定不行,肯定要有背景图,而且游戏嘛,肯定也要有音乐。那首先来讲讲地图的加载吧。

其实地图加载我认为一点都不简单,别以为地图只是一张图片,如果是张图片,那游戏开发过程中要定位位置怎办。其实在游戏中有一个格式的文件很见,.tmx文件,它包括地图上某些你标记的点的信息。这样的图片用文本编辑器打开如下:

<?xml version="1.0" encoding="UTF-8"?>
<map version="1.0" orientation="orthogonal" width="14" height="6" tilewidth="46" tileheight="54">
 <tileset firstgid="1" name="bk1" tilewidth="46" tileheight="54">
  <image source="bk1.jpg" width="678" height="331"/>
 </tileset>
 <layer name="block" width="14" height="6">
  <data encoding="base64" compression="zlib">
   eJwNw4lSQQEAAMAXEZWEpKjooEShkhJdEkmS+v9vaXdml4IgCBl22YhRV4wZd9U11024YdJNU6bNuGXWbXPuuGvegnvue2DRkoceeeyJZSueembVc2vWvfDShk1bXnntjW1v7XjnvV0f7Nn30SefffHVgW8OfXfk2A8nfjr1y5nfzv1x4a9//gNAug3z
  </data>
 </layer>
 <objectgroup name="road" width="14" height="6" visible="0">
  <object x="473" y="96"/>
  <object x="474" y="306"/>
  <object x="23" y="303"/>
 </objectgroup>
</map>

它会被解析成一个xml格式的文件,里面记录了地图长宽,及长宽分为几块,以及一些标记点的集合。如上面name为“road”的集合。看到这你是不是吓到了,难道每个地图都要自己手写?这肯定是不可能的。这里介绍一款小软件,tiled(下载链接)。它可以方便的进行地图标记,显示如下图:

如果你想改变背景图的样子,可以用上边栏的填充工具等进行绘制,如果想定点就新建一个对象层,如同图中的road。再用工具栏上的创建对象按钮在地图上定点,记住定点的顺序是有讲究的,先确定的点会存在前面,这个待会解析时会说,可以创建多个对象层,起的名字就是文件中objectgroup的name属性对应的名字。图片制作完成后保存就自动更改为.tmx格式。把它放到assets文件下,就可以加载它了,来看下加载的代码:

map = CCTMXTiledMap.tiledMap("map_day.tmx");
		map.setAnchorPoint(0.5f,0.5f);
		CGSize contentSize = map.getContentSize();
		//左移右移一半
		map.setPosition(contentSize.width/2,contentSize.height/2);
		this.addChild(map);

CCTMXTiledMap.tiledMap就是把图片加载进来,getContentSize就是获取地图的大小,之所以把地图的位置置为大小的一半,是因为地图可能大于屏幕,这样设置希望它一开始就能显示左下部分。那如何获得我们在地图上设置的点呢,如下:

CCTMXObjectGroup zombiesGroup = map.objectGroupNamed("road");
		ArrayList<HashMap<String, String>> zombies = zombiesGroup.objects;
		// 分别以x和y为键,获取坐标值信息---->封装到点集合中
		List<CGPoint> points = new ArrayList<CGPoint>();
		for (HashMap<String, String> item : zombies) {
			float x = Float.parseFloat(item.get("x"));
			float y = Float.parseFloat(item.get("y"));
			points.add(CGPoint.ccp(x, y));
		}

这个可以说是固定格式,你可以把它写到工具类中,最后points这个list中点信息的顺序就是你在地图上添加点的顺序,所以添点忌随意。有了点你就能干很多事,如设置移动路线。地图加载讲完后,就是音乐加载了。

这样相对比较简单,音乐的话要获得一个音乐引擎来加载音乐。音乐引擎其实就是封装了下mediaPlayer而已。看下代码:

SoundEngine engine = SoundEngine.sharedEngine();
		engine.preloadSound(getContext(), R.raw.start);
engine.playSound(getContext(), com.lxj.zhiwuvsani.R.raw.start, true);

如上,raw文件下放一些音乐文件,preloadSound是预先加载,可以把它放在游戏加载过程中,而playSound就是你想在哪播就设置在哪。true表示循环播放。

最后,说下字体的加载吧,Cocos2d_android字体用的是CCLabel,它继承自CCSprite,设置如下:

CCLabel cLabel = CCLabel.makeLabel("hello world", "Roboto_Thin.ttf", 20);//创建字体,中间参数为ttf文件,20为字体大小
			cLabel.setPosition(cgSize.width/2,cgSize.height/2);
			this.addChild(cLabel,1);

它加载的字是hello world,加载的字体格式是Roboto_Thin.ttf,这个文件要存在assets文件夹下,如有不想设置那就直接置为“”。

讲了这么多,现在己经可以自己布置场景,加载精灵任意摆放,并配上音乐字体了。一个游戏界面就差不多了,但是,很重要的一点,东西都不会动!!!!,不会动怎么玩。下一篇我将讲讲如何让精灵动起来,设置各种action及判断位置。看完下篇,你就能完整的做游戏了。当然看这些可能还不够,这些只是最常用的,对于一些不常用的不懂的时还要去查下api,好啦,说了这么多,好累,歇会。

植物大战僵尸源码下载