Chapter2 – 添加精灵图片
1.添加图片资源
接着我们上一章的进度,我们创建了一个 Cocos2dxSimpleGame 项目,在COCOS2DX_HOME下建立了一个Cocos2dSimpleGame的文件夹,并且能够在Win32平下下运行了。
现在我们要把三张图片添加到我们的项目中,这是Ray Wenderlich的太太的作品,作为这个简单的小游戏的主角儿。(分别为"Player.png"、"Target.png"、"Projectile.png")
要加入资源的方法很简单,只要把图片文件加入到COCOS2DX_HOME\Cocos2dxSimpleGame\Resources 文件夹内,新版本的Cocos2d-x的HelloWorld已经把Resources文件夹设置为工作目录了,这样在项目生成运行的时候就可以找到它们。
但是如果要从生成的可执行文件启动游戏,那么必须要让你的图片资源在可执行文件所在的目录下,就是说你要手动把Resources文件夹里的内容都复制到可执行文件所在的目录:Cocos2dxSimpleGame\Debug.win32 或者是 Release.win32里。
但是我们这样的懒人,一定要懒到骨头里,让系统自动完成这一步:
回到Visual Studio 2012,在Cocos2dSimpleGame.win32项目上 “右键->属性”。
在左侧找到”生成事件->后期生成事件”:
将左上角的“配置”改为“所有配置”,以确保我们同时修改了 Debug 和 Release的配置
在“命令行”中添加如下命令行
xcopy /Y /E $(ProjectDir)..\Resources\* $(OutDir)
2.添加精灵图片
接下来我们要通过代码将图片添加到我们的游戏场景里。打开 HelloWorldScene.cpp 源文件,替换 init 方法。
这是原来的init代码:
// on "init" you need to initialize your instance bool HelloWorld::init() { bool bRet = false; do { // // super init first // CC_BREAK_IF(! CCLayer::init()); // // add your codes below... // // 1. Add a menu item with "X" image, which is clicked to quit the program. // Create a "close" menu item with close icon, it's an auto release object. CCMenuItemImage *pCloseItem = CCMenuItemImage::create( "CloseNormal.png", "CloseSelected.png", this, menu_selector(HelloWorld::menuCloseCallback)); CC_BREAK_IF(! pCloseItem); // Place the menu item bottom-right conner. pCloseItem->setPosition(ccp(CCDirector::sharedDirector()->getWinSize().width - 20, 20)); // Create a menu with the "close" menu item, it's an auto release object. CCMenu* pMenu = CCMenu::create(pCloseItem, NULL); pMenu->setPosition(CCPointZero); CC_BREAK_IF(! pMenu); // Add the menu to HelloWorld layer as a child layer. this->addChild(pMenu, 1); // 2. Add a label shows "Hello World". // Create a label and initialize with string "Hello World". CCLabelTTF* pLabel = CCLabelTTF::create("Hello World", "Arial", 24); CC_BREAK_IF(! pLabel); // Get window size and place the label upper. CCSize size = CCDirector::sharedDirector()->getWinSize(); pLabel->setPosition(ccp(size.width / 2, size.height - 50)); // Add the label to HelloWorld layer as a child layer. this->addChild(pLabel, 1); // 3. Add add a splash screen, show the cocos2d splash image. CCSprite* pSprite = CCSprite::create("HelloWorld.png"); CC_BREAK_IF(! pSprite); // Place the sprite on the center of the screen pSprite->setPosition(ccp(size.width/2, size.height/2)); // Add the sprite to HelloWorld layer as a child layer. this->addChild(pSprite, 0); bRet = true; } while (0); return bRet; }
原来的Hello World添加了背景啊Label啊按钮啊什么的,这里不需要他们。简单地换成下面的C++代码。
// cpp with cocos2d-x bool HelloWorld::init() { if ( CCLayer::init() ) { CCSize winSize = CCDirector::sharedDirector()->getWinSize(); CCSprite* player = CCSprite::create("Player.png", CCRectMake(0, 0, 27, 40)); player->setPosition( ccp(player->getContentSize().width/2, winSize.height/2) ); this->addChild(player); return true; } return false; }
这段代码,实际上是以下obj-c的翻译。
// objc with cocos2d-iphone -(id) init { if( (self=[super init] )) { CGSize winSize = [[CCDirector sharedDirector] winSize]; CCSprite *player = [CCSprite spriteWithFile:@"Player.png" rect:CGRectMake(0, 0, 27, 40) ]; player.position = ccp(player.contentSize.width/2, winSize.height/2); [self addChild:player]; } return self; }
TIPS 1
译注:原文是从objc程序猿的角度写的从objc代码翻译到C++代码,因为很多人是从cocos2d-iphone过渡到cocos2d-x。但我们都是C++/java程序猿,看起来好像不需要这些知识。事实上虽然用不着写objc,但是cocos2d-x有关的API的介绍很多只有写在cocos2d-iphone的教程里,要学好cocos2d-x我们还是需要看懂一些obj-c的代码的。我作为一个C++/java程序猿,从我们的角度解释一下这两种语言的一些差别,使大家基本能看懂一些objc的代码。
1.objc中有super,代表当前类的父类,可以用来继承父类的函数,和java也不太一样。C++里的一般做法是直接用父类的类名调用表示继承,像这样CCLayer::init()。实际上C++里也有__super关键字,但它只能被VC++识别,而GCC编译器不认它,我们的项目是跨平台的,一定会经过GCC和苹果的编译器,所以我们最好的还是使用C++的一般做法。
2.objc中有属性这个概念(property,就像C#),所以能够直接调"player.contentSize",并不代表它是public的。而C++就需要和java里一样写get/set方法了。
3.同样的objc程序猿能够写"player.position = ...",C#程序猿能写"player.Position = ...",C++/java程序猿继续setPosition(...)一脸。
4.幸亏还有一些简单的对象是结构体,比如CCSize、CCPoint,在C++里也能直接访问其成员。
5.关于objc里的成员函数——objc里叫消息,他的意思就是给某个类或者对象发送一个消息,他和函数的写法完全不一样,在C++/java程序猿看来objc的成员函数调用是非常奇葩的。首先它用一个中括号表示一个成员函数调用(无论是静态的,还是不是静态的)。中括号里先写对象名或者类名,然后用一个空格分隔,再写调用的函数名。我们假设一个对象Talker,他有个talk方法。对于没有参数函数就是这样:
[对象名/类名 函数名];
[Talker talk];
如果函数有一个参数,那么要在函数名右边加一个冒号,然后写上那个参数。像这样:
[对象名/类名 函数名: 参数1];
[Talker talk: para1];
如果你有多个参数,那么你要在上一个参数后面再写一个空格,一个提示表示参数意义,跟上冒号,再写参数。像这样:
[对象名/类名 函数名: 参数1 提示: 参数2 提示: 参数3];
[Talker talk: para1 content: para2 time: para3];
可是有个问题呀,后面的参数倒是有了提示符,知道他们什么意义了,第一个参数却不知道什么意义的,所以objc程序猿会在函数名中直接提示第一个参数的意义:
[对象名/类名 函数名: 参数1 提示: 参数2 提示: 参数3];
[Talker talkToTalker: para1 content: para2 time: para3];
而如果你有可变个数的参数,要用逗号分隔它们。像这样:
[对象名/类名 函数名: 参数1, 参数2, ...];
[Talker talkToTalker: para1, para2, ...];
看看上文出现过objc的例子,能看懂了吧?比如
CCSprite *player = [CCSprite spriteWithFile:@"Player.png" rect:CGRectMake(0, 0, 27, 40) ];
包括这些在内的静态构造方法,在新版的cocos2d-x里已经改成对C++程序猿来说比较容易理解的create()方法了。这略蛋疼的函数调用,作为我们C++/java程序猿只要能看懂就行。
6.cocos2d-iphone里经常用到有很多与CGGeometry有关的方法,比如 CGRectMake, CGPointMake, CGSizeMake, CGPointZero, CGSizeZero, CGRectZero。还有一些别的NS、UI开头的来自于iOS的类,在cocos2d-x里统一都是CC开头。细心的你看到了C++代码中的CCRectMake和objc代码里的CGRectMake了吗?
7.所有cocos2d-x的游戏元素比如sprite、layer、scene、label、action,都应该用指针调用。
8.C++/java里有this,objc里呢叫做self。一物一人的感觉……
9.objc程序猿喜欢用一个叫id的东西,有点像void*一样的指针,可以指一切,C++里我们思路很正常,用各种*。同时在cocos2d-x里的init方法返回值是bool,需要写一个分支的return,这和cocos2d-iphone不一样。
10.安卓上有个titile bar会占用屏幕空间,所以玩家的位置要加一个40。ccp(player.contentSize.width/2 + 40, winSize.height/2)。
好了,言归正传我们现在跑一遍。就看到黑色背景中有一只黑色的,忍者?这玩起来比较郁闷,所以我们改一下背景颜色。首先要改一下HelloWorldScene.h的声明:
C++
// cpp with cocos2d-x class HelloWorld : public cocos2d::CCLayerColor
objc
// objc with cocos2d-iphone @interface HelloWorld : CCLayerColor
然后改一下init方法里的继承父类方法,原来是这样:
if ( CCLayer::init() )
改成这样~
if ( CCLayerColor::initWithColor( ccc4(255,255,255,255) ) )
ccc4是一个宏,能生成一个rgba颜色。
再给你们看一个objc的对比
// objc with cocos2d-iphone if ( self = [super initWithColor:ccc4(255,255,255,255)] )
TIPS 2
1.对于objc程序员来说默认的继承是public的,C++程序猿都知道默认的继承是private……所以我们要手写public。
2.cocos2d-iphone的大佬Ricardo Quesada建议cocos2d-x要使用命名空间,我们在C++里主要用到两个命名空间cocos2d,和CocosDenshion。顺便一提cocos2d定义了一个宏表示使用cocos2d命名空间,我们可以简单地这样写:
USING_NS_CC;
好现在跑一下吧,你应该能看到一片苍茫之中,孤单的主人公。。。