We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
(此步可被代码代替,但使用可视化工具更加便捷)
主场景: 先添加图片资源,创建基础容器 root,在 root 子树上添加一系列静态元素,这里除了菜单按钮需要用户交互外其他都是图片类型(Cocos Studio中图片与精灵区别不大)。修改相应元素的名称,以便在写代码时做对应操作。
菜单层: 因为菜单是在主场景的基础上跳出来的,所以作为一个层来处理。同样添加所需的元素。
(1) 类的定义(工厂模式) 先创建游戏所需场景MainScene,所需层MenuLayer的 .cpp 与 .h 文件,又由于2048游戏主要体现在对卡片的操作上,且比较复杂,所以这里再创建 CardSprite 的 .cpp 与 .h 文件。
主场景:
class MainScene : public cocos2d::Layer { public: static cocos2d::Scene* createScene(); virtual bool init(); CREATE_FUNC(MainScene); static void ClearData(); //清除上次游戏数据,用于重新开始 private: void LoadBackground(); //加载背景音乐 void MenuTouch(cocos2d::Ref *pSender, Widget::TouchEventType type); //点击弹出菜单事件 void SetScore(); //设置分数 Size GetVisibleSize(); //获取可视化大小 void SetTouchListener(); //调用手势识别的事件监听器,添加事件监听 void createCardSprite(cocos2d::Size size); //创建卡片精灵 virtual bool onTouchBegan(cocos2d::Touch *touch, cocos2d::Event *unused_event); //触摸事件开始 virtual void onTouchEnded(cocos2d::Touch *touch, cocos2d::Event *unused_event); //触摸事件结束 bool doLeft(); //向左 bool doRight(); //向右 bool doUp(); //向上 bool doDown(); //向下 void autoCreateCardNumber(); //自动生成随机数初始卡片 void doCheckGameOver(); //判断游戏是否结束 Layout* root; ImageView* imgIcon; ImageView* imgScorelast; ImageView* imgScoremax; Button* btnMenu; int firstX, firstY, endX, endY; CardSprite *cardArr[4][4]; //二维数组存放16张卡片格 int lastScore; //最新分数 int maxScore; //历史最高分数 LabelTTF *labelTTFCardNumber; LabelTTF *labelTTFlastScore; LabelTTF *labelTTFmaxScore; };
class MainScene : public cocos2d::Layer { public: static cocos2d::Scene* createScene(); virtual bool init(); CREATE_FUNC(MainScene); static void ClearData(); //清除上次游戏数据,用于重新开始
private: void LoadBackground(); //加载背景音乐 void MenuTouch(cocos2d::Ref *pSender, Widget::TouchEventType type); //点击弹出菜单事件 void SetScore(); //设置分数 Size GetVisibleSize(); //获取可视化大小 void SetTouchListener(); //调用手势识别的事件监听器,添加事件监听 void createCardSprite(cocos2d::Size size); //创建卡片精灵 virtual bool onTouchBegan(cocos2d::Touch *touch, cocos2d::Event *unused_event); //触摸事件开始 virtual void onTouchEnded(cocos2d::Touch *touch, cocos2d::Event *unused_event); //触摸事件结束 bool doLeft(); //向左 bool doRight(); //向右 bool doUp(); //向上 bool doDown(); //向下 void autoCreateCardNumber(); //自动生成随机数初始卡片 void doCheckGameOver(); //判断游戏是否结束
Layout* root; ImageView* imgIcon; ImageView* imgScorelast; ImageView* imgScoremax; Button* btnMenu; int firstX, firstY, endX, endY; CardSprite *cardArr[4][4]; //二维数组存放16张卡片格 int lastScore; //最新分数 int maxScore; //历史最高分数 LabelTTF *labelTTFCardNumber; LabelTTF *labelTTFlastScore; LabelTTF *labelTTFmaxScore;
};
菜单层:
class MenuLayer : public cocos2d::Layer { public: virtual bool init(); CREATE_FUNC(MenuLayer); static void playEffect(const std::string &effectName, bool force); //播放音效 private: void SetBackground(); //获取studio ui场景 void SetButton(); //设置菜单选项按钮 void ContinueTouch(cocos2d::Ref pSender, Widget::TouchEventType type); //继续按钮点击事件 void RestartTouch(cocos2d::Ref *pSender, Widget::TouchEventType type); //重新开始按钮点击事件 void Close(Node pSender); //关闭菜单层 void loadSoundMenu(); //加载声音菜单 void soundMenuCallback(Ref *sender); //声音菜单回调函数 Layout* root; ImageView* imgBack; ImageView* imgMenu; Button* btnContinue; Button* btnRestart; CheckBox* checkBox; };
class MenuLayer : public cocos2d::Layer { public: virtual bool init(); CREATE_FUNC(MenuLayer); static void playEffect(const std::string &effectName, bool force); //播放音效
private: void SetBackground(); //获取studio ui场景 void SetButton(); //设置菜单选项按钮 void ContinueTouch(cocos2d::Ref pSender, Widget::TouchEventType type); //继续按钮点击事件 void RestartTouch(cocos2d::Ref *pSender, Widget::TouchEventType type); //重新开始按钮点击事件 void Close(Node pSender); //关闭菜单层 void loadSoundMenu(); //加载声音菜单 void soundMenuCallback(Ref *sender); //声音菜单回调函数
Layout* root; ImageView* imgBack; ImageView* imgMenu; Button* btnContinue; Button* btnRestart; CheckBox* checkBox;
卡片精灵:
class CardSprite : public cocos2d::Sprite { public: static CardSprite *createCardSprite(int numbers, int width, int height, float CardSpriteX, float CardSpriteY); //初始化卡片 virtual bool init(); CREATE_FUNC(CardSprite); void setNumber(int num); //设置数字 int getNumber(); //获取数字 private: void enemyInit(int numbers, int width, int height, float CardSpriteX, float CardSpriteY); //结合卡片与数字 int number; cocos2d::LabelTTF *labTTFCardNumber; cocos2d::LayerColor *layerColorBG; };
class CardSprite : public cocos2d::Sprite {
public: static CardSprite *createCardSprite(int numbers, int width, int height, float CardSpriteX, float CardSpriteY); //初始化卡片 virtual bool init(); CREATE_FUNC(CardSprite); void setNumber(int num); //设置数字 int getNumber(); //获取数字
private: void enemyInit(int numbers, int width, int height, float CardSpriteX, float CardSpriteY); //结合卡片与数字 int number; cocos2d::LabelTTF *labTTFCardNumber; cocos2d::LayerColor *layerColorBG; };
(2) 方法定义 Cocos2d-x是个功能强大的集成库,其提供的方法可满足大部分游戏的需求,写代码时配合官方文档食用风味更佳。
获取 Cocos Studio 的UI页面
void MainScene::LoadBackground() { auto rootNode = CSLoader::createNode("MainScene.csb"); root = (Layout_)rootNode->getChildByName("root"); imgIcon = (ImageView_)Helper::seekWidgetByName(root, "imgIcon"); imgScorelast = (ImageView_)Helper::seekWidgetByName(root, "imgScorelast"); imgScoremax = (ImageView_)Helper::seekWidgetByName(root, "imgScoremax"); btnMenu = (Button*)Helper::seekWidgetByName(root, "btnMenu"); //加载控件 btnMenu->addTouchEventListener(CC_CALLBACK_2(MainScene::MenuTouch, this)); //菜单按钮点击事件 addChild(rootNode); }
2048游戏的特色在于识别上下左右手势,对卡片进行移动或合并操作。(这里有点要注意,当初困扰了我一天,一定要把Cocos Studio中除按钮外的控件取消交互性,否则会和手势识别冲突。)
//调用手势识别的事件监听器,添加事件监听 void MainScene::SetTouchListener() { auto touchListener = EventListenerTouchOneByOne::create(); touchListener->onTouchBegan = CC_CALLBACK_2(MainScene::onTouchBegan, this); touchListener->onTouchEnded = CC_CALLBACK_2(MainScene::onTouchEnded, this); _eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this); } //触摸事件开始 bool MainScene::onTouchBegan(cocos2d::Touch *touch, cocos2d::Event *unused_event) { Point touchP0 = touch->getLocation(); //触摸点 firstX = touchP0.x; firstY = touchP0.y; return true; } //触摸结束触发 void MainScene::onTouchEnded(cocos2d::Touch *touch, cocos2d::Event *unused_event) { // 获取触摸点位置 Point touchP0 = touch->getLocation(); // 获取X轴和Y轴的移动距离 endX = firstX - touchP0.x; endY = firstY - touchP0.y; // 判断X轴和Y轴的移动距离,如果X轴的绝对值大于Y轴的绝对值就是左右否则是上下 if (abs(endX) > abs(endY)) { // 左右 if (endX + 5 > 0) { // 左边 if (doLeft()) { MenuLayer::playEffect("Sound/move.wav", false); autoCreateCardNumber(); doCheckGameOver(); }//右,上,下省略 } } }
//调用手势识别的事件监听器,添加事件监听 void MainScene::SetTouchListener() { auto touchListener = EventListenerTouchOneByOne::create(); touchListener->onTouchBegan = CC_CALLBACK_2(MainScene::onTouchBegan, this); touchListener->onTouchEnded = CC_CALLBACK_2(MainScene::onTouchEnded, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this); }
//触摸事件开始 bool MainScene::onTouchBegan(cocos2d::Touch *touch, cocos2d::Event *unused_event) { Point touchP0 = touch->getLocation(); //触摸点 firstX = touchP0.x; firstY = touchP0.y; return true; }
//触摸结束触发 void MainScene::onTouchEnded(cocos2d::Touch *touch, cocos2d::Event *unused_event) { // 获取触摸点位置 Point touchP0 = touch->getLocation(); // 获取X轴和Y轴的移动距离 endX = firstX - touchP0.x; endY = firstY - touchP0.y;
// 判断X轴和Y轴的移动距离,如果X轴的绝对值大于Y轴的绝对值就是左右否则是上下 if (abs(endX) > abs(endY)) { // 左右 if (endX + 5 > 0) { // 左边 if (doLeft()) { MenuLayer::playEffect("Sound/move.wav", false); autoCreateCardNumber(); doCheckGameOver(); }//右,上,下省略 } } }
有卡片的游戏中初始化卡片的套路
CardSprite* CardSprite::createCardSprite(int numbers, int width, int height, float CardSpriteX, float CardSpriteY) { // 初始化卡片精灵 CardSprite *enemy = new CardSprite(); if (enemy && enemy->init()) { enemy->autorelease(); enemy->enemyInit(numbers, width, height, CardSpriteX, CardSpriteY); return enemy; } CC_SAFE_DELETE(enemy); return NULL; }
该游戏开发的难点在于识别手势后卡片的逻辑
//向左 bool MainScene::doLeft() { //log("doLeft"); bool isdo = false; // 最外层循环为4*4迭代 for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { //判断卡片是合并还是清空 for (int x1 = x + 1; x1 < 4; x1++) { if (cardArr[x1][y]->getNumber() > 0) {//有数字 if (cardArr[x][y]->getNumber() <= 0) { //为空 //设置为右边卡片的数值 cardArr[x][y]->setNumber(cardArr[x1][y]->getNumber()); cardArr[x1][y]->setNumber(0); x--; isdo = true; } else if (cardArr[x][y]->getNumber() == cardArr[x1][y]->getNumber()) { //当前卡片的值与其比较卡片的值相等,设置为其的2倍 cardArr[x][y]->setNumber(cardArr[x][y]->getNumber() * 2); cardArr[x1][y]->setNumber(0); MenuLayer::playEffect("Sound/merge.wav", false); cardArr[x][y]->runAction(Sequence::create(ScaleTo::create(0.05, 1.1), ScaleTo::create(0.05, 1.0), NULL)); //设置分数 lastScore += cardArr[x][y]->getNumber(); if (lastScore > maxScore) { maxScore = lastScore; } labelTTFlastScore->setString(__String::createWithFormat("%i", lastScore)->getCString()); labelTTFmaxScore->setString(__String::createWithFormat("%i", maxScore)->getCString()); isdo = true; } break; //跳出,否则无法运行 } } } } return isdo; }
菜单层中的继续按钮与重新开始按钮的动效不同,前者是缩小到消失,后者是清空变黑
//继续按钮 void MenuLayer::ContinueTouch(cocos2d::Ref *pSender, Widget::TouchEventType type) { switch (type) { case Widget::TouchEventType::ENDED: MenuLayer::playEffect("Sound/move.wav",false); //点击的音效 auto seq = Sequence::create(ScaleTo::create(0.2, 0), CallFuncN::create(CC_CALLBACK_1(MenuLayer::Close, this)), NULL); //弹窗关闭动画 0.2s逐渐缩小到消失 imgBack->runAction(seq); break; } } //关闭菜单层 void MenuLayer::Close(Node* pSender) { this->removeFromParentAndCleanup(true);//把该层从父空间清空 } //重新开始按钮 void MenuLayer::RestartTouch(cocos2d::Ref *pSender, Widget::TouchEventType type) { switch (type) { case Widget::TouchEventType::ENDED: MenuLayer::playEffect("Sound/move.wav",false); //点击的音效 MainScene::ClearData(); Director::getInstance()->replaceScene(TransitionFade::create(0.5f, MainScene::createScene())); //清空变黑,场景替换 break; } }
//继续按钮 void MenuLayer::ContinueTouch(cocos2d::Ref *pSender, Widget::TouchEventType type) { switch (type) { case Widget::TouchEventType::ENDED: MenuLayer::playEffect("Sound/move.wav",false); //点击的音效 auto seq = Sequence::create(ScaleTo::create(0.2, 0), CallFuncN::create(CC_CALLBACK_1(MenuLayer::Close, this)), NULL); //弹窗关闭动画 0.2s逐渐缩小到消失 imgBack->runAction(seq); break; } }
//关闭菜单层 void MenuLayer::Close(Node* pSender) { this->removeFromParentAndCleanup(true);//把该层从父空间清空 }
//重新开始按钮 void MenuLayer::RestartTouch(cocos2d::Ref *pSender, Widget::TouchEventType type) { switch (type) { case Widget::TouchEventType::ENDED: MenuLayer::playEffect("Sound/move.wav",false); //点击的音效 MainScene::ClearData(); Director::getInstance()->replaceScene(TransitionFade::create(0.5f, MainScene::createScene())); //清空变黑,场景替换 break; } }
制作一个高仿的2048游戏是十分简单基础的,还有动画,音效等设置的代码就不贴了。这里格式有些乱,我的github上有源码,网上也有参考教程,有兴趣的同学可以进一步了解。
最后,cocos2d-x 推荐学习路线: 1.麦子学院,极客学院 cocos 入门教学视频系列 2 .cocos 官方文档,教程 3.小游戏项目实战 4. lua 脚本 5.极客学院物理引擎,数据操作,绘图 API 等进阶视频 6. cocos2d-x 底层源码 7.大项目实战 ......
The text was updated successfully, but these errors were encountered:
这个项目是否可以移植到各个平台呢
Sorry, something went wrong.
No branches or pull requests
游戏引擎:cocos2d-x v3.8.1
主要语言:C++
运行平台:win,ios,android
开发流程:
(一) 收集图片素材
(二) Cocos Studio制作静态UI
(此步可被代码代替,但使用可视化工具更加便捷)
主场景:
先添加图片资源,创建基础容器 root,在 root 子树上添加一系列静态元素,这里除了菜单按钮需要用户交互外其他都是图片类型(Cocos Studio中图片与精灵区别不大)。修改相应元素的名称,以便在写代码时做对应操作。
菜单层:
因为菜单是在主场景的基础上跳出来的,所以作为一个层来处理。同样添加所需的元素。
(三) Cocos2d-x 编写游戏逻辑
(1) 类的定义(工厂模式)
先创建游戏所需场景MainScene,所需层MenuLayer的 .cpp 与 .h 文件,又由于2048游戏主要体现在对卡片的操作上,且比较复杂,所以这里再创建 CardSprite 的 .cpp 与 .h 文件。
主场景:
菜单层:
卡片精灵:
(2) 方法定义
Cocos2d-x是个功能强大的集成库,其提供的方法可满足大部分游戏的需求,写代码时配合官方文档食用风味更佳。
获取 Cocos Studio 的UI页面
2048游戏的特色在于识别上下左右手势,对卡片进行移动或合并操作。(这里有点要注意,当初困扰了我一天,一定要把Cocos Studio中除按钮外的控件取消交互性,否则会和手势识别冲突。)
有卡片的游戏中初始化卡片的套路
该游戏开发的难点在于识别手势后卡片的逻辑
菜单层中的继续按钮与重新开始按钮的动效不同,前者是缩小到消失,后者是清空变黑
制作一个高仿的2048游戏是十分简单基础的,还有动画,音效等设置的代码就不贴了。这里格式有些乱,我的github上有源码,网上也有参考教程,有兴趣的同学可以进一步了解。
最后,cocos2d-x 推荐学习路线:
1.麦子学院,极客学院 cocos 入门教学视频系列
2 .cocos 官方文档,教程
3.小游戏项目实战
4. lua 脚本
5.极客学院物理引擎,数据操作,绘图 API 等进阶视频
6. cocos2d-x 底层源码
7.大项目实战
......
The text was updated successfully, but these errors were encountered: