我要吐槽:内存管理篇

我想 creator一定想像不到,特效人员做粒子因为运行时与在编辑器里面不同步时的大骂,
我想 creator一定想像不到,策划配置关卡因为误删了一个图片,导致场景打不开,一个上午的工作浪费时的大骂,
我想 creator一定想像不到,逻辑程序代码播放了动画,动画出现异常而无力修改时的大骂,
同样,
我想 creator一定想像不到,因为使用creator咱们程序代码时间缩短了至少三分之一,打版本不用再等上几个小时,
我想 creator一定想像不到,因为使用creator咱们策划人员做关卡可以边做边改边看,
所以so so……

我一直很奇怪为什么plist,creator会转为自己的东西,为什么龙骨要用这个runtime呢,我把某个动画文件直接转为creator格式

    # 生成PLIST meta    
    plist_meta = {}
    plist_meta['ver'] = '1.2.4'
    plist_meta['uuid'] = str(uuid.uuid1())        
    plist_meta['rawTextureUuid'] = rawTextureUuid
    plist_meta['size'] = {}
    arr = dict2array(str(data['metadata']['size']))
    plist_meta['size']['width'] = int(arr[0])
    plist_meta['size']['height'] = int(arr[1])
    plist_meta['type'] = 'Texture Packer'
    plist_meta['subMetas'] = {}
    for i in data['frames']:
        plist_meta['subMetas'][i] = {}
        plist_meta['subMetas'][i]['ver'] = '1.0.3'
        plist_meta['subMetas'][i]['uuid'] = str(uuid.uuid1())
        plist_meta['subMetas'][i]['rawTextureUuid'] = rawTextureUuid
        plist_meta['subMetas'][i]['trimType'] = 'custom'
        plist_meta['subMetas'][i]['trimThreshold'] = 1            
        rotated = False
        try:
        	rotated = data['frames'][i]['rotated']
        except:
        	rotated = False
        if rotated:
            plist_meta['subMetas'][i]['rotated'] = True
        else:
        	plist_meta['subMetas'][i]['rotated'] = False
        arr = dict2array(str(data['frames'][i]['offset']))
        plist_meta['subMetas'][i]['offsetX'] = float(arr[0])
        plist_meta['subMetas'][i]['offsetY'] = float(arr[1])
        arr = dict2array(str(data['frames'][i]['frame']))
        plist_meta['subMetas'][i]['trimX'] = int(arr[0][0])
        plist_meta['subMetas'][i]['trimY'] = int(arr[0][1])
        plist_meta['subMetas'][i]['width'] = int(arr[1][0])
        plist_meta['subMetas'][i]['height'] = int(arr[1][1])
        arr = dict2array(str(data['frames'][i]['sourceSize']))
        plist_meta['subMetas'][i]['rawWidth'] = int(arr[0])
        plist_meta['subMetas'][i]['rawHeight'] = int(arr[1])
        plist_meta['subMetas'][i]['borderTop'] = 0
        plist_meta['subMetas'][i]['borderBottom'] = 0    
        plist_meta['subMetas'][i]['borderLeft'] = 0
        plist_meta['subMetas'][i]['borderRight'] = 0
        plist_meta['subMetas'][i]['spriteType'] = 'normal'
        plist_meta['subMetas'][i]['subMetas'] = {}        
    f = open(name + '.plist.meta', 'w')
    f.write(json.dumps(plist_meta))
    display_all_count = {}
    for k in data['frames']:
        display_all_count[str(k)] = 0  
    animation = {}
    # 生成AnimationClip
    for i in data['mcs']:   
        display_count = {}
        for k in data['frames']:
            display_count[str(k)] = 0        
        ani_clip = {}
        ani_clip['__type__'] = 'cc.AnimationClip'
        ani_clip['_name'] = i
        ani_clip['_objFlags'] = 0
        ani_clip['_rawFiles'] = None
        ani_clip['sample'] = data['metadata']['fps']
        ani_clip['_duration'] = data['mcs'][i]['totalFrames'] / data['metadata']['fps'] 
        ani_clip['speed'] = 1
        ani_clip['wrapMode'] = 1
        ani_clip['curveData'] = {}
        ani_clip['curveData']['props'] = {}
        ani_clip['curveData']['paths'] = {}
        
        for j in data['mcs'][i]['children']:
            asset = data['mcs'][i]['children'][j]['class']
            display1 = get_dict_key_by_index(display_count, int(data['displays'][asset]['start']))                    
            display = display1 + '_' + str(display_count[str(display1)])
            display_count[str(display1)] += 1
            if display_count[str(display1)] > display_all_count[str(display1)]:
                display_all_count[str(display1)] = display_count[str(display1)]                
            ani_clip['curveData']['paths'][display] = {}
            ani_clip['curveData']['paths'][display]['props'] = {}
            ani_clip['curveData']['paths'][display]['props']['position'] = []
            ani_clip['curveData']['paths'][display]['props']['scaleX'] = []
            ani_clip['curveData']['paths'][display]['props']['scaleY'] = []
            ani_clip['curveData']['paths'][display]['props']['rotation'] = []
            ani_clip['curveData']['paths'][display]['props']['opacity'] = []
            ani_clip['curveData']['paths'][display]['props']['zIndex'] = []
            ani_clip['curveData']['paths'][display]['props']['skewX'] = []
            ani_clip['curveData']['paths'][display]['props']['skewY'] = []
            ani_clip['curveData']['paths'][display]['props']['active'] = []
            frame = {}
            frame['frame'] = 0
            frame['value'] = True
            ani_clip['curveData']['paths'][display]['props']['active'].append(frame)
            frame_data = parse_frames_data(str(data['mcs'][i]['children'][j]['frames'])) 
            current = 0               
            for k in frame_data:
                if len(k) <= 1:
                    current += 1
                    continue
                frame_time =  float(current) / float(data['metadata']['fps'])
                frame = {}
                frame['frame'] = frame_time
                frame['value'] = []
                frame['value'].append(float(k[0]))
                frame['value'].append(float(k[1]))
                ani_clip['curveData']['paths'][display]['props']['position'].append(frame)
                frame = {}
                frame['frame'] = frame_time
                frame['value'] = float(k[2])
                ani_clip['curveData']['paths'][display]['props']['rotation'].append(frame)
                frame = {}
                frame['frame'] = frame_time
                frame['value'] = float(k[3])
                ani_clip['curveData']['paths'][display]['props']['scaleX'].append(frame)
                frame = {}
                frame['frame'] = frame_time
                frame['value'] = float(k[4])
                ani_clip['curveData']['paths'][display]['props']['scaleY'].append(frame)

                frame = {}
                frame['frame'] = frame_time
                frame['value'] = int(255 * float(k[5]))
                ani_clip['curveData']['paths'][display]['props']['opacity'].append(frame)
                if len(k) > 6:
                    frame = {}
                    frame['frame'] = frame_time                        
                    frame['value'] = int(k[6])
                    ani_clip['curveData']['paths'][display]['props']['zIndex'].append(frame)
                if len(k) > 7:
                    frame = {}
                    frame['frame'] = frame_time
                    frame['value'] = float(k[7])
                    ani_clip['curveData']['paths'][display]['props']['skewX'].append(frame)
                if len(k) > 8:
                    frame = {}
                    frame['frame'] = frame_time
                    frame['value'] = float(k[8])
                    ani_clip['curveData']['paths'][display]['props']['skewY'].append(frame)
                current += 1
        f = open(name + '_' + i + '.anim', 'w')
        f.write(json.dumps(ani_clip))
        # 生成anim的meta
        ani_clip_meta = {}
        ani_clip_meta['ver'] = '1.0.0'
        ani_clip_meta['subMetas'] = {}
        ani_clip_meta['uuid'] = str(uuid.uuid1())
        f = open(name + '_' + i + '.anim.meta', 'w')
        f.write(json.dumps(ani_clip_meta))
        animation[i] = ani_clip_meta

    # 生成预制体含动画
    currnet_id = 1
    prefab_list = []
    node_info = {}
    node_info['__type__'] = 'cc.Prefab'
    node_info['_name'] = ''
    node_info['_objFlags'] = 0
    node_info['_rawFiles'] = None
    node_info['data'] = {}
    node_info['data']['__id__'] = currnet_id
    prefab_list.append(node_info)
    # ID顺序为节点、组件、prefabInfo
    # ROOT 节点
    node_info = {}
    node_info['__type__'] = 'cc.Node'
    node_info['_name'] = 'Role'
    node_info['_objFlags'] = 0        
    node_info['_opacity'] = 255
    node_info['_color'] = {}
    node_info['_color']['__type__'] = 'cc.Color'
    node_info['_color']['r'] = 255
    node_info['_color']['g'] = 255
    node_info['_color']['b'] = 255
    node_info['_color']['a'] = 255
    node_info['_cascadeOpacityEnabled'] = True
    node_info['_parent'] = None
    node_info['_anchorPoint'] = {}
    node_info['_anchorPoint']['__type__'] = 'cc.Vec2'
    node_info['_anchorPoint']['x'] = 0.5
    node_info['_anchorPoint']['y'] = 0.5
    node_info['_contentSize'] = {}
    node_info['_contentSize']['__type__'] = 'cc.Size'
    node_info['_contentSize']['width'] = 0
    node_info['_contentSize']['height'] = 0
    
    node_info['_rotationX'] = 0
    node_info['_rotationY'] = 0
    node_info['_scaleX'] = 1
    node_info['_scaleY'] = 1
    node_info['_skewX'] = 0
    node_info['_skewY'] = 0
    node_info['_position'] = {}
    node_info['_position']['__type__'] = 'cc.Vec2'
    node_info['_position']['x'] = 0
    node_info['_position']['y'] = 0
    node_info['_localZOrder'] = 0
    node_info['_globalZOrder'] = 0
    node_info['_tag'] = -1
    node_info['_opacityModifyRGB'] = False
    node_info['_id'] = ''        
    node_info['_active'] = True
    node_info['_components'] = []
    _components = {}

因为龙骨还支持网格变形什么的,在原生平台上有 C++ 加成,性能也会高一些。用 AnimationClip 就没有 C++ 加成了。

你这个根本不是性能问题,龙骨的数据都是保存在Factory里面的包括图片解析数据
这在Native上每ArmatureDisplay都是不同的Factory那岂不是我要换装,我要向每个ArmatureDisplay的Factory里新加图片解析数据,这样会有冗余数据,如果拿到的是全局单例的Factory结果可想而知!

现在确实会有冗余数据,我们会作为已知 bug 进行修复的。

cocos做arpg有什么明显的坑吗。 能不能说2个。。 正在做

哦还有,我希望有个在active = false的时候,能有个像onLoad,start这种函数来初始化数据的函数

在编辑器中如果设置active = false;
onLoad是不会执行的,我希望有个方法能初始化数据,
虽然可以写在ctor构造函数里面,但是编辑器那个黄色警告看起相当不爽

组件上不会增加你说的方法,因为 active 为 false 时,我们希望的是没有任何开销。
建议初始化的话应该是由其它脚本上的逻辑来调用。
另外,1.5 支持用 __ctor__ 定义构造函数,那就不会有黄色的警告了

如果一个开始active = false,这样就无法让active = true;
也只能通过其他脚本来保持有这个节点的引用来调用active = true;
如果由其他的脚本逻辑来调用的话,这样就增加了耦合,并没有达到解耦!

其实通过一开始创建这个组件的时候注册了事件,通过发送事件的方式来设置active = true;
会更好一些!

如果不加的话,也只能在ctor构造函数里去注册了!

@toddlxt @jiuaizxc
这个 active = false 的问题请移步 http://forum.cocos.com/t/active/44891 继续讨论,这里不继续歪楼了。

一个简单de 内存处理方法,官方可以参考下: 当缓存的图片数量超过200个,只保留最近使用的100个图片,其他的全释放掉。这样所占内存的图片数量永远在100-200之间,内存稳定,重复进入某些界面也不会卡顿。
当然这个方案也可以精准到计算尺寸大小。

首先坑确实多,然而感觉你们是想让引擎组帮你们把游戏做了啊,我还是希望引擎组专心搞好引擎部分,引擎就该具有通用性,像内存管理这些应该是游戏框架做的事了,根据数量释放,根据内存大小释放,立即释放延迟释放? 1000款游戏就会有1000个不同的需求,这些上层也很容易定制自己需要的实现,也就是分分钟的事。

4赞

这个贴子看下来,还真是让人很不爽,这次我真的要为Creator引擎组的人说一下公道话。说实话,仅管Creator使用过程中有一些问题,但总体上,他已经比以前的Cocos好太多太多。

我们做的是重度的MMO手游,和端游一样,玩家是在同一个场景里的,最多可以达到同屏上百人。目前的进展已经到堆各种系统的程度。而角色管理,战斗技能,场景管理,刷怪,AI已经基本完成。我列一个大概的数据:同屏50个角色的情况下,安卓1000左右价位的机器,FPS是50帧。而这50帧并不角色静止在那里的,是有战斗特效,有满屏的掉落物。

我想如果Creator可以做成这样的游戏,它真的有像你们说的那么弱吗?在我看来,引擎只要把最核心的和最基本的功能做好,而作为一个有经验的程序员:

要能够驾驭这个引擎,知道它的缺点和优点,扬长避短。

要根据自己项目的特点去做优化,像上面说的NodePool,我觉得刚刚好,因为我相信引擎组的人是预料不到我需要什么样的缓存的,因此我基于NodePool,对项目的资源进行分类Cache,使用LRUCache对无用资源进行释放。最后的效果是很符合项目的。

要积极将自己的想法或问题反馈给引擎组,帮助引擎组的人提高代码质量,而不是带着情绪来喷。

最后,我还是认为当初选择Creator是正确的,也希望Creator向更好的方向发展。

5赞

我们的游戏同屏50人,各种技能特效,满屏的掉落物。安卓低端机50FPS,苹果60毫无压力。

有你说的那么严重么?

我相信是你用得不好:grin:

越战越勇,越槽越勇!

赞成扬长避短 2333

希望你们的产品早点上线,能给大家一个信心

给你看一张我们游戏的画面,我们游戏也是8方向的,和端游的2D MMO基本一样的,这个应该是能集中体现Ceator的能力了:

2赞

这脸打的漂亮。只是会喷,自己嘛都不会的的都是弱鸡,辣鸡