Sign in to follow this  
MicahHauge

PLEASE HELP: PIXI.js animation optimization

Recommended Posts

Hey guys! I'm pretty new to this forum and to PIXI.js so please excuse me if I'm posting in the wrong place.

I am creating a game similar to guitar hero, but for piano (basically a synthesia clone). Here is a link to my JSFiddle

I have the basics setup how I want, but I am running into some performance issues. I need the game to run at a solid framerate with up to ~10,000 notes in a level (not all being displayed at the same time of course).  Currently, the note graphics are created and added to the stage within a group and animated downward. When they are added, their visible property is set to false and is only set to true when the are within the view.

Here is my animation loop: 

function animate() {
    gameTime = performance.now() / 1000 - startTime;
    // CULLING loop
    // loop to go through all notes and see if they should currently be visible
    for (i = 0; i < numNotes; i++) {
      // if the note should be visible, make it visible
      if (gameTime + offset > notes[i].startTime && gameTime < notes[i].stopTime + offset) {
        if (notes[i].graphic.visible == false) {
          notes[i].graphic.visible = true;
        }
      }
      // if the note should not be visible, set visible to false
      else if (notes[i].graphic.visible == true) {
        notes[i].graphic.visible = false;
      }
    }

    // set y pos based on gameTime
	  group.y = (ySlope * -1 * gameTime);

    // or set it manually
	  // group.y += 8;

    renderer.render(stage);
    requestAnimationFrame(animate);
}

 

The frame rate is currently not where I want it to be, so I am hoping that someone can show me where I can make some optimizations. Feel free to play around with the JSFiddle.

Thanks,

Micah 

 

Screen Shot 2016-07-30 at 10.06.18 PM.png

Share this post


Link to post
Share on other sites
1 hour ago, MicahHauge said:

@Milton @ivan.popelyshev I think that is a great recommendation but notes must be able to have varying lengths like so: JSFiddle. Because of this, it makes a sprite for every note. Would sprite batching help in this situation? Or is there some way that I can adjust the length of a sprite without messing up dimensions?

You can create three textures out of one: one for the top, one for middle and one for bottom. Then for each note, create three sprites, adjust the height of second sprite. Look at Texture constructor to understand what I mean: https://github.com/pixijs/pixi.js/blob/dev/src/core/textures/Texture.js . you can make one texture out of other this way : 

var demoTex = new Texture(oldTexture.baseTexture, new PIXI.Rectangle(x, y, width, height));

//create three textures there:
...
//and now create a note

var note = new PIXI.Container();
note.addChild(new PIXI.Sprite(topTexture));
note.addChild(new PIXI.Sprite(middleTexture));
note.addChild(new PIXI.Sprite(bottomTexture));
node.children[1].position.y = 10;
node.children[1].height = noteHeight - 20;
node.children[2].position.y = noteHeight - 10;

Please create only three textures, don't do 10000*3 :)

Share this post


Link to post
Share on other sites

@ivan.popelyshev Thank you for your quick and helpful response. I adjusted my code so that only one texture is created and used for all the sprites. Here is a link to the JSFiddle

It seems a bit faster, but unfortunately stuttering is still occurring. Because of this, I have not yet implemented your three texture approach so all note lengths are the same. I would like to get it running smoothly with fixed note lengths before adding the functionality for variably lengths. Any idea why it is still stuttering? 

Thanks again for your help,

Micah

 

Share this post


Link to post
Share on other sites

I think your idea of just adding 15000 sprites to a container and scrolling that is not very optimal.
Only add them when needed (visible, maybe index them on startTime?). That means your loop will also be optimal, instead of the 15000 you're doing now.

Share this post


Link to post
Share on other sites
17 hours ago, MicahHauge said:

It seems a bit faster, but unfortunately stuttering is still occurring.

I thinks that's because of browser vsync problem. If anybody knows how to fix that - would love to know how. Such small stuttering is apparent on every example of scrolling I've seen. In real games it's usually not noticeable because usually there are more visual elements and movement is not constant and linear. Now if you're making something like Piano Tiles - then it is going to be noticeable probably...

Share this post


Link to post
Share on other sites
3 minutes ago, Fatalist said:

I thinks that's because of browser vsync problem.

Please.

The 'problem' is with the insane "let's just add 15000 sprites to a container, and scroll that'".
There is no need for scrolling. Only draw the sprites needed, and you can pull this of on a Z80.
This is just bad programming.

 

Share this post


Link to post
Share on other sites
17 minutes ago, Milton said:

Only draw the sprites needed

That's exactly what that code does.

It runs at 60fps, but visually, stutters a little, every few seconds. It may not happen on every browser/OS/hardware.

Share this post


Link to post
Share on other sites

Nope. It draws all 15000 of them on a container, and then loops all 15000 of them each and every frame, playing around with visibility. I couldn't think of a more stupid solution...

I can write this on a C64 with 60 FPS...

Share this post


Link to post
Share on other sites
9 minutes ago, Fatalist said:

Setting visible to false = not drawing the sprite.

Duh. That doesn't help the container that has already had 15000 sprites added to it.
Let's increase the notes to 1 million. You're just going to add them up front?
What game adds all 'enemies' up front? Add them when needed...

Share this post


Link to post
Share on other sites
23 minutes ago, Milton said:

Duh. That doesn't help the container that has already had 15000 sprites added to it.

So PIXI needs to loop through 15000 objects every frame and check the .visible property. 15000 checks is not that many. If it was million - then yeah, something more complex would be needed. Anyway, FPS is not a problem here - that code should run at 60fps pretty much everywhere.

Share this post


Link to post
Share on other sites

Complex? Just index on startTime. That way you only loop the needed notes. About a 100 or so. Who cares how many notes there are. A trillion still doesn't matter.
If you need to lookup Zygote in a dictionary, do you start at Aardvark?

The stutter is because of scrolling a huge container containing 15000 sprites, and checking 15000 objects every frame. You can't write it worse...

Share this post


Link to post
Share on other sites

@Milton

Thank you for this recommendation. I have adjusted my code so that the sprites are only added to the stage when they should be visible, and are removed from the stage otherwise. Here is a link to the JSFiddle. It now runs much smoother than it did before, but stuttering still occurs occasionally. You are right in that it would run faster if the loop did not check every note in the array each frame, but am having a bit of trouble implementing this. What do you mean by index on startTime? The array of notes is already sorted by startTime (least to greatest) if that makes it easer. 

Feel free to play around with the JSFiddle and post your changes. 

Share this post


Link to post
Share on other sites
4 hours ago, Fatalist said:

It runs at 60fps, but visually, stutters a little, every few seconds. It may not happen on every browser/OS/hardware.

This is true. It isn't a constant poor frame rate, it is just a stutter every few seconds. I have noticed that it runs better on Windows than it does on Mac OS and Linux. Also, There is a small amount of stuttering occasionally even when only ~100 note objects are created, especially on Linux. This may be due to most linux drivers having poor WebGL support though.

Share this post


Link to post
Share on other sites

I'm not going to do your work :) You learn by doing it yourself.

You have an array of notes that you can't access by startTime (only by a useless counter).
Let's say you use notes[startTime].push(note). Make sure startTime starts with 0 (and is an integer), just so you don't get a crazy big array.
That way you can just loop from notes[gameTime] to notes[gameTime + screenHeight]. Where gameTime == 0 means the first note(s). (I assume every 1 gameTime is 1 Y pixel, don't know if that's true...)

And I wouldn't scroll the container, just keep a Y position, and draw the notes at the right positions (try implementing UP/DOWN instead of using gameTime) . Like a vertical 'scrolling' game. Enemies don't scroll, only background maps do. Your container should be no bigger than your viewport. That should definitely fix any stutter.
 


 

 

Share this post


Link to post
Share on other sites

I dont think removeChild/addChild helped a lot, only a bit. The problem is that you are somehow creating too many objects. You have to profile your memory and fine where is it "leaking". Though, it is not a leak, it is just GC overuse :)

UPD. I'm wrong, everything is ok with your memory.

 

UPD. "else if (notes[ i ].inStage) {   group.removeChild(notes [ i ].sprite);  }" you forgot "notes [ i ] .inStage = false" there. But that's small thing

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
Sign in to follow this  

  • Recently Browsing   0 members

    No registered users viewing this page.