博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
在2020年学习cocos游戏引擎
阅读量:2028 次
发布时间:2019-04-28

本文共 11424 字,大约阅读时间需要 38 分钟。

常用链接

参考书目

《Cocos2d-X游戏开发实战精解》

《我所理解的Cocos2d-x》
《Effective C++》中文版第三版

环境搭建

macOS 10.15.6

Xcode 11.5
cocos2d-x 3.17.2
cmake 3.17.3

创建工程

采用cocos2d-x 3.17版本可直接通过cocos console创建,4.0版本需要额外通过cmake生成.xcodeproj文件。

cocos new 工程名 -p com.cocos2dx.工程名 -l cpp -d 目录名(/Users/xxx)

架构分析

目录分析

cocos目录结构

Classes存放逻辑代码,Resource存放资源文件
C++文件由.hpp(声明)和.cpp(定义及初始化)组成

AppDelegate.h

#ifndef  _APP_DELEGATE_H_  // 宏定义 保证头文件不需要多次编译#define  _APP_DELEGATE_H_#include "cocos2d.h"class  AppDelegate : private cocos2d::Application{
public: AppDelegate(); // 构造 virtual ~AppDelegate(); // 虚析构 virtual void initGLContextAttrs(); // 初始化openGL参数 virtual bool applicationDidFinishLaunching(); // 应用进入 virtual void applicationDidEnterBackground(); // 应用中途退入后台 virtual void applicationWillEnterForeground(); // 应用中途来电 // 虚析构函数能够保证当用一个基类的指针删除一个派生类的对象时,派生类的析构函数会被调用 // 虚函数被继承后仍然是虚拟函数,可以省略掉关键字“virtual”};#endif // _APP_DELEGATE_H_

AppDelegate.cpp

#include "AppDelegate.h"#include "MainScene.h"USING_NS_CC;// visiableSize static cocos2d::Size designResolutionSize = cocos2d::Size(1386, 640);static cocos2d::Size smallResolutionSize = cocos2d::Size(480, 320);static cocos2d::Size mediumResolutionSize = cocos2d::Size(1024, 768);static cocos2d::Size largeResolutionSize = cocos2d::Size(2048, 1536);AppDelegate::AppDelegate(){
}AppDelegate::~AppDelegate() {
}void AppDelegate::initGLContextAttrs(){
// set OpenGL context attributes: red, green, blue, alpha, depth, stencil, multisamplesCount GLContextAttrs glContextAttrs = {
8, 8, 8, 8, 24, 8, 0}; GLView::setGLContextAttrs(glContextAttrs);}bool AppDelegate::applicationDidFinishLaunching() {
auto director = Director::getInstance(); auto glview = director->getOpenGLView(); if(!glview) {
director->setOpenGLView(glview); } // 显示演示信息 director->setDisplayStats(true); // 设置帧率 director->setAnimationInterval(1.0f / 60); // designResolutionSize 设计分辨率大小 glview->setDesignResolutionSize(designResolutionSize.width, designResolutionSize.height, ResolutionPolicy::FIXED_WIDTH); // frameSize 手机分辨率大小 auto frameSize = glview->getFrameSize(); // 适配策略 if (frameSize.height > mediumResolutionSize.height) {
director->setContentScaleFactor(MIN(largeResolutionSize.height/designResolutionSize.height, largeResolutionSize.width/designResolutionSize.width)); } else if (frameSize.height > smallResolutionSize.height) {
director->setContentScaleFactor(MIN(mediumResolutionSize.height/designResolutionSize.height, mediumResolutionSize.width/designResolutionSize.width)); } else {
director->setContentScaleFactor(MIN(smallResolutionSize.height/designResolutionSize.height, smallResolutionSize.width/designResolutionSize.width)); } // 创建场景 auto mainScene = MainScene::createScene(); // 导演类调度场景 director->runWithScene(mainScene); return true;}void AppDelegate::applicationDidEnterBackground() {
Director::getInstance()->stopAnimation();}void AppDelegate::applicationWillEnterForeground() {
Director::getInstance()->startAnimation();}

MainScene.hpp

#ifndef __MAIN_SCENE_H__#define __MAIN_SCENE_H__#include "cocos2d.h"// 继承Sceneclass MainScene : public cocos2d::Scene{
public: static cocos2d::Scene* createScene(); // 静态,用于获取场景对象 virtual bool init() override; // 初始化场景 CREATE_FUNC(MainScene); // };#endif // __HELLOWORLD_SCENE_H__

MainScene.cpp

#include "MainScene.h"USING_NS_CC; // 等同于 using namespace cocos2dScene* HelloWorld::createScene(){
auto scene = Scene::create(); // 创建一个Scene对象 auto layer = MainScene::create(); // 创建一个MainScene对象 scene->addChild(layer); // 将layer加入到场景中 return scene;}bool mainScene::init(){
if ( !Scene::init() ) {
return false; } // 在这里添加逻辑代码 return true;}

层的生命周期函数

bool init()                             // 初始化层调用void onEnter()                          // 进入层时调用void onEnterTransitionDidFinish()       // 进入层且过渡动画结束时调用void onEixt()                           // 退出层时调用void onEixtTransitionDidStart()         // 退出层且开始过渡动画时调用void cleanup()                          // 层对象被清除时调用

场景文件

// .h文件#ifndef __SET_SCENE__#define __SET_SCENE__#include "cocos2d.h"class SetScene : public cocos2d::Scene{
public: static cocos2d::Scene* createScene(); virtual bool init(); CREATE_FUNC(SetScene);private: int volume = 50;};#endif --------------------------------------------------------------------------------------// .cpp文件#include"SetScene.h"USING_NS_CC;Scene* SetScene::createScene() {
return SetScene::create(); }bool SetScene::init(){
if (!Scene::init()) {
return false; } auto visibleSize = Director::getInstance()->getVisibleSize(); Vec2 origin = Director::getInstance()->getVisibleOrigin();}

普通文件

// .h文件#ifndef _PROP_H_#define _PROP_H_#include "cocos2d.h"USING_NS_CC;class Prop : public Entity {
public: Prop(); ~Prop(); CREATE_FUNC(Prop); virtual bool init();};#endif;--------------------------------------------------------------------------------------// .cpp文件#include "prop.h"Prop::Prop() {
}Prop::~Prop() {
}bool Prop::init() {
return true; }void Prop::createProp(float _x, float _y) {
this->x = x; this->y = y; }}--------------------------------------------------------------------------------------// 实例化Prop* props = Prop::create();props->createProp(10, 20);

二段构造

// 二段构造的宏函数,其中(std::nothrow)当new失败后强制返回指针,而非try-catch异常#define CREATE_FUNC(__TYPE__) \static __TYPE__* create() \{ \    __TYPE__ *pRet = new(std::nothrow) __TYPE__(); \    if (pRet && pRet->init()) \    { \        pRet->autorelease(); \        return pRet; \    } \    else \    { \        delete pRet; \        pRet = nullptr; \        return nullptr; \    } \}

二段构造并非经典23种设计模式之一,按照cocos2d-x创始人王哲对于为什么要设计成二段构建的看法:

其实我们设计二段构造时首先考虑其优势而非兼容cocos2d-iphone。初始化时会遇到图片资源不存在等异常,而C++构造函数无返回值,只能用try-catch来处理异常,启用try-catch会使编译后二进制文件大不少,故需要init返回bool值。Symbian, Bada SDK,objc的alloc + init也都是二阶段构造。

我们暂且接受非兼容cocos2d-iphone这个理由(反正我不信)。按我个人的理解,既然C++现在已经愿意支持try-catch了,说明C++本身已经不在乎这些二进制文件的体量问题了,更不用说对于java、C#等一些语言来说异常已是必备的特性。而且既然C++都决定支持异常,还为了这些老版本的技术提供(std::nothrow)强制返回指针,自然也表明了并不推荐返回指针了。

所以实际上对于cocos来说,已经不需要采用二段构建来实例化一个类了,只是没有人在愿意调整框架底层,cocos的每个内置类诸如Sprite、Button等都是采用的二段构建。所以对于开发者来说,需要用的地方自然是要用的,自己写的类可用可不用。不过cocos在实现二段构建的同时,已经实现了简化版的垃圾回收机制,可以省去new/delete操作,所以还是能够简化一些操作的。


常用功能

UI布局

Layer的锚点默认为左下角,其他Node的锚点默认为中心

Layer要设置锚点,必须先:layerTest->setIgnoreAnchorPointForPosition(false);
锚点不等于原点

切换场景

// include "ShopScene.hpp"Director::getInstance()->replaceScene(ShopScene::createScene());

通过图集加载图片

// 使用texture package将美术提供的tps文件转化为plist和pvr.czz文件ZipUtils::setPvrEncryptionKey() // plist->czz需要md5秘钥解码SpriteFrameCache *sfc = SpriteFrameCache::getInstance(); // 定义SpriteFrameCachesfc->addSpriteFrameWithFile("xxx.plist"); // 调用实例方法addSpriteFrameWithFile()auto mainBg = Sprite::createWithSpritesFrameName("xxx.png"); // 使用图集加载图片

添加Button

// include "ui/CocosGUI.h"auto btn = cocos2d::ui::Button::create();btn->loadTextures("xxx_normal.png", "xxx_pressed.png", "", cocos2d::ui::Widget::TextureResType::PLIST);btn->setPosition(Vec2(20, 100));this->addChild(btn);

添加文本

auto label = Label::createWithTTF("Hello World", "fonts/Marker Felt.ttf", 24);auto label2 = Label::createWithSystemFont("Hello World", "Arial", 24);label->setPosition(Vec2(20, 100));this->addChild(label);

添加事件

// 方法一:设置监听器,由_eventDispatcher派发事件。需要注意的是,在添加到多个对象时,需要使用clone()方法。auto listener = EventListenerTouchOneByOne::create();	listener->setSwallowTouches(true);	listener->onTouchBegan = [=](Touch *touch, Event* event) {
// 自己实现事件区域检测,默认全屏可触发 auto target = static_cast
(event->getCurrentTarget());//获取到你点击的对象具体是哪个精灵 Point locationInNode = target->convertTouchToNodeSpace(touch);//获取到点击位置在你这个对象的相对位置 Size size = target->getContentSize();//对象内容大小,在后面用来判断是否点中了某对象的区域 Rect rect = Rect(0, 0, size.width, size.height);//包含这个对象的矩形区域 if (rect.containsPoint(locationInNode))//矩形局域检测,点是否在矩形内部 {
printf("点到了图片"); Director::getInstance()->replaceScene(HelloWorld::createScene()); return true; } return false; }; _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, btn); // 方法二:直接通过对象挂载事件监听器btn->addTouchEventListener([&](Ref* sender, cocos2d::ui::Widget::TouchEventType type){
switch (type) {
default: break; case ui::Widget::TouchEventType::BEGAN: break; case ui::Widget::TouchEventType::ENDED: break; }});// []:默认不捕获任何变量;// [=]:默认以值捕获所有变量;// [&]:默认以引用捕获所有变量;// [x]:仅以值捕获x,其它变量不捕获;

添加菜单

// 1. 创建标签auto volumeHigherLab = Label::createWithTTF("+", "fonts/Marker Felt.ttf", 150);auto volumeLowerLab = Label::createWithTTF("-", "fonts/Marker Felt.ttf", 150);// 2. 创建菜单项auto volumeHigherMenu = MenuItemLabel::create(volumeHigherLab, CC_CALLBACK_1(SetScene::menuCloseCallbackVolumeHigher, this));auto volumeLowerMenu = MenuItemLabel::create(volumeLowerLab, CC_CALLBACK_1(SetScene::menuCloseCallbackVolumeLower, this));// 3. 创建菜单MenuHigherVolume = Menu::create(volumeHigherMenu, NULL);MenuLowerVolume = Menu::create(volumeLowerMenu, NULL);// 4. 设置位置并添加到场景中MenuHigherVolume->setPosition(850, 250);MenuLowerVolume->setPosition(960, 260);this->addChild(MenuHigherVolume, 1);this->addChild(MenuLowerVolume, 1);

添加动画

// 绕y轴旋转180,5sauto* rotateBy = RotateBy::create(5.0f, Vec3(0, 180, 0));// 定义回调函数auto* callFun = CallFunc::create(CC_CALLBACK_0(MainScene::rotateFun, this));// 定义动画序列auto* sequence = Sequence::create(rotateBy, callFun, NULL);sprite->runAction(sequence);

添加定时器

// 在init()中进行调用scheduleUpdate(); // 重写Update(float dt)方法schedule(schedule_selector(MainScene::myUpdate), 0.2f);  // 自定义方法

读取XML文件

// #include 
auto doc = new tinyxml2::XMLDocument();doc->Parse(FileUtils::getInstance()->getStringFromFile("data.xml").c_str()); // 调用解析函数auto root = doc->RootElement(); // 从根节点开始查找for (auto e = root->FirstChildElement(); e != NULL; e = e->NextSiblingElement()) {
for (auto attr = e->FirstAttribute(); attr != NULL; attr = attr->Next()) {
printf("%s %s\n", attr->Name(), attr->Value()); }

读取json文件

// #include 
rapidjson::Document d;d.Parse<0>(FileUtils::getInstance()->getStringFromFile("data.json").c_str()); // 调用解析函数 <0>默认解析方式printf("%s",d[0]["name"].GetString());

读取本地存储

UserDefault::getInstance()->getIntegerForKey("int"); // 设置keyUserDefault::getInstance()->setIntegerForKey("int", 999); // 读取keyprintf("saved file path is %s\n", UserDefault::getInstance()->getXMLFilePath().c_str()); // 存储路径

网络编程

弱联网:CURL库

强联网:socket

音频控制

// 声明 .hCocosDenshion::SimpleAudioEngine* audio;// 定义 .cppaudio = CocosDenshion::SimpleAudioEngine::getInstance();if (!audio->isBackgroundMusicPlaying())	audio->playBackgroundMusic("xxx.mp3", true);

骨骼动画

// #include "cocostudio/CocoStudio.h"// using namespace cocostudio;// 引入骨骼动画文件,保证plist和json在同一个目录下ArmatureDataManager::getInstance()->addArmatureFileInfo("ani_mainshop.ExportJson");auto armature = Armature::create("ani_mainshop");armature->getAnimation()->playWithIndex(1); // 按照Animation的index添加动画armature->setPosition(Vec2(0, 0));mainShopCarBg->addChild(armature, 1);

游戏控制

CCScheduler* defaultScheduler = CCDirector::sharedDirector()->getScheduler();defaultScheduler->setTimeScale(2.0f); // 全局加速defaultScheduler->pauseTarget(this); // 暂停游戏defaultScheduler->resumeTarget(this); // 恢复游戏

开发经验

最小化在编写代码前需要了解的信息

不是解决任何问题都要从头做起
框架只是让你规范地去开发
设计模式是学习OOP的最佳模板

转载地址:http://kqjaf.baihongyu.com/

你可能感兴趣的文章
【IDEA】【6】Maven打包
查看>>
【Oracle】【6】去掉字符串中的空格/字符
查看>>
【Navicat】【1】解决 导入保存为txt文件的数据 中文乱码问题
查看>>
【HTML&CSS】【1】让DIV中的文字换行显示
查看>>
【MySQL】【4】数据库时间与实际时间相差8小时
查看>>
【Git】【1】简单介绍
查看>>
【记录】【2】各种在线网址
查看>>
【Mybatis】【4】mybatis Example Criteria like 模糊查询
查看>>
【Java】【12】Double类型精确的加减乘除运算
查看>>
【Oracle】【9】取前N条记录——rownum和row_number() over()的使用
查看>>
【Java】【15】判断url对应的图片是否存在
查看>>
【JS】【14】判断对象是否为{}
查看>>
【Java】【18】去掉字符串的最后一个字符
查看>>
【Java】【22】读写properties文件
查看>>
【Maven】【2】遇到的问题
查看>>
【其他】【EditPlus】【1】操作使用
查看>>
【其他】【远程控制】【1】安装VNC
查看>>
【实战问题】【9】service层接口无法被注入调用
查看>>
【其他】【freemarker】【1】遇到的问题
查看>>
【Mybatis】【7】order by 使用动态参数时需要注意,用${}而不是#{}
查看>>