Jump to content

Creating and destroying lots of sprites


amshegarh
 Share

Recommended Posts

Hello, in my project i need to create a lot of sprites (we talking about 500 + something) every frame and destroy in next but alas, it seems that i dont dispose them properly and framerate drops extremely quickly, albeit at start it seems okayish

I add each to some sprites container and clear it with

sprites_container.removeChildren();

i dont keep references to sprites

Id appreciate help regarding this issue

Link to comment
Share on other sites

Hey, I use  pixi.js - v5.1.2, i need them to render map of the game, its also worth noting that 'Texture.js:445 Texture added to the cache with an id [Image43.png] that already had an entry' error pops up a lot (there are more than just one image), i grab textures from a spritesheet (its loaded only once tho)

While there is probably a way to avoid creating hundreds of sprites every second via use of zorder (that i want to not do if possible), they must be created at least once and moved/deleted as map is way bigger than screensize, im afraid that this performance drops sooner or later anyway

Link to comment
Share on other sites

I would concur with what Ivan stated. If you're creating and destroying that many objects each frame, you have a problem with approach. Think of game code as a magic trick. You really are trying to make something look a certain way using the minimal amounts of resources possible through "slight of hand". Object creation and removal is generally quite expensive ... hence loading screens ?!

You may want to consider keeping references to sprites and reuse what you can. I haven't used Pixi.js enough (I'm just starting with it), but the engines I wrote allow re-binding of textures at a low cost to sprites. 

To reach your objectives, you probably have numerous ways to reduce object lifecycles.

If you better describe what you're trying to achieve someone may be able to help you out more.

Also if you're seeing that error, you should try and see if you're trying to add the same object twice. With no real code references, it's not very for people to help.

Link to comment
Share on other sites

As I mentioned, i have a map, isometric one, that heavily depends on draw order, while i indeed can create limited amount of sprites and change their positions and textures as needed, it would complicate things as now i'll have to manually track and change z-order, right now i just draw them every game cycle in needed order

About error,  i create sprites like so 

let sprite = new PIXI.Sprite(sheet.textures[this.cell(i, j).sprite]);

since many sprites share the same texture, that might be the issue(?) this.cell...sprite contains only string name of texture

as a side question, can i disable sprite in such a way that it doesnt affect performance (but high number of sprites, like 160k)

Link to comment
Share on other sites

It looks like more tilemap-related question. Tilemaps have two aspects: Algorithm and drawing element.

1. Algorithm.

a) draw all the map. Dont do that. While pixi-tilemap can survive it, everything else cant. And for big maps even pixi-tilemap will go down.

b) For tiles that dont change you can use following strategy: fill up the certain window around a camera. When camera touches that window edge, erase everything, make new window and fill it. Its the easiest route to get good performance, but usually people dont undersatnd it from first reading. I have a Graphics beginTextureFill tilemap demo in https://github.com/ivanpopelyshev/pixi-starfighter/tree/master/lesson1_5-tilemap for that

c) if you have many changing parts, maybe its better to use Chunks. Chunks are difficult to code, especially for tilemaps - you have to separate map to many Graphics object, add new ones that come in screen, and drop ones that are out of your screen, update ones that has changed elements. I dont have pixi demo, but there's my old canvas2d demo https://github.com/ivanpopelyshev/bombermine-shuffle , caching through extra canvases.

2. Drawing element.

a) Sprites . Yeah, you noticed the problem. Of course you can poll them but it affects architecture in a bad way - if you didnt use pools before, it'll be a problem.

b) storing stuff in renderTextures. Yeah, it'll eat memory, and it also can be a problem for isometry and in case you are not familiar with the concept.

c) Graphics beginTextureFill - good , balanced solution.

d) pixi-tilemap plugin: the fastest. the only downside is that the shader does not use rotations and scaling. Everything is 1:1, cant set different scale for tiles.

Link to comment
Share on other sites

I do something similar to 1b, but currently it doesnt work very well. My tiles for the most part shouldnt be changed in game, but supposed to be changeable in real time in map and some point changes can occur.

I intend to create a sprite for every tile and set visible of all to false, making visible only those that can be seen, how would that work? Given there are 40k+ tiles. 

What should i know about 2c?

2d idk if it will suit my demands, tiles will have height associated to them, some tiles will have a texture that covers larger area than their own et cetera

Link to comment
Share on other sites

2c is "clear, refill" solution. You have to FOR through your tile data and fill that graphics with rectangles.

If you use 1b, you shouldnt have 40k+ tiles on screen. Just imagine a window that is 2x times width and height of screen: how many tiles will fit there? I think up to 5k if your tiles are small.

Its not that pixi tree is slow, the problem is that you need time to optimize and polish hard solutions.

1b 2c is the default balanced choice that I recommend people. Of course there is a problem: Graphics also creates like 3 extra objects for every rectangle, but its lighter than sprite.

In my games I use 1c 2d just because I have enough experience to set it up fast.

 

 

Link to comment
Share on other sites

any change in zIndex or adding/removing sprites can trigger a sort once a frame. Nothing special, it just javascript sort is kinda slow :) Also zIndex never works for grandchildren, because it sorts only children of a container.

In general, I recommend not to create 500 sprites every frame (60 frames per second). Sprites are lightweight , there are like only 10 objects per sprite, and tens of thousand sprites in a frame are fine, but big per-frame creation just clogs the GC.

Link to comment
Share on other sites

One thing you may want to try and do is some more research regarding tile map engines. There are probably some GitHub repos for some. This way you can better learn how others are doing it. I have built tile map and scrollers a while back. The engine core itself was very different, since the engine was C++/OpenGL and specialized to handle it. Whereas in your case, you're using an existing engine and its methods to draw.

If you are still truly destroying/creating 500 sprites per frame, you should rewrite your algo now. You definitely want to have a sprite pool you render from. Think of the problem as paging in 1-2 rows or columns. For most cases, those are your deltas. Take the simplified case of moving in the X axis. In this case, as you move to the right, for example, you will eventually hit the point where you will need to page in a column to the right. You can achieve this by simply grabbing the leftmost column and rebinding the texture (no destruction or creation) and updating positioning. Note at this moment I'm ignoring z order. The trick here is your overall tile area needs to be larger than your viewport so one doesn't see either side pop in or out. Now much overdraw will be dependent on individual tile size and the max speed you need to scroll through your map.

Your "worse case" will be 2 dimension movement where it triggers both row and column swapping. However if you think about the problem more. You are no longer modifying 500 sprites. Rather in your case, about 150 sprites. Note in your code, you do far more than 500 sprites, you're doing 1250 sprites, but for consistency I'll use 500 as the high end number. This means that for the 350 other sprites, they should be "correct" and don't need adjustment.

Now you have to address z order. You could opt to re-build z-order each round. The problem area really are when you need to insert versus append/push, since depending on implementation, that will move memory (unless it's a linked list ... I have't looked at the code). If it is a linked list, then it should be relatively cheap, but based on Ivan's comment, it sounds like it is not.

One partial "cheat" you could do for draw order too is to have layer containers in your master container, and use that for your z. Note you'll still need to sort that. The other thing is clever art usage can minimize how much sorting is needed.

A thing to look out for is how non-world objects interact. So, for example, if characters can look like they are "behind"/occluded by tiles, you need to have a method to control that draw order. 

In short:

- Look for other tile engines and see if you can find some code to study. You want approach, engine type doesn't matter so much. You need to understand the basics of that type of engine more.

- Don't destroy/create per frame. Reuse. In general this is a good rule. If you find yourself creating and destroying objects a lot during normal runtime, you should scrutinize your technique. This is not to say you never can do it. I do have some games where I actually create and destroy on the fly but it is for a handful of objects on a custom C++ engine.

- Embrace object management. This is the unfortunate responsibility of the game dev. Sure you want to try and avoid it when possible, but world management ... in particular ones which incorporate a lot of animating visuals need to be managed.

Link to comment
Share on other sites

Very helpful tips, thanks! I changed the way rendering goes, now i create all sprites beforehand(a drawback is that loading is already noticeable, even tho no real sprites are used yet) and put most of them as .visible = false, works rather fine right now, might need reconsidering later. Also, as i said, i already did rendering based on current viewport position, it just didnt make it into demo because i thought it was mostly irrelevant

Link to comment
Share on other sites

Alright there is another issue, pivot seems to work weirdly(?) i need to set up rotation point in sprite while also having custom anchor, but it just doesnt work the way i expected

example: https://www.pixiplayground.com/#/edit/NGUb4kYSI8DXK6O4X5Jmz

bunny2 should keep its position, but rotate 90 degrees around pivot point (its centre) but it just does some nonsense

:: Apparently after rotation sprite uses original anchor to position itself, this is not ideal but i can work with it

Link to comment
Share on other sites

Anchor & pivot both affect how rotation happens. You could overcome this by having container and apply pivot point to it and then put the sprite with custom anchor inside it.

There might also be some way how to handle that with matrixes or without second container but my knowledge in pixi inner workings is not good enough to suggest anything.

Link to comment
Share on other sites

  • 4 years later...

In case someone search some answer in this thread one day,

Had quite the same problem while dealing with some isometric tile map. I use chuncks of 40x40 tiles, 58x30 pixel each. and I always keep 9 chunck (the one I am looking, plus radius 1 around). Problem is, how do you manage scrolls on the map ? Do you move sprites ? Do you delete them and create new ones ? Do you use some kind of pool of sprites, which you move and hide or show following your needs ? Do you just keep sprites where they are and change they texture ?

Here are some tests and performance (on each test, textures are always preloaded).

Option 1 (20 to 80 ms each frame) 
- Each time I need a new chunck, servers send 1600 tiles (40x40), client creates 1600 tiles from textures. When you scroll, the containers are moved using container.x += some value. When a chunck is not needed anymore because too far from camera, delete 1600 sprite and the containers they are grouped in.
 

Option 2 : (0 to 30 ms each frame)
- Almost exactly the same than option 1 except that I keep a pool of sprites. When a new chunck is created and need some tiles, it first search in a pool of sprites if somes are already available. If not, the algo create a sprite. When a chunck is deleted, the container is deleted but the 1600 sprites are added to an array containing my pool of sprite.

Option 3 : (2 to 4 ms each frame).
- I create a scheme of sprites (one per tile) which basically covers all of my screen. They never move, they are created once, and never deleted. Each time I want to scroll, I loop over each sprite and I search in some kind of data structure what kind of ground should be displayed in this sprite (land, shore, water). Once I have this value I just change the texture of the sprite using sprite.texture = "some value";

Fastest is to create sprites in the very beginning and just change their textures.

Hope it will helps some future game makers ! 

Link to comment
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...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...