So I’m working on a 2D project that I’ve spent almost a year on now with some friends. We just started coding recently and I had to design a shader and logic that would allow me to blend tiles together to get a smooth change of textures. It’s a pretty complicated pile of code but basically it converts some arrays of integers that represent the tile data into actual parts to draw (the goal being that we can rotate the view eventually even though it’s 2D, by being able to render it while facing any one of the cardinal directions (i.e. the top of the screen can be West if we rotate it clockwise, and all buildings and heights will be drawn from the new perspective).
Anyway, because of all the computation the framerate isn’t perfect, and I’m looking for ways to make it better. I have reduced the amount of new objects I’m defining each Update() to as little as possible, choosing instead to move existing rectangles and adjust existing Vectors.
I think this has helped, but I’m looking for any kind of ideas that will help me make it quicker. The tiles in our game are 64x64 but each one is made up of nine parts (plus additional parts for cliffs, fauna, etc). So if you can imagine, there are about 30x17 tiles on a 1920x1080 monitor, and I’m drawing averagely ten parts for each one of them. Plus characters, trees, particles, etc. The world is sent to the player from the server in chunks of 64x64 tiles.
My process (in pseudocode) works like this:
Update phase:
Build an array of only the tiles that will be drawn (retrieved from the world’s chunk data)
Calculate edge types for all cliffs (lots of ifs here, but they only go 3 ifs deep)
Based on the edge types determined, determine drawing rectangle sizes and locations (accounting for height and things being behind each other)
Drawing data is saved in rows, along with a List of heights that appear in the row.
Draw phase:
Each row’s tiles are drawn in order of height, lowest to highest, so everything shows up covered correctly by things in front of them (sometimes I am drawing tiles that get drawn over if they are hidden behind a higher tile that’s closer to the bottom of the screen). This is done in a for loop that starts at the lowest indexed height in the list, loops through the row and draws those tiles, then chooses the next higher height in the list, repeat, until the row is done.
My drawing loop is pretty tight at this point so I don’t think I can save processor time here:
Ignore that, you can’t do that, re reading your code, that’s not possible.
I think you need to restructure your data model so that it would make this possible, maybe put a flag in the PartDrawingData class so you can differentiate between the two, you can then store them in one list, or combine them into one, like this:-
In my game, I have a a collection of objects with X and Y coordinates already figured out. I only have to loop through that collection and I can say if X and Y are in the visible range draw them, otherwise skip them.
It also looks like you’re setting values every loop that probably only need to be set when they change (like if the mouse is over the tile). Keep track of the previous tile you were over and when the mouse changes position find the tile and update it.
I like that idea but I’d still have to iterate the list twice, because the cliff pieces can’t be drawn until all the base pieces are drawn (or else you get funky effects where the tile’s texture is drawn over cliff pieces sometimes). It’s because of the tile blending - when there’s a cliff edge, I have to draw the base texture for both the top and bottom of the cliff first, and then draw the cliff edge over it for the correct drawing order. So basically I’m drawing all the base textures in the row first, then all the cliffs, and theoretically then any tile deco in the future.
Yeah, I wish I could do that but since the world is much bigger than the screen I’m basically only drawing a segment of it at any given time by comparing the player position to the values I determined for how many tiles can fit on their screen, etc. So in a way I’m kind of doing the same thing. But you reminded me of something correct, which is that the tileData retrieved from the world only needs to be updated when the player actually changes tiles, not every frame, so that will definitely help. The draw data still has to be updated every frame but not the tileData. Thanks
OK, I see, can you not render the depth of those sprites above the ones underneeth? Trying to think of ways you can do it without re designing your data model, but I think you will have to, because it’s not going to perform well like that.
How do you mean? Are you talking about the depth parameter that gets passed with the Draw? If so I don’t know much about using that, I tried in the past once and it came out all glitchy so I shyed away.
Edit: But if it’s time for me to learn how it works then it’s time
OK, well, working in 2D is the same as 3D, so you still have depth. you can still apply depth to textures, draw order is still important due to alpha blending though I guess.
I think the glitches you may be seeing are either due to bad draw call order with the alpha blend, or z fighting. Probably the former as you are alpha blending.
I think you need to re think your data model, it the very least, instead of itterating throuhg ALL the tiles in the row again, just get the ones you need to render.
If it was me, and, naturally I don’t know ultimatley whay you are rendering, I would do it all in a pixel shader, have one texture passed to indicate what’s to be rendered at each “tile” pass it either a set of texture atlas or texture arrays and have the shader do it all in one draw call to a massive quad.
This may not be possible due to your target platforrm too I guess Sorry I could not help
Target Platform: Windows only for now (possibly android in the future)
If I’m understanding what you’re saying right, I think that’s what I’m doing now. I wrote a shader to which I’m passing in those variables, and the shader allows me to blend textures together, but there’s really only one texture2D being used for all the tiles (it’s big and divided up into tiles as normal).
Is that what you are saying? I’m probably not as advanced as you.
Also, some of the comments above made me wonder: would it be faster to draw all the tile stuff to a different renderTarget and make a texture2D of it and just reuse that texture until the player changes tiles again? (And then not forget to dispose of it :p)
I’ll post you a screenshot later on today so you can get an idea of what’s going on; I’m working out some visual bugs atm since I completely rewrote the drawing code the other day.
I’m also going to look into using that depth stuff you speak of because that sounds like it could easily and totally allow me to get rid of that second loop.
By the look of your code, you are making a draw call per tile, that’s not what I am saying, say you had a map 1024x1024, that would be a lot of draw calles to draw each tile. So would be better to send a 1024x1024 “map” of the tiles values to the shader each pixel being 0-1, a pixel of 0 will get the shader to render texture 0 from the atlas or texture array, a value of .5, will render the midle texture in the array/atlas and so on.
The tile map would also be fully dynamic at runtime to, just alter the map, and the shader does the rest, this could be layered too for water, snow and other weather effects.
Not sure it would work too well on Android though…
Well, my Virtual World is 16000 x 12000. I’m not using “Tiles” in the traditional sense since any object can be placed anywhere within that 16000 x 12000 (or any size, just want to keep the world “Medium” sized). I have a World Offset so I say if X >= WorldOffsetX and X <= WorldOffset + ViewPort.Width and X >= WorldOffset and Y <= WorldOffsetY + ViewPort.Height DrawObject.
That’s an over simplification as I would take the width of the object into account as well.
I also have Multiple layers that I support by having objects in different collections.
So I have something like:
List BackgroundObjects
List NormalObjects
List EnemyObjects
List PlayerObjects
Etc… So I draw them in specific orders and it optimizes collision detection etc…
Ok, thanks for the help and ideas! I don’t really know much about the atlas but I will look into it. That’ll be my goal for tonight, lol.
I’m still working on cleaning my math up a little (I wrote it when I was super tired) but here’s a shot of the game in its current state (I am currently not drawing the drop cliffs so I can make sure the stuff behind raised areas is looking good.
Just as an aside to all the good advice here. It can be hard to improve performance if you don’t measure what parts of the code are slow. Use Stopwatch to time the different sections of your code and figure out which parts are actually slow, then focus on making that faster.