cocos2dx源码完全解析,cocos2dx开发
win7 用vs2015 打开cocos2dx 源码,一大片提示错误,都是 namespace "cocos2d::std" 没有 xxx 这种
可能程序不兼容, 可以更换个版本试试。另外建议参考下程序对配置的要求。
或者右键需要运行的程序 选择兼容性 用兼容模式运行试试。
怎样制作基于Cocos2d-x的SLG游戏
创建项目
如果要说到环境搭建,那又该是一篇长篇大论了,所以这里我就不说了,不清楚的同学到网上问度娘吧,会有很多不同系统不同版本搭建的结果的。
Cocos2d-x创建项目的方式一直都在不停的改动,所以这里我觉得很有必要给大家介绍下如何创建项目。
创建3.2的项目其实很简单,打开终端(Windows是cmd)进入到引擎文件夹目录,然后输入以下命令就可以创建。
cocos new SLG -p com.cocos2dx.rs -l cpp -d /Users/cocos2d-x/workspace/cocos2dx/cocos2d-x-3.2/projects
如下图:
p1
说明:
new:new后是项目名
-p :-p后是包名
-l :-l后是语言(cpp指c++)
-d :-d后是项目生成路径
如果一切无误,那等待几分钟以后你就可以在给定的目录下找到新建的项目了。打开项目工程后,下面我们就可以开始游戏的制作了。
前期准备
打开工程后,运行程序,你会发现它不是一个空的项目,在Classes和Resource文件夹下Cocos2d-x已经给出了一些实质性的东西,当然这些不是一定都有用的,它们存在的目的是为了给我们展示一个典型的Cocos2d-x的例子。
除了AppDelegate.h 和 AppDelegate.cpp文件,这两个文件夹下其他的东西都是可被删除的(不过在删除之前,可以先看一下HelloWorld类,了解下它的类结构、类方法,以便对Cocos2d-x进行初步的学习,也方便初学者依葫芦画瓢再写一个类似的场景)。AppDelegate类是创建项目时自动生成的一个类,它控制着游戏的生命周期,是Cocos2d-x游戏的通用入口文件,类似于一般 Windows 工程中main函数所在的文件。
打开AppDelegate.cpp文件,在游戏加载期的最后一个applicationDidFinishLaunching()函数中我们可以设置第一个启动的游戏场景,如下:
auto scene = GameScene::createScene();
director-runWithScene(scene);
GameScene是我们新建的一个游戏场景,下面会讲解。
分辨率适配
在我的游戏中,首先第一件事还是做分辨率适配,这是个恒古不变的定律。现如今市场中各种屏幕尺寸和分辨率的移动设备层出不穷,为了更好地适应这些设备,游戏的分辨率适配是十分有必要的。
在农场游戏中,很最要的一点就是实现大地图背景的拖动、放大缩小等操作,所以可想而知,我们的地图不是全部都显示在屏幕内的,也就是说,我们不能像之前的方式那样把整个显示内容做适配,我们应该留有一定的边距供玩家拖动。如下图所示:
p2
同样是在applicationDidFinishLaunching函数中,我们添加如下一段代码对游戏做分辨率适配,以便它能更好的适应不同的运行环境。
glview-setDesignResolutionSize(480.0f, 320.0f, ResolutionPolicy::FIXED_HEIGHT);
std::vector searchPath;
searchPath.push_back("H_1920");
FileUtils::getInstance()-setSearchPaths(searchPath);
director-setContentScaleFactor(1440.0f / 320.0f);
分辨率适配的原理,建议大家阅读一下:Cocos2d-x 多分辨率适配完全解析这篇文章,虽然它不是针对最新版Cocos2dx引擎,但它还是能很清楚的告诉你分辨率适配的原理和方法。
还有要说明的一点是,我们的游戏地图的高为1920,分辨率适配时则只设为了1440,意思就是说,我们本该全在屏幕内的内容留出了4分之一的高度在屏幕外。
编辑游戏地图
模拟经营游戏中,游戏地图多为拼接而成,这里我们用瓦片地图编辑器(Tiled Map Editor)来制作游戏的地图,它可以把编辑后的地图文件保存为TMX格式的文件,能被Cocos2d-x很好的支持。瓦片地图(Tile Map)不但生成简单,而且可以灵活的用于引擎中。关于瓦片地图的介绍可参考瓦片地图一文。
接下来我们开始创建地图。
运行TiledMap编辑器,新建一个地图文件。填写如下对话框:
p3
在地图方向选项内,可以选择正常、45度(传说中的2.5D)和45度交错,这里我们选择45度。接下来需要设置地图大小,这里的数值是指有多少格tile元件,并不是像素,这里我们选择30×30的地图。
最后是确定tile元件的大小,根据美工提供的地面元件大小设置,这个教程里,我们使用128×64的大小。
在地图大小一栏中,你可以看到最终的地图大小为3840 * 1920。
点击确认之后,你可能已经发现了,这个游戏地图它是菱形的,如下图所示。
p4
这里你可能会想,为什么要菱形的啦,选择45度交错建一个接近矩形的不行吗?呵呵,其实这样也是可以的,只不过啦,Cocos2d-x引擎中默认是不支持45度交错的,如果需要在引擎中加载这种交错的地图必须自己修改引擎代码(看到这,是不是整个人都不好了),而且在修改过后还不能正确的得到地图的大小,需要自己编写计算大小的公式代码。鉴于这一点,我在想,难道《全名农场》、《请叫我海盗》等游戏地图的四个角都不能被点击操作都是因为这个原因吗? 哈哈,就当是我想多了吧。其实菱形就菱形吧,其他的游戏也都这样,可以在菱形地图的下层贴一层背景来掩盖这一现象。
接下来,我们还是回到正题开始拼接地图吧。选择地图-》新图块,然后填写如下所示的对话框。
p5
选择浏览按钮,将准备好的图块文件载入编辑器。接着设置图块的宽度和高度(默认情况下是一个tile元件的大小,但),根据图块文件中图块的大小来设置。边距、间距什么的,可不做修改,0就好。
最后选中相应的图块,拖动到渲染区拼一个理想的地图。
p6
暂时我们就只简单的拼一个地图就可以了,后面再根据游戏需要,设置一些必要的对象和属性。
加载地图资源
新建一个GameScene场景,加载游戏地图。不过在此之前,请把编辑好的tmx和图块文件拷贝到Resource文件夹下。GameScene的结构和HelloWorld差不多,照着HelloWorld类依葫芦画瓢就可以建一个。你可以先看看它是如何实现的,再实现自己的类。这里就不多说了。
在Cocos2d-x中使用TMX,有以下流程供你参考:
首先用地图编辑器编辑你的地图,导出成TMX 格式。
将导出的TMX 文件和相关图片放在工程的Resoure文件夹下。
使用Cocos2d-x中TMXTileMap 类的create方法创建地图对象,TMX 文件的解析是引擎内部完成的,所以我们不需要担心。TMXTileMap 是Node 的子类,因此只要添加到场景中即可。
通过TMXTileMap,可以获得其他相关对象,比如单个瓦片(属Sprite类),比如对象组(ObjectGroup类),比如层(TMXLayer类)等;你可以通过TMXLayer类修改,删除或者添加某个网格位置的瓦片,这样可以动态的修改地图了,你还可以进行其他的操作,相关的API 我们后面使用到了再做讲解。
在GameScene类的init函数中添加如下的代码创建游戏地图:
mapLayer = LayerColor::create(Color4B(78,127,41,255));
this-addChild(mapLayer,-1);
auto map = TMXTiledMap::create("mymap4.tmx");
mapLayer-setContentSize(map-getContentSize());
mapLayer-addChild(map, 10);
auto treeSprite = Sprite::create("1.png");
treeSprite-setAnchorPoint(Vec2(0, 0));
treeSprite-setPosition(Vec2(0, 0));
treeSprite-setScale(2);
mapLayer-addChild(treeSprite, 11);
代码中新建了一个带颜色的背景层,背景层的尺寸等于加载的TMX地图大小。接着把地图和如下的一个遮盖层(其实就是为了防止菱形的地图看起来那么突兀而添加的一层)依次添加到层上。
cocos2dx 3.x版本中导入tiled地图如何导入对象层中对象的自定义信息(源码+注释)
步骤如下:
1. cocos2d-x 中使用类 TMXTiledMap 创建瓦片地图
2. 使用 TMXTiledMap 中的 getObjectGroup 接口取得对象层
3. 使用对象层的 getObject 接口获取对象信息,对象信息是一个 ValueMap 实例
4. 从 ValueMap 中取出数据
测试地图如下所示:
如图所示:
1. 有一个名为 object 的对象层
2. object 对象层中有一个名为 test 的对象
3. test 对象有一个名为 attr 的属性
代码以及注释如下所示:
cocos2dx CCFileUtils 或者CCDictionary如何读取文件
在cocos2d-x中可以用.plist格式的文件来保存数据,它是XML文件格式的一种,在cocos2d-x解析.plist方面相关的资料比较少,但本身也很简单,要解析.plist文件可以参考cocos2d-x类库中的CCSpriteFrameCache类和CCParticleSystem类,它主要是使用CCDictionary类来对.plist文件进行操作。
下面有一个.plist文件:
?xml version="1.0" encoding="UTF-8"?
plist version="1.0"
dict
keylevel1/key
dict
keybg_far_scene/key
dict
keypath/key
stringimages/far_scene.png/string
keypos/key
string{358, 309}/string
/dict
keybg_near_scene/key
dict
keypath/key
stringimages/near_scene.png/string
keypos/key
string{360, 100}/string
/dict
/dict
/dict
/plist
读取.plist文件的代码如下:
const char* testPlistPath = "BSPlistDatas\\test.plist";
const char* fullPath = CCFileUtils::sharedFileUtils()-fullPathFromRelativeFile("test.plist", testPlistPath);
CCDictionary* plistDic = CCDictionary::createWithContentsOfFile(testPlistPath);
CCDictionary* levelDic = dynamic_castCCDictionary*(plistDic-objectForKey("level1"));
CCDictionary* farScene = dynamic_castCCDictionary*(levelDic-objectForKey("bg_far_scene"));
CCString* farScenePath = dynamic_castCCString*(farScene-objectForKey("path"));
CCPoint point = CCPointFromString(farScene-valueForKey("pos")-getCString());
CCLog("path = %s", farScenePath-getCString());
CCLog("pos = %f, %f", point.x, point.y);
第一行是.plist文件的相对路径,通过CCFileUtils类获得文件中绝对路径后,使用CCDictionary::createWithContensOfFile(filePath);将文件中内容加载到CCDictionary数据结构的内存中,然后通过xxxForKey获得相应的key下的value。
这里需要注意的是,当在读取'pos'的时候,它的值一个{x, y}的字符串,这是.plist文件中的数组存储规则,我们可以通过cocos2d-x提供函数api将这样的字符串转化为CCpoint对象。
CCPoint point = CCPointFromString(farScene-valueForKey("pos")-getCString());
上面这句话就是做了这样的一个转化的过程,同样的cocos2d-x还支持CCSize、CCRect的字符串的转化。他们转化的方法以及在.plist中对应的字符串格式如下:
CCPoint: CCPointFromString();{x, y}
CCSize: CCSizeFromString();{w, h}
CCRect: CCSizeFromString();{x, y, w, h}
这样我们2D游戏所初始化所需要的数据都基本上够用了,可以尝试将游戏的初始数据放在.plist中,然后修改调整数值就可以直接修改plist文件,而无需重新编译程序了,从而实现游戏数据和游戏逻辑的分离。
cocos2dx 3.10 事件机制
cocos2dx的事件机制里存在三类: Event、EventListener、EventDispatcher
先理解一下它们之间的关系
当我们按下按钮时(Event),会触发一个特定的事件(EventListener相当于回调函数),而这个特定的事件又存储在EventDispatcher里,可能按下这个按钮会触发多个事件,而事件的先后就是靠EventDispatcher来决定的。
Event的相关类
当出现来自鼠标,键盘,触屏,摇杆等输入源的输入时,这个事实称之为事件
引擎无时无刻都在感受事件。
Event
可以看出Event主要包含三个变量,一个是事件类型_type(也就是定义的枚举类型:触摸、键盘等),isStopped判断事件是否停止,只要事件停止,其相关的Listener都要停止callback调用。
EventTouch
它对应于四种触摸操作,不同的EventCode可以告诉Listener来调用不同的callback。
EventCustom
它是用户自定义事件,userData记录用户自定义数据,另一个eventName是用户给事件取的别名
上面的源码都有英文注释,我就不多解释了,我只说一个最重要的_isRegistered,它判断事件有没有被注册,如果没有被注册就不会触发。(如何注册事件?将事件加入dispatcher)
在讲它之前,我们先了解一下它的一个重要变量。
sceneGraphListeners: 一个事件(比如说触摸事件),需要按照一定的响应序列,依次对这些Node进行事件响应,所以该类型的事件都会绑定一个与此相关联的node,并且 响应顺序是与node在scene下的zorder相关的 。该类型下的事件优先级统一为0。(与渲染树有关)
fixedListeners: 优先级根据 fixedPriority 的数值从小往大排序、
只要出现了删除,修改,添加监听器的时候,监听器列表需要重新排序,都需要设置相应的 DirtyFlag 操作。但是 Cocos-2dx v3.10 里面的 updateListeners 函数有删除监听器的操作,然而并没有设置相应的 DirtyFlag 操作。
会抛出下面的异常
Gt0Index() 方法其实就是获取到当前监听器中 fixedPriority == 0 的监听器在监听器向量中的位置,它 只有在给 Listener 排序的时候会设置,但是如果更新了对应 ListenerID 的向量(EventListenerVector),但是没有重新排序,就会出现 _gt0Index 未及时更新的情况 ,导致抛出这个异常。
引用:
Cocos2dx游戏引擎(3.x)----新的事件分发机制
cocos2dx之event事件(一)
cocos2dx之event事件(三):事件分发器EventDispatcher
Cocos2dx-v3.10 事件分发机制源码解析