最新文章
Cocos2d-x游戏开发实例详解7:对象释放时机
03-25 13:59
Cocos2d-x游戏开发实例详解6:自动释放池
03-25 13:55
Cocos2d-x游戏开发实例详解5:神奇的自动释放
03-25 13:49
Cocos2d-x游戏开发实例详解4:游戏主循环
03-25 13:44
Cocos2d-x游戏开发实例详解3:无限滚动地图
03-25 13:37
Cocos2d-x游戏开发实例详解2:开始菜单续
03-25 13:32
Cocos2d-x 3.x《飞机大战》教程2:素材准备与游戏菜单场景
在解决好环境和项目问题后,今天我们正式开启代码之旅。第一步,先来进行素材准备。
一、素材准备
“打飞机”游戏有众多版本,十分流行。我在百度搜索时找到了微信飞机大战的素材。由于这个游戏相对简单,非常适合新手练手,所以我们选择它作为实践项目。实际上,实现基本的游戏逻辑只需几张图片,具体用到哪些后续会提及。
打开项目文件夹,将所需的游戏图片资源导入游戏项目根目录下的 Resourse 文件夹。需要注意的是,游戏默认从该文件夹开始读取资源。例如,若 Resourse 文件夹下有一张名为 simple.png 的图片,在项目中可直接使用资源路径 "simple.png" 进行读取。
二、屏幕大小调节
接下来,我们要对代码进行调试和编写。
首先打开 VS2012(这是我当前使用的 IDE),依次点击“文件” -> “打开” -> “项目/解决方案”,打开游戏项目的 proj.win32 下的 planegame.sln,即可将游戏项目导入到 VS2012 中。
“打飞机”游戏竖屏体验更佳,我们需要在 AppDelagate 中修改窗口属性,调整游戏窗口大小。
打开 src 目录(该目录用于存放游戏代码)下的 AppDelegate,在 applicationDidFinishLaunching() 方法的相应位置添加如下代码:
// 设置窗口大小
glView->setDesignResolutionSize(480, 800, ResolutionPolicy::SHOW_ALL);
现在可以运行程序,查看窗口大小改变的效果。
三、背景图片与滚动
游戏启动过程说明(可略过)
在开始处理背景图片之前,先简单介绍一下游戏的启动过程。我们的游戏从 win32 下的 main.cpp 中的 APIENTRY _tWinMain 方法开始,这其实是 C++ 语言的 main 方法的宏定义。在该方法中,定义了 AppDelegate 的一个对象,随后会初始化 AppDelegate 的父类 Application,并将自身指针赋值给父类的一个变量,目的是调用 AppDelegate 的 applicationDidFinishLaunching() 方法。此方法用于初始化游戏窗口、导演类以及第一个场景 HelloWorld。最后,main 方法执行 Application::getInstance()->run(),其中封装了游戏的循环和渲染等逻辑,此时游戏便开始运行。若想深入了解,可研究源码,这也是开源的优势所在。
背景图片添加与设置
打开 HelloWorldScene.cpp,删除原有的创建菜单和背景的代码,添加如下代码:
// 获取可见区域大小和原点坐标
auto visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
// 创建第一张背景精灵
auto bg1 = Sprite::create("background.png");
// Vec2 是 Cocos2d-x 的数据结构,包含两个 float 型数据,分别表示 x 轴和 y 轴
// setPosition 用于设置精灵的相对屏幕位置(世界坐标),这里将精灵设置在屏幕宽度一半、高度为零的位置,即屏幕最下方中间
// Cocos2d-x 以 OpenGL 的右手坐标为标准,屏幕左下方为原点
bg1->setPosition(Vec2(origin.x + visibleSize.width / 2, 0));
// 设置锚点,为使图片始终在屏幕中间,x 轴设为 0.5,意味着精灵向左移动自身宽度的一半,y 轴向下
bg1->setAnchorPoint(Vec2(0.5, 0));
// 设置 Tag,方便后续通过标签找到该精灵进行操作
bg1->setTag(101);
// 将背景精灵添加到层中,0 表示可见优先权,数值越小越容易被遮挡
this->addChild(bg1, 0);
// 创建第二张背景精灵,与第一张无缝连接,实现不间断的地图滚动
auto bg2 = Sprite::create("background.png");
bg2->setPosition(Vec2(origin.x + visibleSize.width / 2, bg1->getPositionY() + bg1->getContentSize().height));
bg2->setAnchorPoint(Vec2(0.5, 0));
bg2->setTag(102);
this->addChild(bg2, 0);
背景滚动逻辑实现
先在 HelloWorldScene.h 文件中声明定时器的回调方法:
void backgroundMove(float f); // 定时器回调的背景滚动方法
然后在 HelloWorldScene.cpp 中定义该方法:
void HelloWorld::backgroundMove(float f) {
// 获取两张背景精灵
auto bg1 = this->getChildByTag(101);
auto bg2 = this->getChildByTag(102);
// 当第二张图片退出屏幕时,将第一张图片设置到合适位置
if (bg2->getPositionY() + bg2->getContentSize().height <= Director::getInstance()->getVisibleSize().height) {
bg1->setPositionY(-bg1->getContentSize().height + Director::getInstance()->getVisibleSize().height);
}
// 移动背景精灵
bg1->setPositionY(bg1->getPositionY() - 3);
bg2->setPositionY(bg1->getPositionY() + bg1->getContentSize().height);
}
背景滚动的逻辑可通过以下图示理解:游戏开始时处于①状态,定时器会不断修改背景精灵一和背景精灵二的坐标。当两者坐标到达②状态时,将其修改为③状态,即回到游戏开始时的①状态,从而实现无限背景滚动效果。需要注意的是,若背景图片高度小于屏幕高度,两张图片可能无法实现该效果,具体处理方式可自行思考。
最后,启动定时器,在添加两个背景精灵的代码下方添加如下代码:
// 启动定时器,每秒调用一次 backgroundMove 方法
this->schedule(schedule_selector(HelloWorld::backgroundMove), 1.0f / 60);
现在可以运行程序,查看背景滚动效果。
四、游戏菜单项
目前项目较为简单,只有“开始游戏”和“退出游戏”两个选项,实际上游戏还可能包含“游戏设置”“关于”“帮助”等选项。
首先在 HelloWorldScene.h 中声明两个回调方法,这两个方法将在点击菜单项时触发:
void menuStartCallback(Ref* pSender); // 开始游戏回调方法
void menuCloseCallback(Ref* pSender); // 退出游戏回调方法
然后创建如下菜单项:
// 创建开始游戏菜单项
auto startItem = MenuItemFont::create("Start Game", CC_CALLBACK_1(HelloWorld::menuStartCallback, this));
startItem->setPosition(Vec2(visibleSize.width / 2 + origin.x, visibleSize.height / 2 + origin.y + 50));
// 创建退出游戏菜单项
auto closeItem = MenuItemFont::create("Exit Game", CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));
closeItem->setPosition(Vec2(visibleSize.width / 2 + origin.x, visibleSize.height / 2 + origin.y - 50));
// 创建菜单
auto menu = Menu::create(startItem, closeItem, NULL);
menu->setPosition(Vec2::ZERO);
this->addChild(menu, 1);
回调函数的实现如下:
void HelloWorld::menuStartCallback(Ref* pSender) {
// 开始游戏逻辑待实现
}
void HelloWorld::menuCloseCallback(Ref* pSender) {
Director::getInstance()->end();
}
现在编译运行程序,会看到屏幕中间出现两个菜单项,点击“退出游戏”可退出程序,但“开始游戏”的逻辑尚未实现。
五、蠢蠢欲动的飞机
为了展示飞机迫不及待冲向敌人基地的姿态,我们为其添加一个动画。
添加如下代码:
// 添加飞机精灵
auto plane = Sprite::create("hero1.png");
plane->setPosition(visibleSize.width / 2 + origin.x, 200);
this->addChild(plane);
// 创建动画帧
auto spriteFrame1 = SpriteFrame::create("hero1.png", Rect(0, 0, 102, 126));
auto spriteFrame2 = SpriteFrame::create("hero2.png", Rect(0, 0, 102, 126));
// 创建动画
auto animation = Animation::create();
animation->addSpriteFrame(spriteFrame1);
animation->addSpriteFrame(spriteFrame2);
animation->setDelayPerUnit(0.15f); // 两张图片交互播放的间隔
// 创建动画动作
auto animate = Animate::create(animation);
// 让飞机执行动画
plane->runAction(RepeatForever::create(animate));
动画以精灵为单位,为精灵添加动画后,让精灵执行该动作即可。若需要停止动画,可使用以下代码:
sprite->stopAllActions(); // 停止所有动画,清除动画内存
Animate 继承自 Ref,其内存由 Cocos2d-x 框架自动回收,但不需要时仍需手动停止。由于当前飞机精灵动画在当前场景中无限存活,无需手动停止,切换到其他场景时会自动清除内存。
现在启动程序,就能看到飞机飞翔的动画效果。
最终界面
相关教程链接
- Cocos2d-x 3.x《飞机大战》教程1:环境与创建项目
- Cocos2d-x 3.x《飞机大战》教程3:物理引擎的使用
- Cocos2d-x 3.x《飞机大战》教程4:游戏场景
- Cocos2d-x 3.x《飞机大战》教程5:敌我碰撞处理、分数计算、音乐播放
- Cocos2d-x 3.x《飞机大战》教程6:游戏结束场景
注:我会尽量详细解释代码,并分享一些心得体会。若有未解释清楚的地方,可自行百度。代码注释过多可能影响可读性,若有影响,可直接参考源代码。