Hello Guest

Author Topic: Poor Performance when moving a grid of sprites?  (Read 18181 times)

eshan.mathur

  • 2D Toolkit
  • Newbie
  • *
  • Posts: 14
    • View Profile
Re: Poor Performance when moving a grid of sprites?
« Reply #15 on: August 11, 2012, 04:25:43 pm »
I'm not sure I understand what you're recommending in your first paragraph - moving away from 2D Toolkit and using just simple quads as I had tested with? Seems like I'd lose important functionality that 2D Toolkit would provide.

I can describe it for you but a webplayer will have to wait a bit. Essentially there is a grid of 16x16 tiles (TK2DSprites) with a simple texture on them. They fill up the screen, forming a grid of size 60x40 (2400 tiles) on a Retina iPhone screen.

The important piece here is that the camera is moving at a fixed pace (i.e. sidescroller) to the right. Accordingly, the tiles are "treadmilling" to keep up. In other words, as the camera moves 16 pixels, the last column of tiles to go off the left side of the screen is simply moved to the right. This creates the illusion of an endless grid of tiles. There isn't much more happening there than that.

Code: [Select]
void spawnInitialTHINGY() {
for (int i = 0; i < (initGridWidth + numTHINGYColsTreadmillAtOnce+1); i++) { // 2 screen-lengths of THINGY tiles
List<tk2dSprite> THINGYCol = new List<tk2dSprite>();
for (int j = 0; j < gridHeight; j++) {
tk2dSprite temp = Instantiate(THINGYTile, new Vector3(THINGYSize*i, THINGYSize*j, 15.0f), Quaternion.identity) as tk2dSprite;
THINGYCol.Add(temp);
}
THINGYTileCols.AddLast(THINGYCol);
}
}

// Update is called once per frame
void Update () {
float dt = Time.deltaTime;
float cameraMoveX = scrollSpeed*dt;
cameraMoveX = Mathf.Round(cameraMoveX * 100f) / 100f;

treadmillTHINGY(cameraMoveX);
treadmillBacking(cameraMoveX);
camera.transform.Translate(new Vector3(cameraMoveX, 0, 0));
}

private void treadmillTHINGY(float cameraMoveX){
THINGYTileMoveCounter += cameraMoveX;
// if camera traveled one THINGY col's width, treadmill leftmost col
if(THINGYTileCols.Count != 0){
if(THINGYTileMoveCounter >= xDistanceToTreadmill){
float leftOverTHINGYX = THINGYTileMoveCounter - xDistanceToTreadmill; // how far past the desired distance did the camera go?
xDistanceToTreadmill = THINGYSize*numTHINGYColsTreadmillAtOnce - leftOverTHINGYX; // set next desired distance before treadmilling again

List<tk2dSprite> firstCol = THINGYTileCols.First.Value; // leftmost THINGY col
List<tk2dSprite> lastCol = THINGYTileCols.Last.Value; // rightmost
float firstColX = firstCol[0].transform.position.x;
float lastColX = lastCol[0].transform.position.x;
// treadmill THINGY tile col(s)
for(int i = 0; i < numTHINGYColsTreadmillAtOnce; i++){
foreach(tk2dSprite currTile in firstCol){ // treadmill all tiles in col
currTile.transform.Translate(lastColX-firstColX + THINGYSize, 0.0f, 0.0f);
currTile.renderer.enabled = true;
currTile.collider.isTrigger = false;
}
THINGYTileCols.AddLast(firstCol); // move col to back of linked list
THINGYTileCols.RemoveFirst();
firstCol = THINGYTileCols.First.Value;
firstColX = firstCol[0].transform.position.x;
lastCol = THINGYTileCols.Last.Value;
lastColX = lastCol[0].transform.position.x;
}
THINGYTileMoveCounter = 0.0f;
}
}
}

Each one has a collider on it used for cutting, but that entire process can be ignored for now probably because drawing itself is taking up so much processing power. If you want to know, I basically raycast at every touch and see where I'm colliding - I then use Physics.OverlapSphere to grab the tiles around that tile and then I turn their renderers and colliders off. When a column is "treadmilled" to the right, I turn everything back on. But, as I said before, even with colliders off the thing lags like nuts.

How can I optimize dynamic batching and drawing itself? I'm not entirely sure what culling is (I think it's the removal of objects that are not in view from being rendered/processed?) but are there ways to optimize that as well? I wonder if moving the camera as opposed to moving the tiles under a fixed camera is causing the issue?

P.S. I've sent a webplayer to your support address.
« Last Edit: August 11, 2012, 04:45:29 pm by eshan.mathur »

unikronsoftware

  • Administrator
  • Hero Member
  • *****
  • Posts: 9709
    • View Profile
Re: Poor Performance when moving a grid of sprites?
« Reply #16 on: August 11, 2012, 07:29:33 pm »
OK. I see what you're doing with this, and this isn't the way to go about it. There are just too many instances which need to be processed - you'd get the same issue with the same number of planes or indeed any other GameObjects in Unity. Unity can handle 2000+ objects no problem, but having 2000 small objects visible is simply doing a LOT of work, even more so for a general purpose 3D engine. Unity does all the usual stuff any 3d/2d engine would do, it tries to work out what objects are outside the view (viewport culling), sorting (all of these are transparent objects by default, so will need to be sorted by z to draw in the correct order), and in Unity's case, it needs to be dynamically batched.

With just those 3 things, its obvious why its taking such a long time to do what seems to be a trivial case.
- All of those boxes are always visible, so culling isn't necessary
- The objects are all coplanar - sorting is unnecessary
- These objects don't move (only the edge ones move) so no need for dynamic batching either.

You can't just turn these off in Unity (not as far as I'm aware anyway), so the best bet here is to roll your own.

The trick will be to get it fast enough, and minimizing the amount of work you need to do. You can pretty much get around #1 #2 and #3 by creating a custom mesh. Look at the static sprite batcher for an example of how to merge a bunch of sprites into one mesh (ignore all the complex bits where it sorts sprites, material handling, etc. as mentioned above your case doesn't require any of that). I suggest not creating a full mesh for EVERYTHING, but rather, merge one vertical strip into one batch. That in one swoop will reduce your GameObject count from 2400 -> 60. Now you can concentrate on writing a bit of code to modify the 40 tiles efficiently. If you ensure that you don't use the custom geometry / dicing for these sprites - updating mesh data will be trivial. You won't need to regenerate the indices every frame, only position & uv data will need to be updated, and only when the contents of that particular strip changes.

Hope that helps.

eshan.mathur

  • 2D Toolkit
  • Newbie
  • *
  • Posts: 14
    • View Profile
Re: Poor Performance when moving a grid of sprites?
« Reply #17 on: August 14, 2012, 07:16:01 pm »
So I looked through the class and it seems like a lot of work for the scope of the project I'm going for - not to mention I don't really understand how your sprite batcher works. I ended up just settling for 32x32 tiles and that got my framerate up to 30 on the phone, even scrolling at 500px/sec and cutting all the while. That'll have some negative impacts on how my game operates but I can think of a few hacks to alleviate the problem.

I appreciate all your help, at the very least I understand what's going on behind the scenes in Unity/2D Toolkit just a bit better. You have a great product, and thanks for supporting it so well!

Eshan