0%

游戏项目客户端开发备忘录(二) - Cocos2d-x 框架

列举了一些 Cocos2d-x 框架的技术备忘录,基于 Cocos2d-x v3.16 和 Lua 5.1.5。

框架架构

导演 (Director)

  • 单例类,负责创建并管理主窗口,场景切换,暂停恢复游戏运行,游戏主循环等。例如:
1
2
3
4
5
6
Director* director = Director::getInstance();
director->runWithScene(Scene::create());
director->pushScene(Scene::create());
director->popScene();
director->replaceScene(Scene::create());
Scene* scene = director->getRunningScene();
1
2
3
4
5
6
local director = cc.Director:getInstance()
director:runWithScene(cc.Scene:create())
director:pushScene(cc.Scene:create())
director:popScene()
director:replaceScene(cc.Scene:create())
local scene = director:getRunningScene()
  • 挂载一些全局变量,例如: TextureCacheSchedulerEventDispatcherRenderer 等。
1
2
3
4
5
Director* director = Director::getInstance();
TextureCache* textureCache = director->getTextureCache();
Scheduler* scheduler = director->getScheduler();
EventDispatcher* eventDispatcher = director->getEventDispatcher();
Renderer* renderer = director->getRenderer();
1
2
3
4
local director = cc.Director:getInstance()
local textureCache = director:getTextureCache()
local scheduler = director:getScheduler()
local eventDispatcher = director:getEventDispatcher()
  • 其他:
    • 设置 OpenGLView,投影模式,视窗大小等。
    • 设置缩放系数 contentScaleFactor
    • 设置帧率 FPS :setAnimationInterval(float interval)

场景 (Scene)

  • 场景中所有节点 Node 以树形结构表示,Scene 为根节点。
  • 过渡场景:TransitionScene
1
2
TransitionScene* scene = TransitionScene::create(1.0, Scene:create());
Director::getInstance()->replaceScene(scene);
1
2
local scene = cc.Scene:create()
cc.Director:getInstance():pushScene( cc.TransitionScene:create(1, scene) )

节点 (Node)

  • 场景图中的基础类。所有元素以此为基类。
  • 子节点以树形结构表示,存放在 Vector<Node*> _children;
  • 属性:位置 position,缩放 scale,旋转 rotation,锚点 anchorPoint,大小 contentSize,可见 visible 等。
  • 管理子节点。
1
2
3
4
5
6
7
Node* parent = Node:create();
parent->addChild(Node:create(), 1, "child1");
parent->addChild(Node:create(), 2, "child2");
Node* child1 = parent->getChildByName("child1");
parent->removeChild(child1, true);
Node* child2 = parent->getChildByName("child2");
child2->removeFromParent();
1
2
3
4
5
6
7
local parent = cc.Node:create()
parent:addChild(cc.Node:create(), 1, "child1")
parent:addChild(cc.Node:create(), 2, "child2")
local child1 = parent:getChildByName("child1")
parent:removeChild(child1, true)
local child2 = parent:getChildByName("child2")
child2:removeFromParent(true)
  • 运行动作 (Action)。
1
2
3
4
Node* node = Node:create();
Action* action = node->runAction(MoveTo::create(1.0, Vec2(100, 50)));
node->stopAction(action);
node->stopAllActions();
1
2
3
4
local node = cc.Node:create()
local action = node:runAction(cc.MoveTo:create(1.0, cc.p(100, 50)))
node:stopAction(action);
node:stopAllActions();
  • 定时器。
1
2
3
4
5
6
7
8
9
10
Node* node = Node:create();
node->schedule(CC_SCHEDULE_SELECTOR(Node::update), 1.0);
node->unschedule(CC_SCHEDULE_SELECTOR(Node::update));
node->schedule(CC_CALLBACK_1(Node::update, node), 1.0, "update1");
node->unschedule("update1");
node->schedule([](float dt) {
// do something
}, 1.0, "update2");
node->unschedule("update2");
node->unscheduleAllCallbacks();
  • 定时器未在 Lua 中绑定,可以使用 Scheduler:scheduleScriptFunc 方法。
1
2
3
4
5
local scheduler = cc.Director:getInstance():getScheduler()
local scheduleScriptEntryID = scheduler:scheduleScriptFunc(function (dt)
-- do something
end, 1.0)
scheduler:unscheduleScriptEntry(scheduleScriptEntryID)
  • 也可以用 RepeatForever 来代替。
1
2
3
4
5
6
7
8
9
function Node:schedule(callback, interval)
local seq = cc.sequence:create({
cc.DelayTime:create(interval),
cc.CallFunc:create(callback),
})
local action = cc.RepeatForever:create(seq)
self:runAction(action)
return action
end

精灵 (Sprite)

  • 渲染 2d 图片。
  • 使用文件和 spriteFrame 创建图片。
1
2
3
4
5
6
Sprite* sprite1 = Sprite::create("example.png");
Sprite* sprite2 = Sprite::createWithSpriteFrameName("frame.png");

SpriteFrameCache* cache = SpriteFrameCache::getInstance();
SpriteFrame* frame = cache->getSpriteFrameByName("frame.png");
Sprite* sprite3 = Sprite::createWithSpriteFrame(frame);
1
2
3
4
5
6
local sprite1 = cc.Sprite:create("example.png")
local sprite2 = cc.Sprite:createWithSpriteFrameName("frame.png")

local cache = cc.SpriteFrameCache:getInstance()
local frame = cache:getSpriteFrameByName("frame.png")
local sprite3 = cc.Sprite:createWithSpriteFrame(frame)
  • 设置纹理 texturespriteFrame、翻转 flipXflipY 等。
  • 常见优化方式:
    • 将图片打包在同一图集 spritesheet
    • 使用同样的混合方法 blend function
    • 渲染器会自动进行批量渲染,在同一个 OpenGL call 中进行。

动作 (Action)

ActionInterval

  • 常用:MoveByMoveToRotateByRotateToFadeToFadeInFadeOut 等。
  • 序列 Sequence 和同步 Spawn
1
2
3
4
5
6
7
8
9
10
Sequence* seq = Sequence::create(MoveTo::create(1.0, Vec2(0, 0)),
FadeTo::create(1.0, 0),
NULL);
Node* node = Node::create();
node->runAction(seq);

Spawn* spawn = Spawn::create(Vector<FiniteTimeAction*>{
MoveTo::create(1.0, Vec2(0, 0)),
FadeTo::create(1.0, 0)});
node->runAction(spawn);
1
2
3
4
5
6
7
8
9
10
11
12
13
local seq = cc.Sequence:create(
cc.MoveTo:create(1.0, cc.p(0, 0)),
cc.FadeTo:create(1.0, 0)
)
local node = cc.Node:create()
node:runAction(seq)

local spawn = cc.Spawn:create(
{
cc.MoveTo:create(1.0, cc.p(0, 0)),
cc.FadeTo:create(1.0, 0)
})
node:runAction(spawn)
  • 重复:RepeatRepeatForever
1
2
3
4
5
6
7
8
9
10
Sequence* seq = Sequence::create(
MoveBy::create(0.5, Vec2(100, 0)),
MoveBy::create(0.5, Vec2(-100, 0)),
NULL);
Repeat* repeat = Repeat::create(seq, 3);
Node* node = Node::create();
node->runAction(repeat);

RepeatForever* repeatForever = RepeatForever::create(seq->clone());
node->runAction(repeatForever);
1
2
3
4
5
6
7
8
9
10
11
local seq = cc.Sequence:create(
{
cc.MoveTo:create(0.5, cc.p(100, 0)),
cc.MoveTo:create(0.5, cc.p(-100, 0)),
})
local actionRepeat = cc.Repeat:create(seq, 3)
local node = cc.Node:create()
node:runAction(actionRepeat)

local repeatForever = cc.RepeatForever:create(seq:clone())
node:runAction(repeatForever)

ActionInstant

  • 常用:ShowHideRemoveSelf等。
  • 回调动作:CallFuncCallFuncN
1
2
3
4
5
6
7
8
9
10
CallFunc* callFunc = CallFunc::create([]{
// callback
});
Node* node = Node::create();
node->runAction(callFunc);

CallFuncN* callFuncN = CallFuncN::create([](Node* n){
// callback
});
node->runAction(CallFuncN);
  • Lua 中使用了 LuaCallFunc 实现 CallFunc
1
2
3
4
5
local callFunc = cc.CallFunc:create(function (node)
-- callback
end)
local node = cc.Node:create()
node:runAction(callFunc)

其他

  • 节点动作存储在 ActionManager 中,在每帧 Scheduler::update() 时遍历更新所有节点的所有动作。

动画

帧动画 (Animate)

  • 通过每隔一个短暂时间进行图像替代。
  • Animate 是动作,Animation 是存储了一组 SpriteFrame 的对象。
1
2
3
4
5
6
7
8
9
10
Animation* animation = Animation::create();
for (int i = 0; i < 10; ++i) {
char tmp[50];
sprintf(tmp, "frame_%02d.png", i);
std::string file = tmp;
animation->addSpriteFrameWithFile(file);
}
Animate* animate = Animate::create(animation);
Sprite* sprite = Sprite::create("frame_01.png");
sprite->runAction(animate);
1
2
3
4
5
6
7
local animation = cc.Animation:create()
for i = 1, 10 do
animation:addSpriteFrameWithFile(string.format("frame_%02d.png", i))
end
local animate = cc.Animate:create(animation)
local sprite = cc.Sprite:create("frame_01.png")
sprite:runAction(animate)

龙骨动画 (DragonBone)

1
2
3
4
5
6
7
dragonBones::DBCCFactory* factory = dragonBones::DBCCFactory::getInstance();
factory->loadTextureAtlas("texture.xml");
factory->loadDragonBonesData("skeleton.xml");
dragonBones::DBCCArmatureNode* armatureNode = factory->buildArmatureNode("armature");
dragonBones::Animation* animation = armatureNode->getAnimation();
animation->gotoAndPlay("animation1");
animation->gotoAndStop("animation1", 0);
  • 比起帧动画更能节省资源,动画也可以做的精细自然。

Spine 动画

1
2
spine::SkeletonAnimation* skeletonAnimation = spine::SkeletonAnimation::createWithFile("spine.json", "spine.atlas");
skeletonAnimation->setAnimation(0, "actionName", true);

粒子特效(Particle)

  • 使用示例:
1
ParticleSystem* particle = ParticleSystem::create("particle.plist");
1
local particle = cc.ParticleSystem:create("particle.plist")

事件

  • 触摸事件、键盘事件、鼠标事件、加速度传感器事件和自定义事件等。
  • 优先级:设置优先级,层级优先级。
  • 使用示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
EventListenerTouchOneByOne* touchListener = EventListenerTouchOneByOne::create();
touchListener->setSwallowTouches(true);
touchListener->onTouchBegan = [&](Touch* touch, Event* event){
return true;
};
touchListener->onTouchEnded = [&](Touch* touch, Event* event){

};
EventDispatcher* eventDispatcher = Director::getInstance()->getEventDispatcher();
eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this);

EventCustom event("eventName");
eventDispatcher->dispatchEvent(&event);
1
2
3
4
5
6
7
8
9
10
11
12
13
local touchListener = cc.EventListenerTouchOneByOne:create()
touchListener:setSwallowTouches(true)
touchListener:registerScriptHandler(function (event, eventTouch)
return true
end, cc.Handler.EVENT_TOUCH_BEGAN)
touchListener:registerScriptHandler(function (event, eventTouch)
return true
end, cc.Handler.EVENT_TOUCH_ENDED)
local eventDispatcher = cc.Director:getInstance():getEventDispatcher()
eventDispatcher:addEventListenerWithSceneGraphPriority(touchListener, self)

local event = cc.EventCustom:new("eventName")
eventDispatcher:dispatchEvent(event)

文件 IO

  • FileUtils 类。
  • 使用示例:
1
2
3
4
FileUtils* fileUtils = FileUtils::getInstance();
if (fileUtils->isFileExist("example.txt")) {
string str = fileUtils->getStringFromFile("example.txt");
}
1
2
3
4
local fileUtils = cc.FileUtils:getInstance()
if fileUtils:isFileExist("example.txt") then
local str = fileUtils:getStringFromFile("example.txt")
end
  • Lua 也用 io 模块读写文件
1
2
3
4
5
local file = io.open("example.txt", "r")
if file then
local content = file:read("*a")
io.close(file)
end

参考链接