quick-cocos2dx 组件管系统初探

第一次遇见组件系统,我想大家基本都是跟着教程学习中提到的状态机吧!

cc.GameObject.extend(self.fsm)
	:addComponent("components.behavior.StateMachine")
	:exportMethods()

作者刚看到这种使用方式,觉得很困惑,今天终于去走了一遍源码。

在此先感谢这个大神写的文章:quick-cocos2dx 组件管理器

接触过unity3D的都知道,unity里的对象,都是由一个空的gameObject附加上相应的组件构成的。quick-lua里也有这种功能。它是由Registry.lua,GameObject.lua和Component.lua协调完成的。如果想使用这套机制,只需要调一个函数:GameObject.extend(obj),这样obj就能附加组件了。如果相用自定义组件,则要自定义Component的派生类。

下面的代码注释,以添加状态机组件为例,假设target为精灵,component为状态机。这样描述相对形象点,没有那么抽象,方便理解。

GameObject:负责为target对象拓展功能,使其能够附加各种组件

function GameObject.extend(target)
    target.components_ = {}	--精灵添加一个组件集合表

    function target:checkComponent(name)
        return self.components_[name] ~= nil
    end

    function target:addComponent(name)
        local component = Registry.newObject(name)  --创建一个状态机组件
        self.components_[name] = component	    --精灵的组件表保存状态机组件
        component:bind_(self)                       --将状态机绑到精灵上 
        return component
    end

    function target:removeComponent(name)
        local component = self.components_[name]
        if component then component:unbind_() end
        self.components_[name] = nil
    end

    function target:getComponent(name)
        return self.components_[name]
    end

    return target
end

Registry:此类用于管理所有组件类。相当于一张注册表,所有的组件都要在这张表中注册。当然

不用去显示地注册,newObject会根据组件的名字,自动加载并注册组件

function Registry.newObject(name, ...)
    local cls = Registry.classes_[name]
    if not cls then
        -- auto load
        pcall(function()
            cls = require(name)	        --加载状态机模块
            Registry.add(cls, name)	--对状态机组件进行保存记录
        end)
    end
    return cls.new(...)	--创建一个状态机对象
end
function Registry.add(cls, name)
    if not name then name = cls.__cname end
    Registry.classes_[name] = cls
end

Component:组件基类,所有组件都要派生自它

-- 构造函数,
-- name为组件名称
-- depends为该组件需要依赖于哪些组件,
-- 当精灵附加状态机时,会自动为且附加所有的depends组件  
function Component:ctor(name, depends)
    self.name_ = name
    self.depends_ = checktable(depends)
end

-- 将状态机绑到精灵上 
function Component:bind_(target)
    self.target_ = target
    for _, name in ipairs(self.depends_) do
        if not target:checkComponent(name) then
            target:addComponent(name)
        end
    end
    self:onBind_(target)
end

我挖到这里,对这个depends很有兴趣,然后输出了下,结果发现目前depends目前为nil,应该是为了后续的扩展做好准备,所以就没再纠结下去了!

-- 将该组件某些的方法导出到target上
-- methods为字符串数组,字符串名字就是函数名。
-- 有了这套机制,在调用addcomponent后接着调用此函数,
-- 则以后想使用该组件的功能,直接通过target就能调用,无需先获取组件,再调用函数 
function Component:exportMethods_(methods)
    self.exportedMethods_ = methods    
    local target = self.target_
    local com = self
    for _, key in ipairs(methods) do    -- 遍历函数名集合
        if not target[key] then         -- 如果精灵对象有了该函数,就不精灵进行添加
            local m = com[key]
            target[key] = function(__, ...)
                return m(com, ...)
            end
        end
    end
    return self
end

这个exportMethods_()函数是我之前一直不明白的,现在我解释下。函数中的self是状态机,还记得在addComponent函数里有一句component:bind_(self)吗?在bind_()函数里self.target_ = target这句就把状态机绑定到了精灵上。

在exportMethods_()里,target = self.target_这句就获取到了精灵对象,com = self之后,com就为状态机。在遍历判断,说明精灵对象可以覆盖掉状态机里函数(不是真正意义上的覆盖,只是无法直接调用到状态机的,可以通过getComponent()函数获取状态机,然后调用)。

target[key] = function(__, ...)
    return m(com, ...)
end

以上代码就把状态机里的函数导出到了精灵上,以后使用状态机的函数,就不要获取状态机了,可以直接调用。大家可能对m(com, ...)这句有点遗憾,这是lua的语法。如果在函数内用到self,那函数参数要传入调用函数的对象实例。这里m是类成员函数,所以需要传入对象实例,因为函数里没用到self传入self也不影响。大家可以试验下,以下代码把函数前2句注释和不注释看下效果就明白了!

a = {}
function a:say()
	self.x = 5
	print(self.x)
	print("Hello")
end

b = a["say"]
b(a)
b()

有时候大家可能看到以下代码:

cc(self):addComponent("components.behavior.StateMachine")
	:exportMethods()

这个效果一样的,因为quick把在初始化的时候 cc = cc.GameObject.extend()。

暂时就这么多了,以后如果对组件还有深入研究会来补充的!

如有不足之处,请大家指出,一起交流成长!

时间: 2024-08-02 15:44:30

quick-cocos2dx 组件管系统初探的相关文章

Mac下搭建quick cocos2d-x编译环境

前言 虽然之前已经写过了很多 Cocos2d-x 相关的教程和文档,但本次却是我第一次接触 Quick,第一次接触 Lua,所以此次的教程本人将站在一个初学者的角度(看到这里是不是想白眼我了,哈哈,别切啊!尽管第一次,但我身边可是有很多 Quick 大神的,廖大大也在旁边办公室,没准撒个娇大神就把他知道的全部要点倾囊相授了啦!),全方位的解析 Quick 的学习过程,并同大家一起学习如何利用 Quick-Cocos2d-x 开发一款属于自己的游戏,包教包会的哦. 好了,那么下面我们就开始进入正题

Quick Cocos2dx 版本更新

呵呵,不出所料,我又把项目的quick x版本从2.2.1升级到2.2.3了,不知道下次升级到3.x回事神马时候呢,好期待的说. 话说运行2.2.3的player的时候,老是提醒我显卡不支持opengl 1.5,说我的显卡才支持到opengl 1.1. 用各种工具查看,老子显卡支持opengl4.4, opencl 1.1. 然后就反反复复的更新显卡驱动,整了两个晚上都没有结果. 后来,到论坛求助,有坛友说是显卡驱动没弄好,windows7的透明框都没有. 最后,彻底卸载了显卡驱动,用驱动精灵给

Quick Cocos2dx 调试问题

最近由于忙了一段时间,忙完了之后又迷茫了这么久,然后终于开始继续Quick-x的学习之路了. 然后遇到了一个比较棘手的问题. 虽然照着官方mvc的例子敲代码,但是还是不停的报错,报错的问题下次集结成一个帖子发出来好了. 今次记录的是对于错误的DEUG方面的问题. 我的首选当然是Eclipse + LDT了,因为从业是自java而始,继而是AS,用得最熟的莫过与FB了. 参考的是官方的文章: 用 Eclipse LDT 调试 quick-cocos2d-x 游戏 但是,进行到配置player的时候

quick cocos2d-x 精灵大小(宽高)获取

quick下sprite的大小获取,记录一下: local w = sprite:getContentSize().width local h = sprite:getContentSize().height 今天连这个最基本的,都不知道怎么获取.挺实用的代码额~ quick cocos2d-x 精灵大小(宽高)获取,布布扣,bubuko.com

quick cocos2dx 判断坐标是否在精灵内部

local pos = ccp(10, 10)local sprite=display.newSprite("02.png")    //创建一个精灵 sprite:setPosition(ccp(10,10))self:addChild(sprite) if sprite:getBoundingBox():containsPoint(pos) then print("在内部") end quick cocos2dx 判断坐标是否在精灵内部,布布扣,bubuko.c

quick cocos2dx 3.x 配置win32工程

公司项目主体部分用c++,而ui部分用lua写,所以选择了用quick框架.项目先开发了ios/mac版,这两天试着配置其win32工程,遇到一些问题,记录一下(纯c++版本cocos2dx配置方法应该也是类似的). 先配debug模式: 把c++文件都添到工程中去,并在附加包含目录下配置c++文件的搜索路径.然后编译会遇到一些问题: 一,win32下fullPathForFilename函数与ios/mac下行为不一致的问题. fullPathForFilename当传入的参数是文件夹路径时,

quick cocos2dx 滚动条

--滚动条 labar local function valueChanged(strEventName,pSender) if nil == pSender then return end local pControl = tolua.cast(pSender,"CCControlSlider") local strFmt = nil if pControl:getTag() == 10 then print(pControl:getValue()) elseif pControl:

Quick Cocos2dx 场景对象基类实现

从使用Quick-Cocos2d-x搭建一个横版过关游戏(四)拷来个进度条类, 但是由于那个类有个bug,在setProgress里面self.fill是找不到的,所以我改进了一下,代码如下: 1 local Progress = class("Progress", function() 2 -- body 3 return display.newNode() 4 end) 5 6 function Progress:ctor(background, fill) 7 local pro

Quick Cocos2dx 与 DragonBones

照着官方的例子试验了一下DragonBone的使用,代码如下: 1 local AnotherScene = class("AnotherScene", function() 2 return display.newScene("AnotherScene") 3 end) 4 5 function AnotherScene:ctor() 6 self.curBehaviorId = 1; 7 self.layer = display.newLayer() 8 sel