Sign in to follow this  
PainKKKiller

Make movieclip from several atlases and play it whithout lags

Recommended Posts

In my project I have some animations which made from too many frames, and on machines with weak videocards I am getting error with MAX_TEXTURE_SIZE and animation shows as black rectangle. To avoid that I divided spritesheet with animation into smaller ones, and make movieclip from them. Code looks like this:

this.loader = new PIXI.loaders.Loader();

this.loader  //  load spritesheets with anime
            .add("b_b_0", 'res/anime/b_b_0.json')
            .add("b_b_1", 'res/anime/b_b_1.json')

...

AssetsManager.prototype.getFrames = function(resName) {
    var json = this.getJSON(resName);
    var frames = [];
    var framesKeys = Object.keys(json.frames).sort(naturalSort);
    var textures;
    if(this.commonLoader.resources[resName])
        textures = this.commonLoader.resources[resName].textures
    else if(this.ringLoader.resources[resName])    
        textures = this.ringLoader.resources[resName].textures;
    else throw new Error("There is no textures with name " + resName);
    
    for(var i = 0; i < framesKeys.length; i++) {
        frames.push(textures[framesKeys[i]]);
    }
    return frames;
}

//MAX_TEXTURE_SIZE problem WebGL
AssetsManager.prototype.getBigMovieClip = function(resName, length) {
    var frames = [];
    for (var i = 0; i < length; i++) {
         frames = frames.concat(this.getFrames(resName + "_" + i));
    }

    var movie = new PIXI.extras.AnimatedSprite(frames);
    return movie;  
}

and the I use getBigMovieClip("b_b", 2) to get MovieClip made from 2 spritesheets. Everything ok, but I see small lag while movieclip playing and I have no idea how to overcome it. Thanks for any suggestions in advance. 

Share this post


Link to post
Share on other sites

 

Don't know how to use it properly, just found this post, tried to use it in several ways. one of attempts looks like this:

renderer = PIXI.autoDetectRenderer(consts.resolutions[consts.RESOLUTION].width, consts.resolutions[consts.RESOLUTION].height, 
                                        { view: document.getElementById("game-canvas") });

...

AssetsManager.prototype.getBigMovieClip = function(resName, length) {
    var frames = [];
    for (var i = 0; i < length; i++) {
        var fr = this.getFrames(resName + "_" + i);
        renderer.plugins.prepare.upload(fr);
        frames = frames.concat(this.getFrames(resName + "_" + i));
    }

    
    var movie = new PIXI.extras.AnimatedSprite(frames);
    return movie;  
}

But anyway I still have a lag in the middle of movieclip.

Share this post


Link to post
Share on other sites

ServerCharlie thanks for reply, tried in this way: 

AssetsManager.prototype.getBigMovieClip = function(resName, length) {
    var frames = [];
    for (var i = 0; i < length; i++) {
        var fr = this.getFrames(resName + "_" + i);
        renderer.plugins.prepare.upload(fr);
        frames = frames.concat(fr);
    }
    var fr_set = [];
    for (var j = 0; j < frames.length; j++) {
        var frame = {
            texture: frames[j],
            time: 50
        };
        fr_set.push(frame);
    }
    
    var movie = new PIXI.extras.AnimatedSprite(fr_set);
    return movie;  
}

Animation has got much slower, but lag still exists.

Share this post


Link to post
Share on other sites

So I smoothed out my movie clip animation with the following.

The prepare plugin was the right solution but never worked. WebGL needs the entire texture(s) uploaded not the frames. The way textures are uploaded to the GPU is via renderer.bindTexture(texture). When the PIXI loader receives a sprite atlas url e.g. my_sprites.json it automatically downloads the image file and names it as mysprites.json_image in the loaders resources. So you need to grab that, make a texture and upload it to the GPU. 

let loader = new PIXI.loaders.Loader()
loader.add('http://mysite.fake/sprite1.json')
loader.add('http://mysite.fake/sprite2.json')
loader.add('http://mysite.fake/sprite3.json')
loader.once('complete', callback)
loader.load()


function uploadToGPU(resourceName){
  resourceName = resourceName + '_image'
  let texture = new PIXI.Texture.fromImage(resourceName)
  this.renderer.bindTexture(texture)
}

loadSpriteSheet(function(resource){
  uploadToGPU('http://mysite.fake/sprite1.json')
  uploadToGPU('http://mysite.fake/sprite2.json')
  uploadToGPU('http://mysite.fake/sprite3.json')

  // helper function to get all the frames from multiple textures
  let frameArray = getFrameFromResource(resource)
  let animSprite = new PIXI.extras.AnimatedSprite(frameArray)
  this.stage.addChild(animSprite)
  animSprite.play()  
})

Share this post


Link to post
Share on other sites

Quick explanation about attached demo.

Sorry for little mess, I tried to include only necessary files. 

Demo made in big (1920x1080 resolution) to show effect.

I do my project with browserify, so if you want to compile it from source (in src folder) you have to install it. Otherwise you can use app.bundle.js file, it not minified,  just check index.html to use it.

When click button PLAY movieclip plays, and in first time I clearly see lag when stone falls on back, after that movieclip plays without lag.

The module where movieclips are making is src/AssetsManager.js where getBigMovieClip function is located, where I tried to use prepare plugin. Then I play it in app.js with settimeout function just to check that all uploaded.

 

Thanks in advance.

bigdemo.zip

Share this post


Link to post
Share on other sites

Adding this to your code worked for me. As has been pointed out this is not the proper solution. But it works.

AssetsManager.prototype.preloadCommon = function(callBack) {

        preloader.show();

        this.commonLoader

            ...

            //Added this on complete handler
            .on("complete",function(loader, resource){
                console.log(resource)
                renderer.bindTexture(new PIXI.Texture.fromImage("b_k_0_image"))
                renderer.bindTexture(new PIXI.Texture.fromImage("b_k_1_image"))
            })

            ...

            .load(function() {
                callBack();
                preloader.remove();
            });

 

Share this post


Link to post
Share on other sites

From slack:

Quote

andrewstart [5:03 PM] 
looking at that, I am assuming that no one is actually passing a callback to `renderer.plugins.prepare.upload()`, so they are telling it to get prepared, then immediately trying to use it

I dont see in your demo the place when you actually wait for prepare.

 

Share this post


Link to post
Share on other sites

If you are talking in regard to my demo, I am waiting because the sprite is only added to the stage and played when the user clicks the canvas. You can wait as long as you like for the for the prepare to work.

Share this post


Link to post
Share on other sites

I have done some further research and I have found at least one of the possible issues. PIXI.extras.AnimatedSprite extends PIXI.Sprite, which assumes the concept that it has one BaseTexture. Creating an AnimatedSprite from frames from multiple sources still results in an AnimatedSprite with one BaseTexture, it appears to choose the last frames texture as the BaseTexture. Then when you run the prepare plugin it uses a function called `findBaseTextures` which uploads the baseTexture of an item to the GPU. In the case of an AnimatedSprite it uploads only the one texture, meaning one is the GPU and the others are not and when you play you AnimatedSprite it has to load any other things into the GPU at that time. Hence the laggy playback. I might place all this info in the PIXI issue tracker, see If I can get some help.

Share this post


Link to post
Share on other sites

I am pretty sure my jsfiddle is correct

app.renderer.plugins.prepare.upload(sprite,function(){
  console.log('uploaded animated sprite')
  enableButton()
})
    
...

function enableButton(){
  var button = document.getElementById('button')
  button.disabled = false
  button.innerHTML = 'Ready, play animation'
  button.addEventListener('click',function(){
    if(!app || !app.stage) return
    console.log('click')
    app.stage.addChild(sprite)
    sprite.gotoAndPlay(0)   
  })
}

 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

  • Recently Browsing   0 members

    No registered users viewing this page.