2D Toolkit Forum
2D Toolkit => Support => Topic started by: eshan.mathur on August 08, 2012, 08:35:24 pm
-
Hey there!
I keep reading reports of great performance on iOS using 2D Toolkit leading me to believe I'm doing something incredibly performance intensive without knowing it, but I was hoping someone could see my problem. Basically all I'm trying to do right now is generate a grid of tiles with a texture on them. Each tile is 16x16, so on a retina iPhone there are 60x40=2400 tiles on screen. Kind of a lot, but hey. The simple goal here is to be able to drag my finger and make tiles that I collide with disappear, kind of like 'cutting' a path through them. There's one more important bit: the tiles all move. The camera itself (and I'm using a TK2D camera) moves to the right, and as it does so columns of tiles are all 'treadmilling' - i.e. disappearing from the left and reappearing on the right, giving the illusion of sidescrolling. As I "cut" through tiles, they get turned to inactive. Once they get treadmilled, they're turned back on. Each tile has a collider on it that I use to help out with that 'cutting' detection and that I'll need for collisions later.
In other words, all that happens is:
- Simple for loop generates a grid of TK2DSprites that cover the iPhone 4 screen.
- Move the camera at a steady rate.
- Move tiles that the camera moves past to the front of the dirt grid.
What's bugging me is that even if I turn colliders off completely on the tiles, the framerate on an iPhone 4 is atrocious. I don't know what it is because I haven't put in a framerate display script yet, but I know it's probably lower than 10 FPS. My question has a few parts:
- Is the very fact that I'm moving the camera over 2400 tiles at once the root cause of the problem?
- If so, what is a better way to approach this? Should I move the tiles instead of the camera, etc?
- Are there any texture or sprite or sprite collection optimizations I need to do that are obvious? I just followed the tutorial - I have one sprite collection with one sprite in it that I use for the tiles.
- Will those colliders add a whole new layer of complexity to the problem or do I not really need to worry all that much about those?
Thanks!
-
Hey,
A few things to work out whats going on.
1. First, replace the tiles with the default Unity box. Do you get performance issues then? I suspect you would, as the tk2dSprites are just plain meshes there. If not, that implies that something could be wrong with the dynamic batching, etc.
2. If you have Unity pro, just turn on the profiler and see what is taking up the most time. It should be obvious even when running on Mac. You obviously have a pro license on the mac anyway, so you could try that. Also, if you don't have iOS Pro, you can enable the profiler in one of the .mm files in xcode, can't remember which one offhand. That'll tell you whats taking the most time.
3. These aren't animated sprites are they? Animated sprites do take quite a bit of time to update, so having a lot of them all animatiing at the same time could be bad - there are ways around this.
4. I dont know about the colliders - test #1 and #2 will tell you if the colliders are the slow bits.
Let me know what you find and we'll take it from there.
-
I'll test those out within the hour and let you know. Quick side question: I'm having trouble getting a text mesh to show up. It's Z-value is above that of the tiles and according to the camera preview it's positioned properly, but it won't show up. Any ideas? I wanted to make a quick FPS counter.
-
Is it not showing up when running in game? Or is it broken in the viewport too? Its hard to tell what has gone wrong there from your description, any number of things could be wrong.
-
Just kidding. I experimented in scene view and it looks like the TK2D camera preview isn't accurate or scaled properly at all. I had to move and scale the text mesh in Scene view to make sure it was the right scale and position for the camera. Let me know if there's a better way to do that.
-
Are you on 1.76 final + patch1? That works around that Unity bug that incorrectly draws the preview window. If not, I suggest updating :)
-
I'll look into that! I'm trying to implement your first suggestion - how would be the best way to go about replacing sprites with native Unity objects? Should I use a plane with a texture on it?
-
just a standard untextured box will do. Make it the same size as the sprite and you're good to go.
-
I'm trying to do it with a plane but running into really strange rotation issue. I suppose I'll try it with the box!
-
So I ended up actually trying it with something more efficient than all - a simple plane rendered out of maya composed of two triangles. That's it. I ended up getting my tiling and cutting code to work with it. It's actually marginally better than 2D Toolkit - I get 5k vertices and 3 draw calls vs what I was getting using TK2DSprites (10k verts and 8 draw calls), but the framerate on a phone is still around 4-7 FPS. Really bad :( Also, these are not animated sprites.
Agh! I really need this to work smoothly. What are some next steps?
P.S. I will update to the newest version as soon as I get approval for the usergroup.
-
Updating to the newest version isn't going to help, as this test has proven it isn't the geometry rendering, etc. To narrow down the cause of this sensibly, the best option is to start doing those profiler tests I mentioned earlier.
-
Okay I finally figured out how to hook up the Unity profiler to the game while it's running on the phone. Basic results were basically this from CPU, with colliders on:
Avg Frame Rate: 4-5 FPS.
Physics.Simulate - 65%-75% Total Usage, 114ms-175ms.
Camera.Render - 25-30% Total, 50-90ms.
The rest doesn't compare to those two all that much performance wise. I then turned off colliders, and noticed at least some improvement in performance:
Avg Frame Rate: 12-15 FPS.
Camera.Render - 93% Total, 55ms
Physics.Simulate barely registered.
Obviously the colliders are a problem but I will screw around with them in a bit. The much bigger problem is that I'm not even able to render and move a grid of 60x40 physics-less tiles on an iPhone 4 at a rate better than 12 FPS. I've also tried locking Unity's Application.targetFrameRate to 30, but no noticeable improvement.
Any thoughts on why it's going so slow?
-
Well with camera.render taking 55 ms the most you're going to ever get is 18fps so what you're seeing is expected. Now we need to work out why its taking so long to render and what specifically in camera.render is taking up all the time. Does this not expand any further?
-
Sorry! I didn't look into the breakdown. Basically, most of it is being taken up by Render.TransparentGeometry and other alpha based operations. I'm not sure what they all mean and I'm sure you have a better idea, but if my sprites are all using opaque textures and shouldn't have any transparency to them, why is there so much work being done on transparent geometry? Is the map not being generated properly?
Here's a screenshot:
(http://dl.dropbox.com/u/27137/profiler.png)
-
Render.TransparentGeometry hasn't got much relevance to the gpu performance, its just the path Unity uses for these. The default materials in 2D Toolkit are transparent, hence them appearing in this stage. Transparent materials will affect gpu performance, and this is easily sorted. But this isn't the issue you're seeing here. To eliminate it, switch to your maya object test, and replace the material with a newly created untextured one.
Culling is taking a lot of time followed by actually rendering the meshes. In this case, it looks like the main overhead is dynamic batching (and drawing) all these objects. What exactly do you do with these sprites? Can you send me a webplayer or something so I will have a better idea of whats going on? If possible, send to support@unikronsoftware.com.
In any case, it looks like the overhead is caused by the sheer number of sprites. This can be sped up in one or more ways, the best way is really dependent on your needs here.
-
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.
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.
-
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.
-
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