新手入门

[Cocos2d-x v3.x官方文档]瓦片地图


glory原创,首发于泰然,转载请注明出处
https://github.com/chukong/cocos-docs/blob/master/manual/framework/native/v3/tiled-map/zh.md
欢迎大家斧正错误,提交PR。

Cocos2d-x官方中文文档 v3.x

在游戏开发过程中,我们会遇到超过屏幕大小的地图,例如即时战略游戏,使得玩家可以在地图中滚动游戏画面。这类游戏通常会有丰富的背景元素,如果直接使用背景图切换的方式,需要为每个不同的场景准备一张背景图,而且每个背景图都不小,这样会造成资源浪费。

瓦片地图就是为了解决这问题而产生的。一张大的世界地图或者背景图可以由几种地形来表示,每种地形对应一张小的的图片,我们称这些小的地形图片为瓦片。把这些瓦片拼接在一起,一个完整的地图就组合出来了,这就是瓦片地图的原理。

TileMap方案

在Cocos2d-x中,瓦片地图实现的是TileMap方案,TileMap要求每个瓦片占据地图上一个四边形或六边形的区域。把不同的瓦片拼接在一起,就可以组成完整的地图了。我们需要很多较小的纹理来创建瓦片。通常我们会将这些较小的纹理放图一张图片中,这样做会提高绘图性能。

瓦片地图编辑器

Cocos2d-x支持由瓦片地图编辑器Tiled Map Editor制作并保存为TMX格式的地图。Tiled Map Editor是一个开源项目,支持Windows、Linux及Mac OS X多个操作系统,我们可以从官网下载到编辑器的Java和QT版本。

如何使用Tiled工具建立地图可以参考以下文章:

如何使用cocos2dx3.0制作基于tilemap的游戏

地图方向

Tiled地图支持直角鸟瞰地图(90°地图)、等距斜视地图(斜45°地图)和六边形地图,不支持左右或上下边界的六边形地图。

地图资源

  • 建议瓦片地图素材大小为32*32的倍数
  • 瓦片素材组与其他图片不能混合使用
  • 只有瓦片素材图能被导入TMX文件
  • 每个Layer最多支持1套瓦片素材组。

瓦片层

  • TMX文件中瓦片层的数量没有上限
  • 每一个瓦片层只能由一种瓦片素材组成
  • 每一个瓦片层可以被TMXLayer类表示-为SpriteSheet的子类
  • 每一个单一的瓦片被Sprite表示-父节点为TMXLayer

对象层

  • 瓦片地图支持对象组
  • 用来添加除背景以外的游戏元素-道具、障碍物等
  • 对象组中的对象在TMX文件中以键值对形式存在,因此可以直接在TMX文件中对他进行修改

瓦片地图坐标系

对于一个16*16的瓦片地图文件的坐标系统为

  • (0, 0): 左上角
  • (15, 15): 右下角

tiledmap

在Cocos2d-x中使用TMX

创建TMX节点

TMXTiledMap *map = TMXTiledMap::create("bg.tmx");
addChild(map, 0);

遍历子节点

Vector pChildrenArray = map->getChildren();

SpriteBatchNode* child = NULL;

Ref* pObject = NULL;

for (Vector::iterator it = pChildrenArray.begin(); it != pChildrenArray.end(); it++) {
    pObject = *it;
    child = (SpriteBatchNode*)pObject;

}

获取/删除一个瓦片

TMXLayer* layer = map->getLayer("layer0");
Sprite* tile0 = layer->getTileAt(Point(1, 15));
layer->removeTileAt(Point(1, 15));

遍历对象层中对象

TMXObjectGroup* objectGroup = map->getObjectGroup("center");
ValueVector object = objectGroup->getObjects();

for (ValueVector::iterator it = object.begin(); it != object.end(); it++) {
    Value obj = *it;
    ValueMap map = obj.asValueMap();
    log("x = %d y = %d", map.at("x").asInt(), map.at("y").asInt());
}

[Cocos2d-x v3.x官方文档]骨骼动画详解-Spine


任珊原创,首发于泰然,转载请注明出处
https://github.com/chukong/cocos-docs/blob/master/manual/framework/native/v3/spine/zh.md
欢迎大家斧正错误,提交PR。

Cocos2d-x官方中文文档 v3.x

游戏中人物的走动,跑动,攻击等动作是必不可少,实现它们的方法一般采用帧动画或者骨骼动画。

帧动画与骨骼动画的区别在于:帧动画的每一帧都是角色特定姿势的一个快照,动画的流畅性和平滑效果都取决于帧数的多少。而骨骼动画则是把角色的各部分身体部件图片绑定到一根根互相作用连接的“骨头”上,通过控制这些骨骼的位置、旋转方向和放大缩小而生成的动画。

它们需要的图片资源各不相同,如下分别是帧动画和骨骼动画所需的资源图:

骨骼动画比传统的逐帧动画要求更高的处理器性能,但同时它也具有更多的优势,比如:

  • 更少的美术资源: 骨骼动画的资源是一块块小的角色部件(比如:头、手、胳膊、腰等等),美术再也不用提供每一帧完整的图片了,这无疑节省了资源大小,能为您节省出更多的人力物力更好的投入到游戏开发中去。
  • 更小的体积: 帧动画需要提供每一帧图片。而骨骼动画只需要少量的图片资源,并把骨骼的动画数据保存在一个 json 文件里面(后文会提到),它所占用的空间非常小,并能为你的游戏提供独一无二的动画。
  • 更好的流畅性: 骨骼动画使用差值算法计算中间帧,这能让你的动画总是保持流畅的效果。
  • 装备附件: 图片绑定在骨骼上来实现动画。如果你需要可以方便的更换角色的装备满足不同的需求。甚至改变角色的样貌来达到动画重用的效果。
  • 不同动画可混合使用: 不同的骨骼动画可以被结合到一起。比如一个角色可以转动头部、射击并且同时也在走路。
  • 程序动画: 可以通过代码控制骨骼,比如可以实现跟随鼠标的射击,注视敌人,或者上坡时的身体前倾等效果。

骨骼动画编辑器——Spine

Spine是一款针对游戏的2D骨骼动画编辑工具,它具有良好的UI设计和完整的功能,是一个比较成熟的骨骼动画编辑器。Spine旨在提供更高效和简洁的工作流程,以创建游戏所需的动画。

使用Spine创建骨骼动画分两大步骤:

  1. 在SETUP模式下,组装角色部件,为其绑定骨骼;
  2. 在ANIMATE模式下,基于绑定好的骨骼创建动画。

下面简单介绍下具体步骤,更多详细内容请查看官方网站教程:Spine快速入门教程

1)在SETUP模式下,选中Images属性,导入所需图片资源所在文件夹,其中路径名和资源名中不能出现中文,否则解析不了;
2)拖动Images下的图片到场景,对角色进行组装(把各个身体部位拼在一起),可通过Draw Order属性调整图片所在层的顺序;
3)创建骨骼,并绑定图片到骨骼上,要注意各骨骼的父子关系。
4)切换到ANIMATE模式,选中要“动”的骨骼,对其进行旋转、移动、缩放等操作,每次改动后要记得打关键帧。
5)在菜单栏找到Texture Packer项,对角色纹理进行打包,资源文件后缀为atlas(而非Cocos2d-x常用的plist)。打包后将生成两个文件,即:png 和 atlas。

6)导出动画文件Json。

Spine动画的使用

Cocos2d-x程序中,使用Spine动画首先需要包含spine的相关头文件。

include 
include "spine/spine.h"
using namespace spine;

其常用方法如下:

创建一个Spine动画对象,将动画文件和资源文件导入。

auto skeletonNode = new SkeletonAnimation("enemy.json", "enemy.atlas");

骨骼动画往往是不止一个动画的,例如:当人物需要行走时,就设置播放动画为行走;当要发动攻击时,就设置播放动画为攻击。下面方法可以设置当前播放动画,其中参数false表示不循环播放,true表示循环播放。

skeletonNode->setAnimation(0, "walk", true);

setAnimation方法只能播放一种动画,所以当要连续播放不同的动画时,需要使用addAnimation方法来实现,它可以一条一条的播放不同的动画。

skeletonNode->addAnimation(0, "walk", true);
skeletonNode->addAnimation(0, "attack", false);

对于一般情况下,动画的切换要求两个动画完全能衔接上,不然会出现跳跃感,这个对于美术来说要求很高,而Spine加了个动画混合的功能来解决这个问题。使得不要求两个动画能完全的衔接上,比如上面的walk和attack动画, 就是衔接不上的,直接按上面的办法播放,会出现跳跃,但是加了混合后,看起来就很自然了。哪怕放慢10倍速度观察,也完美无缺。这个功能在序列帧动画时是无法实现的,也是最体现Spine价值的一个功能。

skeletonNode->setMix("walk", "attack", 0.2f);
skeletonNode->setMix("attack", "walk", 0.4f);

设置动画的播放快慢可通过设置它的timeScale值来实现。

skeletonNode->timeScale = 0.6f;

设置是否显示骨骼通过设置debugBones,true表示显示,false表示隐藏。

skeletonNode->debugBones = true;

例子:创建一个player行走和攻击的动画, 并且循环播放。

auto skeletonNode = new SkeletonAnimation("enemy.json", "enemy.atlas");
skeletonNode->setMix("walk", "attack", 0.2f);
skeletonNode->setMix("attack", "walk", 0.4f);

skeletonNode->setAnimation(0, "walk", false);
skeletonNode->setAnimation(0, "attact", false);
skeletonNode->addAnimation(0, "walk", false);
skeletonNode->addAnimation(0, "attact", true);

skeletonNode->debugBones = true;

Size windowSize = Director::getInstance()->getWinSize();
skeletonNode->setPosition(Point(windowSize.width / 2, windowSize.height / 2));
addChild(skeletonNode);

效果图:

Unity 4.3 2D 教程: 滚动,场景和音效

原文地址:http://www.raywenderlich.com/71029/unity-4-3-2d-tutorial-scrolling-scenes-and-sounds 泰然翻译组:RusHibaM。校对:lareina。

欢迎回到我们的 Unity 4.3 2D 系列教程。

的确,Unity 4.5在不久前已经发布了。但是本系列主要涉及的是Unity的2D特效,而这些特效正是在Unity 4.3版本中才被首次引入的。在4.5版本中,我们修复了一些bug并稍微修改了一些GUI元素。所以,如果你正在使用一个更新版本的引擎你应该时刻留意这一点,并且你应该注意你自己的编辑器与这个网页中的截图之间的细微差别。

通过这个系列教程的第一、二、三部分,你已经大致学会了使用Unity 2D工具所需要的相关知识,包括如何引入和初始化你的精灵。

在这个系列教程的第四部分,我们介绍了Unity 2D的物理引擎并且你还学会了一个处理不同屏幕尺寸和不同屏幕比例的方法。

阅读全文»

Unity 4.3 2D 教程: 动画

原文地址:http://www.raywenderlich.com/66345/unity-2d-tutorial-animations 泰然翻译组:Lyubishchev。校对:gloryming, lareina。

欢迎回到我们的Unity 4.3 2D系列教程!

系列的第一部分中,我们已经开始制作一个叫做Zombie Conga的好玩的的游戏。你已经学会如何添加精灵,如何使用精灵表,配置游戏视图以及用脚本移动精灵和做精灵动画。

在这个系列第二部分,你将会使僵尸再次动起来(他毕竟还没死),这次是使用Unity的内置的动画系统. 你也将会给猫精灵添加一些动画。

阅读全文»

如何用Unity 2D做一个类似于疯狂喷气机(Jetpack Joyride)的游戏 - 第一部分

原文地址:http://www.raywenderlich.com/69392/make-game-like-jetpack-joyride-unity-2d-part-1 泰然翻译组:大熊。校对:gloryming。

Unity 4.3发布后,开发者不再需要使用第三方库或者创建自己的解决方案来构建2D游戏了。现在可以使用一个即视即用的2D工具集来进行开发。结合这些标准统一的工具,开发Unity2D游戏不再是一个痛苦的过程,变成一个富有乐趣的工作了:]

在这个教程中,你将要做一些同样有趣的事。这个游戏没有用疯狂的或者乱飞舞翅膀的小鸟作为角色,而是一个穿喷气背包的小飞鼠。你能猜到这是什么样的游戏么(提示:本教程的标题)?

对,就是疯狂喷气机(Jetpack Joyride).此处来点掌声。

flying mouse

阅读全文»

如何使用Unity制作2.5D游戏(第2部分)

原文出自:http://www.raywenderlich.com/4595/how-to-make-a-2-5d-game-with-unity-tutorial-part-2, 由游戏邦翻译, 转载请注明来自游戏邦

这是关于使用Unity开发一款简单的2.5D游戏教程的第二部分内容。

第一部分教程中我们解析了Unity的使用基础并基于C#语言编写了脚本。我们创造了一款简单的游戏,即你控制着一架能够来回穿梭的飞机并向鲨鱼投射炸弹,以此保护小丑鱼。

在本篇教程的这最后一部分中我们将进一步扩展游戏并完成最后的润色。即我们将添加一些音效和音乐,整合游戏逻辑,并添加更多不同的游戏场景。

阅读全文»

如何使用Unity制作2.5D游戏(第1部分)

原文出自:http://www.raywenderlich.com/4551/how-to-make-a-2-5d-game-with-unity-tutorial-part-1, 由游戏邦翻译, 转载请注明来自游戏邦

2.5D游戏从根本上来说是3D游戏,但是经过开发者的处理后,游戏玩法只停留在2D轴上。(请点击此处查看第2部分

《超级玛丽兄弟Wii》、《小小大星球》和《Paper Monsters》都是绝妙的2.5D游戏。

制作2.5D游戏(3D游戏也可以使用)的绝妙方式是使用流行、简单和廉价的游戏开发工具Unity。

所以,在本系列教程中,我将向你展示如何使用Unity来制作简单的2.5D游戏《Shark Bomber!》。

如果你是个Unity新手,而且想要学习这种工具,那么本教程正适合你!你会学到如何从头开始制作出简单的游戏,并在此过程中学到许多东西。

在这款游戏中,你控制的是架小型但装备精良的飞机,你的目标是向邪恶的鲨鱼投炸弹,同时还要保护可爱的小丑鱼。

Unity不使用Objective-C,所以阅读此教程不需要任何Objective-C经验。但是,拥有OO语言经验会使学习更加顺畅,比如C#、Java或Actionscript。

要记住,这是个针对Mac用户的教程,Windows用户可能无法使用。而且还要记住,你只能在iOS设备(不是模拟器)上进行测试,所以要先确保拥有可使用的设备!

阅读全文»

[Cocos2d-x v3.x官方文档]如何使用WebSocket


ZeroYang原创,首发于泰然,转载请注明出处
https://github.com/chukong/cocos-docs/blob/master/manual/framework/native/v3/websocket/zh.md
欢迎大家斧正错误,提交PR。

Cocos2d-x官方中文文档 v3.x

介绍

WebSocket是HTML5开始提供的一种浏览器与服务器间进行全双工通讯的网络技术。在WebSocket API中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。

Cocos2d-x引擎集成libwebsockets,并在libwebsockets的客户端API基础上封装了一层易用的接口,使得引擎在C++, JS, Lua层都能方便的使用WebSocket来进行游戏网络通讯。

引擎支持最新的WebSocket Version 13。

在C++中使用

详细代码可参考引擎目录下的/samples/Cpp/TestCpp/Classes/ExtensionsTest/NetworkTest/WebSocketTest.cpp文件。

头文件中的准备工作

首先需要include WebSocket的头文件。

#include "network/WebSocket.h"

cocos2d::network::WebSocket::Delegate定义了使用WebScocket需要监听的回调通知接口。使用WebSocket的类,需要public继承这个Delegate。

class WebSocketTestLayer
: public cocos2d::Layer
, public cocos2d::network::WebSocket::Delegate

并Override下面的4个接口:

virtual void onOpen(cocos2d::network::WebSocket* ws);
virtual void onMessage(cocos2d::network::WebSocket* ws, const cocos2d::network::WebSocket::Data& data);
virtual void onClose(cocos2d::network::WebSocket* ws);
virtual void onError(cocos2d::network::WebSocket* ws, const cocos2d::network::WebSocket::ErrorCode& error);

后面我们再详细介绍每个回调接口的含义。

新建WebSocket并初始化

WebSocket.org 提供了一个专门用来测试WebSocket的服务器"ws://echo.websocket.org"。
测试代码以链接这个服务器为例,展示如何在Cocos2d-x中使用WebSocket。

新建一个WebSocket:

cocos2d::network::WebSocket* _wsiSendText = new network::WebSocket();

init第一个参数是delegate,设置为this,第二个参数是服务器地址。
URL中的"ws://"标识是WebSocket协议,加密的WebSocket为"wss://".

_wsiSendText->init(*this, "ws://echo.websocket.org")

WebSocket消息监听

在调用send发送消息之前,先来看下4个消息回调。

onOpen

init会触发WebSocket链接服务器,如果成功,WebSocket就会调用onOpen,告诉调用者,客户端到服务器的通讯链路已经成功建立,可以收发消息了。

void WebSocketTestLayer::onOpen(network::WebSocket* ws)
{
    if (ws == _wsiSendText)
    {
        _sendTextStatus->setString("Send Text WS was opened.");
    }
}

onMessage

network::WebSocket::Data对象存储客户端接收到的数据,
isBinary属性用来判断数据是二进制还是文本,len说明数据长度,bytes指向数据。

void WebSocketTestLayer::onMessage(network::WebSocket* ws, const network::WebSocket::Data& data)
{
    if (!data.isBinary)
    {
        _sendTextTimes++;
        char times[100] = {0};
        sprintf(times, "%d", _sendTextTimes);
        std::string textStr = std::string("response text msg: ")+data.bytes+", "+times;
        log("%s", textStr.c_str());

        _sendTextStatus->setString(textStr.c_str());
    }
}

onClose

不管是服务器主动还是被动关闭了WebSocket,客户端将收到这个请求后,需要释放WebSocket内存,并养成良好的习惯:置空指针。

void WebSocketTestLayer::onClose(network::WebSocket* ws)
{
    if (ws == _wsiSendText)
    {
        _wsiSendText = NULL;

    }
    CC_SAFE_DELETE(ws);
}

onError

客户端发送的请求,如果发生错误,就会收到onError消息,游戏针对不同的错误码,做出相应的处理。

void WebSocketTestLayer::onError(network::WebSocket* ws, const network::WebSocket::ErrorCode& error)
{
    log("Error was fired, error code: %d", error);
    if (ws == _wsiSendText)
    {
        char buf[100] = {0};
        sprintf(buf, "an error was fired, code: %d", error);
        _sendTextStatus->setString(buf);
    }
}

send消息到服务器

在init之后,我们就可以调用send接口,往服务器发送数据请求。send有文本和二进制两中模式。

发送文本

_wsiSendText->send("Hello WebSocket, I'm a text message.");

发送二进制数据(多了一个len参数)

_wsiSendBinary->send((unsigned char*)buf, sizeof(buf));

主动关闭WebSocket

这是让整个流程变得完整的关键步骤, 当某个WebSocket的通讯不再使用的时候,我们必须手动关闭这个WebSocket与服务器的连接。close会触发onClose消息,而后onClose里面,我们释放内存。

_wsiSendText->close();

在Lua中使用

详细代码可参考引擎目录下的/samples/Lua/TestLua/Resources/luaScript/ExtensionTest/WebProxyTest.lua文件。

创建WebSocket对象

脚本接口相对C++要简单很多,没有头文件,创建WebSocket对象使用下面的一行代码搞定。
参数是服务器地址。

wsSendText = WebSocket:create("ws://echo.websocket.org")

定义并注册消息回调函数

回调函数是普通的Lua function,4个消息回调和c++的用途一致,参考上面的说明。

local function wsSendTextOpen(strData)
    sendTextStatus:setString("Send Text WS was opened.")
end

local function wsSendTextMessage(strData)
    receiveTextTimes= receiveTextTimes + 1
    local strInfo= "response text msg: "..strData..", "..receiveTextTimes    
    sendTextStatus:setString(strInfo)
end

local function wsSendTextClose(strData)
    print("_wsiSendText websocket instance closed.")
    sendTextStatus = nil
    wsSendText = nil
end

local function wsSendTextError(strData)
    print("sendText Error was fired")
end

Lua的消息注册不同于C++的继承 & Override,有单独的接口registerScriptHandler。
registerScriptHandler第一个参数是回调函数名,第二个参数是回调类型。
每一个WebSocket实例都需要绑定一次。

if nil ~= wsSendText then
    wsSendText:registerScriptHandler(wsSendTextOpen,cc.WEBSOCKET_OPEN)
    wsSendText:registerScriptHandler(wsSendTextMessage,cc.WEBSOCKET_MESSAGE)
    wsSendText:registerScriptHandler(wsSendTextClose,cc.WEBSOCKET_CLOSE)
    wsSendText:registerScriptHandler(wsSendTextError,cc.WEBSOCKET_ERROR)
end

send消息

Lua中发送不区分文本或二进制模式,均使用下面的接口。

wsSendText:sendString("Hello WebSocket中文, I'm a text message.")

主动关闭WebSocket

当某个WebSocket的通讯不再使用的时候,我们必须手动关闭这个WebSocket与服务器的连接,以释放服务器和客户端的资源。close会触发cc.WEBSOCKET_CLOSE消息。

wsSendText:close()

在JSB中使用

详细代码可参考引擎目录下的/samples/Javascript/Shared/tests/ExtensionsTest/NetworkTest/WebSocketTest.js文件。

创建WebSocket对象

脚本接口相对C++要简单很多,没有头文件,创建WebSocket对象使用下面的一行代码搞定。
参数是服务器地址。

this._wsiSendText = new WebSocket("ws://echo.websocket.org");

设置消息回调函数

JSB中的回调函数是WebSocket实例的属性,使用匿名函数直接赋值给对应属性。可以看出JS语言的特性,让绑定回调函数更加优美。四个回调的含义,参考上面c++的描述。

this._wsiSendText.onopen = function(evt) {
    self._sendTextStatus.setString("Send Text WS was opened.");
};

this._wsiSendText.onmessage = function(evt) {
    self._sendTextTimes++;
    var textStr = "response text msg: "+evt.data+", "+self._sendTextTimes;
    cc.log(textStr);

    self._sendTextStatus.setString(textStr);
};

this._wsiSendText.onerror = function(evt) {
    cc.log("sendText Error was fired");
};

this._wsiSendText.onclose = function(evt) {
    cc.log("_wsiSendText websocket instance closed.");
    self._wsiSendText = null;
};

send消息

发送文本,无需转换,代码如下:

this._wsiSendText.send("Hello WebSocket中文, I'm a text message.");

发送二进制,测试代码中,使用_stringConvertToArray函数来转换string为二进制数据,模拟二进制的发送。
new Uint16Array创建一个16位无符号整数值的类型化数组,内容将初始化为0。然后,循环读取字符串的每一个字符的Unicode编码,并存入Uint16Array,最终得到一个二进制对象。

_stringConvertToArray:function (strData) {
    if (!strData)
        return null;

    var arrData = new Uint16Array(strData.length);
    for (var i = 0; i < strData.length; i++) {
        arrData[i] = strData.charCodeAt(i);
    }
    return arrData;
},

send二进制接口和send文本没有区别,区别在于传入的对象,JS内部自己知道对象是文本还是二进制数据,然后做不同的处理。

var buf = "Hello WebSocket中文,\0 I'm\0 a\0 binary\0 message\0.";
var binary = this._stringConvertToArray(buf);

this._wsiSendBinary.send(binary.buffer);

主动关闭WebSocket

当某个WebSocket的通讯不再使用的时候,我们必须手动关闭这个WebSocket与服务器的连接,以释放服务器和客户端的资源。close会触发onclose消息。

onExit: function() {
    if (this._wsiSendText)
        this._wsiSendText.close();
},

[Cocos2d-x v3.x官方文档]cocos2d::Vector<T>


ZeroYang原创,首发于泰然,转载请注明出处
https://github.com/chukong/cocos-docs/blob/master/manual/framework/native/v3/data-structure/vector/zh.md
欢迎大家斧正错误,提交PR。

Cocos2d-x官方中文文档 v3.x
  • v3.0加入

定义在"COCOS2DX_ROOT/cocos/base"的"CCVector.h"头文件中。


templateclass CC_DLL Vector;


cocos2d::Vector是一个封装好的能动态增长顺序访问的容器。

cocos2d::Vector中的元素是按序存取的,它的低层实现数据结构是标准模版库中的标准顺序容器std::vector。

在cocos2d-x v3.0 beta之前,使用的是另外一个顺序访问容器cocos2d::CCArray,不过它将会被废弃。

设计者们将cocos2d::Vector设计为cocos2d::CCArray的替代品,所以建议优先考虑使用cocos2d::Vector

cocos2d::Vector的一些操作的时间复杂度如下:

  • 随机访问,O(1)
  • 将元素插入到尾部或者删除尾部的元素,O(1)
  • 随机插入或删除, O(n)

模版参数

T - 元素类型

  • T的类型必须是继承自cocos2d::Ref类型的指针。因为已经将cocos2d-x的内存管理模型集成到了cocos2d::Vector中,所以类型参数不能是其他的类型包括基本类型。

内存管理

cocos2d::Vector类只包含一个成员数据:

std::vector _data;

_data的内存管理是由编译器自动处理的,如果声明了一个cocos2d::Vector类型,就不必费心去释放内存。

注意:使用现代的c++,本地存储对象比堆存储对象好。所以请不要用new操作来申请cocos2d::Vector的堆对象,请使用栈对象。

如果真心想动态分配堆cocos2d::Vector,请将原始指针用智能指针来覆盖。

警告:cocos2d::Vector并不是cocos2d::Ref的子类,所以不要像使用其他cocos2d类一样来用retain/release和引用计数内存管理。

基本用法

作者们用std::vector的基本操作加上cocos2d-x的内存管理规则来覆盖该模版原先的普通操作。

所以pushBack()操作将会保留传递过来的参数,而popBack()则会释放掉容器中最后的一个元素。

当你使用这些操作的时候,你需要特别注意这些受托管的对象,对于新手来说,这往往是陷阱。

警告:cocos2d::Vector并没有重载[]操作,所以不能直接用下标[i]来获取第i位元素。

cocos2d::Vector提供了不同类型的迭代器,所以我们可以受益于c++的标准函数库,我们可以使用大量标准泛型算法和for_each循环。

除了std::vector容器的操作之外,开发者们还加入许多标准算法诸如:std::find, std::reverse和std::swap,这些算法可以简化很多通用的操作。

要了解更多的api用例,可以参考cocos2d-x 3.0的源码和压缩包里附带的例子。

下面是一些简单的例子:

//create Vector with default size and add a sprite into it
auto sp0 = Sprite::create();
sp0->setTag(0);
//here we use shared_ptr just as a demo. in your code, please use stack object instead
std::shared_ptr>  vec0 = std::make_shared>();  //default constructor
vec0->pushBack(sp0);

//create a Vector with a capacity of 5 and add a sprite into it
auto sp1 = Sprite::create();
sp1->setTag(1);

//initialize a vector with a capacity
Vector  vec1(5);
//insert a certain object at a certain index
vec1.insert(0, sp1);

//we can also add a whole vector
vec1.pushBack(*vec0);

for(auto sp : vec1)
{
    log("sprite tag = %d", sp->getTag());
}

Vector vec2(*vec0);
if (vec0->equals(vec2)) { //returns true if the two vectors are equal
    log("pVec0 is equal to pVec2");
}
if (!vec1.empty()) {  //whether the Vector is empty
    //get the capacity and size of the Vector, noted that the capacity is not necessarily equal to the vector size.
    if (vec1.capacity() == vec1.size()) {
        log("pVec1->capacity()==pVec1->size()");
    }else{
        vec1.shrinkToFit();   //shrinks the vector so the memory footprint corresponds with the number of items
        log("pVec1->capacity()==%zd; pVec1->size()==%zd",vec1.capacity(),vec1.size());
    }
    //pVec1->swap(0, 1);  //swap two elements in Vector by their index
    vec1.swap(vec1.front(), vec1.back());  //swap two elements in Vector by their value
    if (vec2.contains(sp0)) {  //returns a Boolean value that indicates whether object is present in vector
        log("The index of sp0 in pVec2 is %zd",vec2.getIndex(sp0));
    }
    //remove the element from the Vector
    vec1.erase(vec1.find(sp0));
    //pVec1->erase(1);
    //pVec1->eraseObject(sp0,true);
    //pVec1->popBack();

vec1.clear(); //remove all elements log("The size of pVec1 is %zd",vec1.size()); }

输出:

Cocos2d: sprite tag = 1
Cocos2d: sprite tag = 0
Cocos2d: pVec0 is equal to pVec2
Cocos2d: pVec1->capacity()==2; pVec1->size()==2
Cocos2d: The index of sp0 in pVec2 is 0
Cocos2d: The size of pVec1 is 0

推荐做法

  • 考虑基于栈的cocos2d::Vector优先用于基于堆的
  • 当将cocos2d::Vector作为参数传递时,将它声明成常量引用:const cocos2d::Vector&
  • 返回值是cocos2d::Vector时,直接返回值,这种情况下编译器会优化成移动操作。
  • 不要用任何没有继承cocos2d::Ref的类型作为cocos2d::Vector的数据类型。

[Cocos2d-x v3.x官方文档]cocos2d::Value


ZeroYang原创,首发于泰然,转载请注明出处
https://github.com/chukong/cocos-docs/blob/master/manual/framework/native/v3/data-structure/value/zh.md
欢迎大家斧正错误,提交PR。

Cocos2d-x官方中文文档 v3.x
  • v3.0加入

定义在"COCOS2DX_ROOT/cocos/base"的头文件"CCValue.h"中

class Value;

cocos2d::Value是许多基本类型(int,float,double,bool,unsigned char,char*和std::string)还有std::vector, std::unordered_map和std::unordered_map这些类的包装类型。

你可以将上面提及的基本类放进cocos2d::Value对象将它们转换成对应的类型,反之亦然。

cocos2d::Value底层用一个统一的变量来保存任意基本类型值,它将更加节省内存。在cocos2d-x v3.0beta之前使用的是CCBool, CCFloat, CCDouble和CCInteger这样基本类型包装类,不过它们将被废弃掉。

注意:当处理基本类型和和容器时,请使用cocos2d::Vector, cocos2d::Map和cocos2d::Value。

内存管理

cocos2d::Value的内存由它的析构函数来释放,所以使用cocos2d::Value时请尽量用推荐的最佳做法。

cocos2d::Value包含下面的数据成员:

union
{
    unsigned char byteVal;
    int intVal;
    float floatVal;
    double doubleVal;
    bool boolVal;
}_baseData;

std::string _strData; ValueVector* _vectorData; ValueMap* _mapData; ValueMapIntKey* _intKeyMapData; Type _type;

代码段中,_baseData, _strData和_type是由编译器和它们的析构函数负责释放内存的,而cocos2d::Value的析构函数则负责释放指针成员(_vectorData, _mapData和intKeyMapData)。

注意:cocos2d::Value不能像其它cocos2d类型一样使用retain/release和refcount内存管理

基本用法

ocos2d::Value的用法非常简单直接。下面就是使用的例子:

Value val;   // call the default constructor
if (val.isNull()) {
    log("val is null");
}else{
    std::string str =val.getDescription();
    log("The description of val0:%s",str.c_str());
}
//----------------------------------------------------
Value val1(65);   // initialize with a integer
//Value val1(3.4f);   // initialize with a float value
//Value val1(3.5);   // initialize with a double value
log("The description of the integer value:%s",val1.getDescription().c_str());
log("val1.asByte() = %c",val1.asByte());
//----------------------------------------------------
std::string strV = "string";
Value val2(strV);   // initialize with string
log("The description of the string value:%s",val2.getDescription().c_str());
//----------------------------------------------------
auto sp0 = Sprite::create();
Vector* vecV = new Vector();
vecV->pushBack(sp0);
Value val3(vecV);   // initialize with Vector
log("The description of the Vector value:%s",val3.getDescription().c_str());
delete vecV;
//----------------------------------------------------
Map* mapV = new Map();
mapV->insert(strV,sp0);
Value val4(mapV);   // initialize with Map
log("The description of the Map value:%s",val4.getDescription().c_str());
delete mapV;
//----------------------------------------------------
Value val6(&val4);   // initialize with Map
log("The description of the Value-type value:%s",val6.getDescription().c_str());
//----------------------------------------------------
val2 = val1;   // assigning between 2 Value-type
log("operator-> The description of val2:%s",val2.getDescription().c_str());
val2 = 4;   //assigning directly
log("operator-> The description of val4:%s",val2.getDescription().c_str());

结果是:

cocos2d: val is null
cocos2d: The description of the integer value:
65

cocos2d: val1.asByte() = A
cocos2d: The description of the string value:
string

cocos2d: The description of the Vector value:
true

cocos2d: The description of the Map value:
true

cocos2d: The description of the Value-type value:
true

cocos2d: operator-> The description of val2:
65

cocos2d: operator-> The description of val4:
4

推荐用法

  • 考虑使用cocos2d::Value和新的模版容器(cocos2d::Vector和cocos2d::Map)优于使用cocos2d::CCBool, cocos2d::CCFloat,cocos2d::CCDouble,cocos2d::CCString,cocos2d::CCInteger和旧的objective-c风格的容器(cocos2d::CCArray和cocos2d::CCDictionary)。
  • 当要使用基本类型的聚合时,将基本类型包装成cocos2d::Value,然后将它们和模版容器cocos2d::Vector和cocos2d::Map联合使用。
?>