首页 > 代码库 > cocos2d-x开发: 场景实体(entity)管理

cocos2d-x开发: 场景实体(entity)管理

公司现在开新项目,主题的框架部分都是我自己在做,不用受到别人的牵制,所以还算是比较的自由,很好发挥. 游戏并不大,所以需要用到的地方并不多.今天花了一些时间写了场景entity管理的部分代码,还没有完全的完善.

我的思路是这样的, entity manager提供注册一个update( dt )的帧频回调事件, 在每一次回调的时候都会遍历管理的所有的entity,调用entity的update( dt )帧频回调.何为帧频回调?我解释一下,cocos2d-x在c++那边是可以重写onDraw()方法实现的.在lua这边的话,如果是从cocos2d::node派生的子类,也就是使用 node = class( "node", function() return cc.Node:create() end),然后也可以注册schedulexxxx系列的方法,具体的可以去参考源码.由于我的管理类和entity都不是node派生的,所以我在提供了管理类的update( dt )回调,通过cc.Director:getInstance():getScheduler(),然后调用scheduler:scheduleScripteFunc( function(dt) end,0,false )方式实现的.

管理类的源码如下:

 1 local entity_manager = class( "entity_manager", nil ) 2  3 function entity_manager:ctor() 4     self.entity_list_ = {} 5     self.entity_nums_ = 0 6 end 7  8 function entity_manager:register_entity( entity ) 9     if entity == nil then10         return11     end12 13     local entity_rd14     entity_rd = self.entity_nums_ + 115     entity:set_runtime_id( entity_rd )16 17     table.insert( self.entity_list_, entity )18     self.entity_nums_ = entity_rd19 end20 21 function entity_manager:remove_entity( entity )22     if entity == nil then23         return24     end25 26     local entity_rd27     entity_rd = entity:get_runtime_rd()28 29     if entity_rd > self.entity_nums_ then30         return31     end32     entity:remove_from_node()33     table.remove( self.entity_list_, entity_rd )34     for index, v_t in ipairs( self.entity_list_ ) do35         if index >= entity_rd then36             v_t:set_runtime_id( index )37         end38     end39     self.entity_nums_ = #self.entity_list_40 end41 42 function entity_manager:update( dt )43     for _, v_t in ipairs( self.entity_list_ ) do44         v_t:update( dt )45     end46 end47 48 return entity_manager

 

提供一个概念就是runtime entity id, 我们不能保证一个类型只可以创建一个对象,这是不合理的,所以除了entity的uniqure_id之外,就提供了运行时候的id。由于这个运行时候的id是动态的,所以在remove的时候需要更新一下,也就是上面table.remove下面的操作。下面是entity的代码:

 1 local entity = class( "entity", nil ) 2  3 entity.debug_mode_ = true 4 entity.debug_color_ = cc.c4f( 0, 1, 0, 1 ) 5 entity.callback_list_ = {} 6 entity.runtime_id_ = nil 7  8 function entity:set_debug_mode( mode ) 9     self.debug_mode_ = mode10 end11 12 function entity:get_debug_mode()13     return self.debug_mode_14 end15 16 function entity:set_debug_color( color )17     self.debug_color_ = color18 end19 20 function entity:get_debug_color()21     return self.debug_color_22 end23 24 function entity:set_runtime_id( runtime_id )25     self.runtime_id_ = runtime_id26 end27 28 function entity:get_runtime_id()29     return self.runtime_id_30 end31 32 function entity:register_callback( callback, target )33     if callback == nil then34         return35     end36 37     for _, v_t in ipairs( self.callback_list_ ) do38         if v_t[1] == callback and v_t[2] == target then39             return40         end41     end42 43     table.insert( self.callback_list_, { callback, target } )44 end45 46 function entity:remove_callback( callback, target )47     if callback == nil then48         return49     end50 51     for index, v_t in ipairs( self.callback_list_ ) do52         if v_t[1] == callback and v_t[2] == target then53             table.remove( self.callback_list_, index )54         end55     end56 end57 58 function entity:update( dt )59     local callback60     local target61     for _, v_t in ipairs( self.callback_list_ ) do62         callback = v_t[1]63         target = v_t[2]64         if target ~= nil then65             callback( target, dt )66         else67             callback( dt )68         end69     end70 end71 72 return entity

 

这是一个基类的实现,不算复杂,也很好理解,就没什么好说的了.为什么要写一个entity base类呢? 因为现在的项目可能会用序列帧,也可能会用骨骼动画. 如果是骨骼动画的话,那么所有的action都比较好处理, bounding_box也很好获得.相应的接口就是

1 armature:getAnimation():play( action_const_name )2 3 local bounding_box = armature:getBoundingBox() 

 

然后就可以很简单的使用AABB或者是OBB进行碰撞检测以及设置AI等这些杂七杂八的东西了. 我们都知道序列帧使用的时候就没有那么方便了,在面对.png和Plist这样的组合的时候,动作的处理需要自己去解析,而Boundingbox也需要根据当前执行的动作做状态监测.好吧,废话说的有点多了,我简单实现了部分骨骼实体的封装,代码如下:

 1 local entity = require "src.firework.entity.entity" 2  3 local skeleton_entity = class( "skeleton_entity", entity ) 4      5 function skeleton_entity:ctor( armature_const_name ) 6     self.skeleton_armature_ = nil 7     self.draw_debug_node_ = nil 8  9     self.skeleton_armature_ = ccs.Armature:create( armature_const_name )10     self.draw_debug_node_ = cc.DrawNode:create()11     self.skeleton_armature_:addChild( self.draw_debug_node_ )12     self:init_callbacks()        13 end    14 15 function skeleton_entity:play( const_action_name )16     self.skeleton_armature_:getAnimation():play( const_action_name )17 end18 19 function skeleton_entity:init_callbacks()20     self:register_callback( self.draw_debug_bounding_box, self )21 end22 23 function skeleton_entity:set_anchor_point( anchor_point )24     self.skeleton_armature_:setAnchorPoint( anchor_point )25 end26 27 function skeleton_entity:get_anchor_point()28     return self.skeleton_armature_:getAnchorPoint()29 end30 31 function skeleton_entity:set_position( position )32     self.skeleton_armature_:setPosition( position )33 end34 35 function skeleton_entity:get_position()36     return self.skeleton_armature_:getPosition()37 end38 39 function skeleton_entity:add_to_node( node )40     node:addChild( self.skeleton_armature_ )41 end42 43 function skeleton_entity:remove_from_node()44     self:remove_callback( self.draw_debug_bounding_box, self )45     self.skeleton_armature_:getParent():removeChild( self.skeleton_armature_ )46 end47 48 function skeleton_entity:get_bounding_box()49     return self.skeleton_armature_:getBoundingBox()50 end51 52 function skeleton_entity:draw_debug_bounding_box( dt )53     local bounding_box = self:get_bounding_box()54     local lb = self.skeleton_armature_:convertToNodeSpace( cc.p( bounding_box.x, bounding_box.y ) )55     local lt = self.skeleton_armature_:convertToNodeSpace( cc.p( bounding_box.x, bounding_box.y + bounding_box.height ) )56     local rt = self.skeleton_armature_:convertToNodeSpace( cc.p( bounding_box.x + bounding_box.width, bounding_box.y + bounding_box.height ) )57     local rb = self.skeleton_armature_:convertToNodeSpace( cc.p( bounding_box.x + bounding_box.width, bounding_box.y ) )58 59     self.draw_debug_node_:clear()60     self.draw_debug_node_:drawLine( lb, lt, self:get_debug_color() )61     self.draw_debug_node_:drawLine( lt, rt, self:get_debug_color() )62     self.draw_debug_node_:drawLine( rt, rb, self:get_debug_color() )63     self.draw_debug_node_:drawLine( rb, lb, self:get_debug_color() )64 end65 66 return skeleton_entity

 

我只是单纯的去获取创建Armatrue骨骼,没有加载资源,因为资源加载部分肯定是单独做的,这里只是顺便提一下.现在实现的部分代码只是单纯的画出了Boundingbox的区域,其他的还都没做.下面给出我的unittest部分的源码:

 1 local test_case = require "src.unittest.test_case" 2 local skeleton_entity = require "src.firework.entity.skeleton_entity" 3 local entity_manager = require "src.firework.entity.entity_manager" 4 local visible_rect = require "src.firework.visible_rect" 5 local test_entity_manager_case = class( "test_entity_manager_case", test_case ) 6  7 local scene = cc.Scene:create() 8 if cc.Director:getInstance():getRunningScene() then 9     cc.Director:getInstance():replaceScene( scene )10 else11     cc.Director:getInstance():runWithScene( scene )12 end13 14 function test_entity_manager_case:run_impl()15     ccs.ArmatureDataManager:getInstance():addArmatureFileInfo( "Hero/Hero0.png", "Hero/Hero0.plist", "Hero/Hero.ExportJson" )16     local entity_manager_ins = entity_manager.new()17     local skeleton_entity_ins = skeleton_entity.new( "Hero" ) 18     skeleton_entity_ins:play( "loading" )19     skeleton_entity_ins:set_anchor_point( cc.p( 0.5, 0.5 ) )20     skeleton_entity_ins:set_position( visible_rect:center() )21     skeleton_entity_ins:add_to_node( scene )22 23     ccs.ArmatureDataManager:getInstance():addArmatureFileInfo( "tauren/tauren0.png", "tauren/tauren0.plist", "tauren/tauren.ExportJson" )24     local skeleton_entity_ins1 = skeleton_entity.new( "tauren" )25     skeleton_entity_ins1:play( "loading" )26     skeleton_entity_ins1:set_anchor_point( cc.p( 0.5, 0.5 ) )27     skeleton_entity_ins1:set_position( visible_rect:right_center() )28     skeleton_entity_ins1:add_to_node( scene )29 30     entity_manager_ins:register_entity( skeleton_entity_ins )31     entity_manager_ins:register_entity( skeleton_entity_ins1 )32     33     local scheduler = cc.Director:getInstance():getScheduler()34     scheduler:scheduleScriptFunc( function ( dt )35         entity_manager_ins:update( dt )36     end, 0, false )37 end38 39 return test_entity_manager_case

 

把这些代码加到test_controller中就好了.代码如下:

 1 local fmt_logger = require "src.firework.fmt_logger" 2  3 local test_controller = class( "test_controller", nil ) 4  5 function test_controller:ctor() 6     fmt_logger.trace("---------------------------------------------------------") 7     fmt_logger.info("        running mode: [" .. self.__cname .. "]               ") 8 end 9 10 function test_controller:run()11     12     require "src.unittest.test_case"13     get_test_case_sample().new():run()14 15     local test_fmt_logger_case = require "src.unittest.firework.test_fmt_logger_case"16     test_fmt_logger_case.new():run()17 18     local test_default_dispatcher_case = require "src.unittest.firework.test_default_dispatcher_case"19     test_default_dispatcher_case.new():run()20 21     local test_g_firework_case = require "src.unittest.firework.test_g_firework_case"22     test_g_firework_case.new():run()23 24     local test_event_dispatcher_case = require "src.unittest.firework.test_event_dispatcher_case"25     test_event_dispatcher_case.new():run()26 27     local test_measure_manager_case = require "src.unittest.firework.test_measure_manager_case"28     test_measure_manager_case.new():run()29 30     local test_layer_update_case = require "src.unittest.firework.test_layer_update_case"31     --test_layer_update_case.new():run()32 33     local test_entity_manager_case = require "src.unittest.firework.test_entity_manager_case"34     test_entity_manager_case.new():run()35 36 end37 38 return test_controller

 

unittest这一套是我自己写的,只是为了自己用着方便, 如果需要知道如何实现的,请去参考前面文章.我在写代码分离模块的时候写过这部分的代码.

cocos版本是cocos2d-x 3.3 final. 如果是用 <3.3版本或者是2.x版本,相信修改少量的代码就可以了.就到这里了。

 

cocos2d-x开发: 场景实体(entity)管理