上次已经弄好了cocos2d-x的开发环境,接下来就学习一个实际点的项目练习一下。需要说明的是该学习项目来自龙灵修的视频,所有的版权类都归龙灵修所有,在此向这位哥们致敬。
按照我们上次的方法我们新建一个SpaceWar的项目。
1 ./create_project.py -project SpaceWar -package com.simple.demo -language cpp
创建好项目之后我们就可以编译了,因为我是在linux系统下,所以所有的操作都是在linux下操作的,在之后的操作中不在说明。等整个项目完成后,会将其移植到Android平台。
我们进入proj.linux目录就可以编译我们的项目了。make一下就可以执行出可执行文件了。
我们先大致看一下入口函数,其位于proj.linux目录下的main.cpp文件,定义如下:
1 #include "../Classes/AppDelegate.h" 2 #include "cocos2d.h" 3 #include "CCEGLView.h" 4 5 #include <stdlib.h> 6 #include <stdio.h> 7 #include <unistd.h> 8 #include <string> 9 10 USING_NS_CC; 11 12 int main(int argc, char **argv) 13 { 14 // create the application instance 15 AppDelegate app; 16 CCEGLView* eglView = CCEGLView::sharedOpenGLView(); 17 eglView->setFrameSize(320, 480); 18 return CCApplication::sharedApplication()->run(); 19 }
其中USING_NS_CC用来声明命名空间,AppDelegate app是声明的一个Applicaton对象。接着声明了一个OpenGL的窗口。需要注意的是最后一个函数,这个函数开始初始化我们自己的代码,并进入主循环。在这个函数中会调用app对象的applicationDidFinishLaunching函数。其中AppDelegate定义在项目根目录的Classes文件夹下。函数定义如下:
1 bool AppDelegate::applicationDidFinishLaunching() { 2 // initialize director 3 CCDirector* pDirector = CCDirector::sharedDirector(); 4 CCEGLView* pEGLView = CCEGLView::sharedOpenGLView(); 5 6 pDirector->setOpenGLView(pEGLView); 7 8 // turn on display FPS 9 pDirector->setDisplayStats(true); 10 11 // set FPS. the default value is 1.0/60 if you don't call this 12 pDirector->setAnimationInterval(1.0 / 60); 13 14 // create a scene. it's an autorelease object 15 CCScene *pScene = WelcomeLayer::scene(); 16 17 // run 18 pDirector->runWithScene(pScene); 19 20 return true; 21 }
在这个函数里,我们看到了几个新的对象。CCDirector是一个导演类,CCScene是一个场景类。其中第9行和第12行的代码主要是用来调试的,也就是我们创建了项目后,在我们创建窗口的左下角会显示当前场景中有几张图片和刷新率之类的。下面说一下导演和场景的概念。在一个游戏当中通常是由一个导演和多个场景来组成的。导演用来实现场景的切换,场景用来实现游戏的内容。游戏刚进入时一般都会有一个游戏菜单,我们首先看那一下这个游戏菜单是怎么实现的。游戏菜单的实现类为WelcomeLayer。
在看这个菜单图层实现之前首先我们来看一下,抽象出的一个公共类:BaserLayer
我们看一下它头文件和源文件的实现
BaserLayer.h
1 #ifndef _BASER_LAYER_H 2 #define _BASER_LAYER_H 3 #include "cocos2d.h" 4 #include "SimpleAudioEngine.h" 5 6 USING_NS_CC; 7 8 using namespace CocosDenshion; 9 10 class BaserLayer : public CCLayer 11 { 12 public: 13 BaserLayer(); 14 ~BaserLayer(); 15 16 virtual bool init(); 17 18 CCSize getWinSize(); 19 20 CREATE_FUNC(BaserLayer); 21 22 void setBackGroundImage(CCSprite *back); 23 void setBackGroundImage(const char* image); 24 }; 25 26 #endif
BaserLayer.cpp
1 #include "BaserLayer.h" 2 3 BaserLayer::BaserLayer() 4 { 5 } 6 7 BaserLayer::~BaserLayer() 8 { 9 } 10 11 bool BaserLayer::init() 12 { 13 bool sRect = false; 14 15 do 16 { 17 CC_BREAK_IF(!CCLayer::init()); 18 19 sRect = true; 20 }while(0); 21 22 return sRect; 23 } 24 25 26 CCSize BaserLayer::getWinSize() 27 { 28 return CCDirector::sharedDirector()->getWinSize(); 29 } 30 31 void BaserLayer::setBackGroundImage(CCSprite *back) 32 { 33 back->setPosition(ccp(getWinSize().width/2,getWinSize().height/2)); 34 } 35 36 void BaserLayer::setBackGroundImage(const char *image) 37 { 38 39 CCSprite *sp = CCSprite::create(image); 40 sp->setPosition(ccp(getWinSize().width/2,getWinSize().height/2)); 41 this->addChild(sp); 42 } 43
在这个类里,我们主要实现了一个getWinSize函数用来获得窗口的大小,setBackGroundImage用来设置图层的背景。
其中WelcomeLayer继承了BaserLayer类,CCLayer是一个图层类,用来实现游戏不同层次的元素。需要注意的是在此处图层只是一个容器,并不是真的有一张图片的元素。其头文件定义如下:
1 #ifndef WELCOME_LAYER_H 2 #define WELCOME_LAYER_H 3 4 #include "cocos2d.h" 5 #include "BaserLayer.h" 6 7 USING_NS_CC; 8 9 class WelcomeLayer : public BaserLayer 10 { 11 public: 12 WelcomeLayer(); 13 ~WelcomeLayer(); 14 15 virtual bool init(); 16 17 virtual void onEnter(); 18 19 static CCScene* scene(); 20 21 CREATE_FUNC(WelcomeLayer); 22 23 void setViews(); 24 25 void startGameCallback(CCObject *sender ); 26 void aboutGameCallback(CCObject *sender ); 27 void optionGameCallback(CCObject *sender ); 28 }; 29 30 #endif
在上面的代码中我们继承CCLayer类创建了一个图层对象,在这个类中我们实现了一个静态函数scene用来创建一个游戏场景。其中init函数和onEnter函数是继承自父类的函数。接这个我们看到了一个宏定义CREATE_FUNC,这个宏的具体作用我们看了源文件在来了解,我们看一下创建场景的静态函数:
1 CCScene * WelcomeLayer::scene() 2 { 3 CCScene *scene = NULL; 4 5 do 6 { 7 scene = CCScene::create(); 8 CC_BREAK_IF(!scene); 9 10 WelcomeLayer *layer = WelcomeLayer::create(); 11 CC_BREAK_IF(!layer); 12 13 scene->addChild(layer); 14 15 }while(0); 16 17 return scene; 18 }
第7行的代码创建了新的场景对象,第8行CC_BREAK_IF宏用来检查我们创建的场景对象是否成功。第10行的代码用来创建我们自定义的图层对象,在这里需要注意的是我们在实现WelcomeLayer类的时候,并没有定义过create方法,那么这个方法究竟是在哪里创建的呢。大家可以尝试把头文件的CREATE_FUNC这一行代码注释掉,在编译代码看看。这个时候编译器会提示找不到WelcomeLayer::create这个函数。这个函数是由CREATE_FUNC来创建的,我们看一下这个宏的定义:
1 #define CREATE_FUNC(__TYPE__) 2 static __TYPE__* create() 3 { 4 __TYPE__ *pRet = new __TYPE__(); 5 if (pRet && pRet->init()) 6 { 7 pRet->autorelease(); 8 return pRet; 9 } 10 else 11 { 12 delete pRet; 13 pRet = NULL; 14 return NULL; 15 } 16 }
看到这里大家想必就明白了,实际上这个宏实现了一个静态的create函数用来返回一个__TYPE__的对象,这里的__TYPE__实际上就是我们传递的WelcomeLayer对象。接着我们看一下它的init函数实现。代码定义如下:
1 bool WelcomeLayer::init() 2 { 3 bool sRect = false; 4 5 do 6 { 7 CC_BREAK_IF(!BaserLayer::init()); 8 9 setViews(); 10 11 sRect = true; 12 13 }while(0); 14 15 return sRect; 16 }
在这个函数里,我们首先调用了其父类的init函数,接着调用setViews函数同来初始化界面元素。setViews函数定义如下:
1 void WelcomeLayer::setViews() 2 { 3 /** 4 *设置背景 5 */ 6 setBackGroundImage("loading.png"); 7 8 CCSprite *logo = CCSprite::create("logo.png"); 9 logo->setAnchorPoint(ccp(0.5,1.0)); 10 logo->setPosition(ccp(getWinSize().width/2,getWinSize().height-20)); 11 this->addChild(logo); 12 13 /** 14 *创建menu 15 */ 16 CCSprite *startGameNormal = CCSprite::create("menu.png",CCRectMake(0,0,126,33)); 17 CCSprite *startGamePress = CCSprite::create("menu.png",CCRectMake(0,33,126,33)); 18 CCSprite *startGameDisable = CCSprite::create("menu.png",CCRectMake(0,66,126,33)); 19 20 CCMenuItemSprite *startGmae = CCMenuItemSprite::create(startGameNormal,startGamePress,startGameDisable,this, 21 menu_selector(WelcomeLayer::startGameCallback)); 22 23 CCSprite *aboutGameNormal = CCSprite::create("menu.png",CCRectMake(252,0,126,33)); 24 CCSprite *aboutGamePress = CCSprite::create("menu.png",CCRectMake(252,33,126,33)); 25 CCSprite *aboutGameDisable = CCSprite::create("menu.png",CCRectMake(252,66,126,33)); 26 27 CCMenuItemSprite *aboutGmae = CCMenuItemSprite::create(aboutGameNormal,aboutGamePress,aboutGameDisable,this, 28 menu_selector(WelcomeLayer::aboutGameCallback)); 29 30 CCSprite *optionGameNormal = CCSprite::create("menu.png",CCRectMake(126,0,126,33)); 31 CCSprite *optionGamePress = CCSprite::create("menu.png",CCRectMake(126,33,126,33)); 32 CCSprite *optionGameDisable = CCSprite::create("menu.png",CCRectMake(126,66,126,33)); 33 34 CCMenuItemSprite *optionGmae = CCMenuItemSprite::create(optionGameNormal,optionGamePress,optionGameDisable,this, 35 menu_selector(WelcomeLayer::optionGameCallback)); 36 37 CCMenu *menu = CCMenu::create(startGmae,optionGmae,aboutGmae,NULL); 38 menu->setPosition(ccp(getWinSize().width/2,getWinSize().height/2)); 39 menu->alignItemsVerticallyWithPadding(10); 40 41 this->addChild(menu); 42 }
在这个函数里我们设置了图层背景,初始化了选项菜单,主要包括三个选项菜单:New Game ,Option ,About。其中设置背景是通过其父类的setBackGroundImage函数实现的。
关于菜单的创建在此处只看其中一个。由于这个菜单是通过三张不同的图片来实现的,通过三张图片我们实现了不同的点,按,禁用效果。所以在此处我们创建了三个精灵(CCSprite)来实现按钮的效果。在这里我们简单看看创建精灵的函数
1 CCSprite *startGameNormal = CCSprite::create("menu.png",CCRectMake(0,0,126,33));
其中第一个参数为我们要加载的图片,第二个参数为一个CCRect类型的参数,在这个函数里写明了我们将要加载的内容在图片中的位置。在这里要稍微说明一下这个,大家打开menu.png图片文件就知道了。其中CCRectMake的四个参数依次为:起始x坐标,起始y坐标,宽度,高度。在这里在稍微说一下,为什么要把图片搞成这个样子呢,为什么不一张一张弄出来呢。大致的原因网上是这么解释的:假如我们有10张图片,如果我们一张一张加载要调用OpenGL渲染器10次,如果我们只把它加入到一张纹理图片里,加载的时候就只用调用一次OpenGL渲染器1次。
1 CCMenuItemSprite *startGmae = CCMenuItemSprite::create(startGameNormal,startGamePress,startGameDisable,this, 2 menu_selector(WelcomeLayer::startGameCallback));
在这里我们创建菜单item时使用了CCMenuItemSprite对象。其参数依次为:初始效果,按下效果,禁用效果,父对象,按下按钮时的回调函数。接着我们创建一个菜单控件,代码如下:
1 CCMenu *menu = CCMenu::create(startGmae,optionGmae,aboutGmae,NULL); 2 menu->setPosition(ccp(getWinSize().width/2,getWinSize().height/2)); 3 menu->alignItemsVerticallyWithPadding(10);
此处需要注意的是创建CCMenu的函数,由于这个函数的参数是可变的,所以我们必须保证最后一个参数为NULL,依此来作为参数结束的标志。接着设置了菜单的坐标。其中ccp会生成一个CCPoint的对象。第三行的函数用来设置该菜单是一个垂直的菜单。
接着我们看一下当我们按下某一个菜单选项的回调函数是如何处理的
1 void WelcomeLayer::aboutGameCallback(CCObject *sender ) 2 { 3 #ifdef DEBUG 4 CCLog("aboutGame"); 5 #endif 6 7 CCDirector::sharedDirector()->pushScene(CCTransitionFade::create(0.5f, AboutLayer::scene() )); 8 }
以上的这个函数是当我们点下about按钮时所调用的函数,在这个函数中我们创建了一个新的场景,当前场景则会暂停。需要注意的是我们在加载该场景的时候创建了一个淡入淡出的的动画,其中个第一个参数为持续的时间,第二个参数为需要执行该动画的场景。以上就是游戏的开始场景创建。在这里贴一下效果图吧