cocos2d H5加载时崩溃问题

针对Cocos H5游戏开发过程中遇到的角色打斗动画导致的内存溢出问题,本文介绍了如何通过修改引擎加载逻辑来实现资源的按需加载,降低内存消耗。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近手头上一个cocos H5游戏,需求是根据后端传来的数据进行模拟角色打斗。角色不多,但是有很多全屏的技能动画,关键这些人物动作和技能全部是用帧动画来实现的。全部动作加起来应该有近两千的精灵帧吧。如果全部加载的的话,内存肯定会爆掉导致浏览器崩溃。
首先能想到的肯定是动态加载,即主场景只加载主场景的资源。这样试过之后,在浏览器上仍然是在loading的时候就崩溃,起初我以为是调用cc.loader.load文件数量过多导致的,后来做了一个小实验,用批处理生成了几百个文件,同时加载没问题。就否定了文件数量的问题,进一步确定是内存不足导致的崩溃。
其实在Windows上chrome的任务管理器中,内存占用才几百兆,应该不足以使iPhone崩溃。在再Mac上调试是,发现占用内存达到1.5G,惊了我个呆。(这里windows和Mac的内存占用区别这里不做解释,有兴趣的可以自己下去研究一下)
前面说的一个解决方案时动态加载,采取的具体方案是:先只加载主场景所用到的文件与资源。
但这里会产生一个问题,由于是在H5里调用的cc.loader.load()方法,里面其实包括的是下载预处理资源。也就是说战斗场景的资源此时还没有下载,等到去打斗的时候再去下载?打斗再下载会存在一定时间的等待,是否不合常理?
通常我们玩的游戏里应该是一进来就下载所有的文件资源,只是首先会预加载一部分资源,其余的会在需要用到的时候再预加载。应该预加载的时间相对下载时间来说是相当快的,不需要过多时间去等待。
问题又来了,前面说过cc.loader.load()方法里面包含了下载和预处理,所以用引擎提供的方法已经不能满足我们的需求,怎么办?毫不犹豫的改啊!先看下源码,这里主要看texture的加载,看它到底做了什么!

cc._imgLoader = {
    load : function(realUrl, url, res, cb){
        var callback;
        if (cc.loader.isLoading(realUrl)) {
            callback = cb;
        }
        else {
            callback = function(err, img){
                if(err)
                    return cb(err);
                cc.loader.cache[url] = img;
                cc.textureCache.handleLoadedTexture(url);
                cb(null, img);
            };
        }
        cc.loader.loadImg(realUrl, callback);
    }
};

cc.loader.cache[url] = img;
cc.textureCache.handleLoadedTexture(url);
着重看这两句,这是下载img文件之后的回调,第一句cc.loader.cache[url] = img,放入到缓存没问题。
但是handleLoadedTexture,字面意思就是处理已下载的Texture,继续看源码。

var _p = cc.textureCache;

        _p.handleLoadedTexture = function (url) {
            var locTexs = this._textures;
            //remove judge
            var tex = locTexs[url];
            if (!tex) {
                tex = locTexs[url] = new cc.Texture2D();
                tex.url = url;
            }
            tex.handleLoadedTexture();
        };

这里做的事显而易见,new 了一个Texture2D,存在了textureCache中,然后对改Texture2D对象调用了handleLoadedTexture()函数,如下:

handleLoadedTexture: function (premultiplied) {
            var self = this;
            premultiplied =
              (premultiplied !== undefined)
                ? premultiplied
                : self._hasPremultipliedAlpha;
            // Not sure about this ! Some texture need to be updated even after loaded
            if (!cc.game._rendererInitialized)
                return;
            if (!self._htmlElementObj) {
                var img = cc.loader.getRes(self.url);
                if (!img) return;
                self.initWithElement(img);
            }
            if (!self._htmlElementObj.width || !self._htmlElementObj.height)
                return;

            //upload image to buffer
            var gl = cc._renderContext;

            cc.glBindTexture2D(self);

            gl.pixelStorei(gl.UNPACK_ALIGNMENT, 4);
            if(premultiplied)
                gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);

            // Specify OpenGL texture image
            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, self._htmlElementObj);

            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

            self.shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURE);
            cc.glBindTexture2D(null);
            if(premultiplied)
                gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 0);

            var pixelsWide = self._htmlElementObj.width;
            var pixelsHigh = self._htmlElementObj.height;

            self._pixelsWide = self._contentSize.width = pixelsWide;
            self._pixelsHigh = self._contentSize.height = pixelsHigh;
            self._pixelFormat = cc.Texture2D.PIXEL_FORMAT_RGBA8888;
            self.maxS = 1;
            self.maxT = 1;

            self._hasPremultipliedAlpha = premultiplied;
            self._hasMipmaps = false;

            //dispatch load event to listener.
            self.dispatchEvent("load");
        },

这里将webGL讲纹理绘制了出来以便直接使用,而程序里面纹理是最占内存的。
综上,调用cc.loader.load除了下载之外,还会将.png/jpg等格式的纹理绘制出来。所以load资源过多时,会由于纹理绘制导致内存急剧飙高,然后导致崩溃。
知道了这些问题,然后做之前的需求就好做了,改一下引擎的代码,cc._imgLoader里面只下载文件,不做handleLoadedTexture预处理。这个预绘制我们自己手动调用,需要的时候自己来调用,即可做到分批使用和分批绘制。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值