Drawing Depths from Control Object

G

Greg5000

Guest
So I'm making an isometric game and have recently found that my frame rate is suffering and a big culprit is probably the number of vertex batches being sent to the GPU. So I have set all of my objects to be drawn by a single draw control object as the buildings, trees etc. are drawn using a set colour based on the time of day/night. This is working fine, however now my depths are all messed up, all objects draw with the depth = -y code which achieves this fine, however now I think all of the sprites are being drawn at one depth, the depth of the draw control object. I am using 'with' to draw the sprites relative to the object, how can I get them to draw at the depth of the object?

Thanks.
 
Taking this approach, you may need to get the depths from each object, store them in an array, set depth, draw, set depth, draw... iterating through the instances. However, the Game maker authors specifically warn against changing depths in the draw event.
 
M

Multimagyar

Guest
So I'm making an isometric game and have recently found that my frame rate is suffering and a big culprit is probably the number of vertex batches being sent to the GPU. So I have set all of my objects to be drawn by a single draw control object as the buildings, trees etc. are drawn using a set colour based on the time of day/night. This is working fine, however now my depths are all messed up, all objects draw with the depth = -y code which achieves this fine, however now I think all of the sprites are being drawn at one depth, the depth of the draw control object. I am using 'with' to draw the sprites relative to the object, how can I get them to draw at the depth of the object?

Thanks.
If you insist to draw it from a single controller a priority list would be necessary to determine a certain set of order. You can't really force an object to draw on different depths in the draw event. Imagine it like a painting. you don't first paint a bird and then you paint a background behind that on the same layer.
 

Yal

šŸ§ *penguin noises*
GMC Elder
You could use 3D functions in 2D mode to draw stuff at different Z values, I guess. In 2D mode, there's no perspective effects, so no matter the Z value, things will be drawn at the same size. If you go with the priority queue approach, I'd try to sort the queue in the step event, or only when something is created or destroyed... if nothing changes, you don't need to waste CPU time re-sorting it every step.
 

ph101

Member
@Greg5000 I have this same problem and have not found an ideal solution. I guess the priority list may be the way to go.The reason I'm trying to do this is I want to group together objects of different depth but draw them all in row between batch breaking functions like colour set or d3d transforms. Did you find a solution to this one? Making the priority list sounds potentially slow however maybe possible. But it also doesnt really work if you are drawing other objectss with depth that arent in this system in your isometric space... so every object would need to be sorted this way... then you have one object and draw them in order from the back, and it won't matter what depth the object is...

edit. I'll tell you though, my solution is likely going to have to be (unfortunately) - don't use pipeline breaking functions! So I'm making sprites with any blends or effects in place.

Edit2.
 
Last edited:

ph101

Member
@Greg5000 @Yal Even if you created a priority list to draw with one object from the back, you couldn't really avoid breaking the draw pipeline if some things needed an "effect"/blend mode/alpha/colour change and others didn't: because a tile/item in your isometric space nearer the front may need one effect/pipeline breaking function, and another near the back might, you couldnt actually batch that because by necessity in this system you are ordering the draw calls from the back, so would need to perform tha batch breaking function twice. Unless I'm missing something, the fact you can't or shouldn't alter depth per step makes this an intractable "problem" in GM?

When I have attempted to alter the depth of the drawing object, per step, while using 'with (object_name) {scr_draw_stuff}' to automatically iterate through every object and perform the draw, it doesn't work as expected/makes inexplicable depth decisions as @Multimagyar you have nicely explained why. I haven't looked at the 3D functionality but surely the same issue would exist. Hence my sprite route. Open to suggestions though by all means.
 
Last edited:
A

anomalous

Guest
Can you use tiles for most of the environment (floors, walls, trees, etc.) instead of objects?
If only a small set of objects is needed for say, doors, and interactive objects, this may move it down to not being an issue?

That scares me, I probably never use draw_sprite!
 

ph101

Member
Thats an intersting question. I'm not at all familiar with tiles, though perhaps I should be. But I presume that the drawing of tiles falls into the same pipeline and that the same issue exists in that you would need to interrupt pipeline at a specific depth for that item in the isometric space before continuing drawing as normal, however you prioritise drawing by depth (ie using GM depth per object/tiile or sort draw order from back and draw using one object.)

edit.. You have inspired me to look closer, though I dont think it solves issue from OP - because I use an instance per isometric grid space for the floor, as it were, which takes up lots of CPU, I guess could supplant it with a tile instead. For sure th GM isometric tutorial doesnt suggest what i'm doing, it has one object drawing everything per grid from the back I think.. But it's whatever suits what your game is trying to do I guess, there was a thread on this on the old forum too..
 
Last edited:
A

anomalous

Guest
I have a ton of questions but I think its out of a desire to understand if this cannot be solved...so maybe I'll check out that tutorial rather than derailing.

Floors to me can done in tiles, its the wall objects that you have to be able to walk behind in isometirc (and trees, etc.) that cause the grief.
I'd think though that if one were able to do floors as tiles (a single lowest depth), and walls/scenery as tiles (depth-y for any you have to walk behind) and to color them post using a hue shader, it couldn't be slow...but talk is cheap and its not my time investment on the line! There are other concerns like the room size, and too many tiles can bog down a room too, etc., so take all that with a grain of salt.

Emulating 3D with 2D is so hacky its frustrating...makes one want to try 3D with fixed perspective!
 

CedSharp

Member
In GameMaker, tiles and objects share the same "layers".
It's just that by default, GameMaker assign a huge depth to tiles ( 100,000 if I recall ).
If you want to be able to go behind a tile, set it's depth to say something like 20.
Then change the dept of the player object as he moves, above 20 to go behind the tile, under 20 to go in front :p

If you setup your tiles correctly, the only depth you'll need to change is the depth of moving instances, like player and enemies.
Hopefully this will help you figure it out.

So I'm making an isometric game and have recently found that my frame rate is suffering and a big culprit is probably the number of vertex batches being sent to the GPU. So I have set all of my objects to be drawn by a single draw control object as the buildings, trees etc. are drawn using a set colour based on the time of day/night. This is working fine, however now my depths are all messed up, all objects draw with the depth = -y code which achieves this fine, however now I think all of the sprites are being drawn at one depth, the depth of the draw control object. I am using 'with' to draw the sprites relative to the object, how can I get them to draw at the depth of the object?

Thanks.
If you want to use a controller to draw your sprites, you cannot use depth. Depth is what gamemaker uses to decide who to draw when, but when you call with() from an instance to draw all objects, you actually draw all of them
from one draw event, so from only one depth.

Maybe you could create a list, call with() to add each instance you want to draw in that list, then sort the list using whatever you want to decide who's behind and who's in front ( for example the y variable ),
and then draw them in order with a loop.

Otherwise, I suggest using the tiles option above :p

Regards,
CedSharp
 

ph101

Member
Maybe you could create a list, call with() to add each instance you want to draw in that list, then sort the list using whatever you want to decide who's behind and who's in front ( for example the y variable ),
and then draw them in order with a loop.
Though as I mentioned this won't solve the interrupting draw pipeline issue unless all instances have the same blend/effect applied to them. And if you arent doing that, it's questionable what drawing using 'with' would acheive, I am guessing very little.
 

CedSharp

Member
I made a small example of what I meant using the tiles: get the .gmz here ( mega.nz )
Here is a screenshot before you download:


You can move using arrow keys, you are the little ball.
You can go behind/in front of walls, as expected in isometric games.

I am not calling with() nor did I change any draw event ( in fact the only draw event is for the debug text in the top left corner )

Since there is only 2 objects in the room, you have no performance issue.
And I use a grid for collision checking, so no sprite mask calculation or whatsoever is needed :p

All I do is create tiles on the appropriate layer, and change the player's depth when he moves.
So all blending should work as expecting since I'm not touching the draw pipeline at all.

unless I misunderstood something...

Regards,
CedSharp
 

ph101

Member
Nice job! :) But it's not I that I am denying you can draw an iso grid with 1 object or using tiles. My point was that if you wanted to make say a cube at the front and a cube at the back draw with a different blend mode (which interrupts draw pipeline), you would need to do it when drawing each one right? You have to turn it on and back off in each case when drawing whatever is at that coordinate. Unlike, say, a pure 2d game where the draw order is less important, you can't change your blend mode, group your 'items' or coords that need that blend mode together, and draw them together then reset your blend mode (which would mean you could draw 20 things with you rblend mode with only one switch), because then you lose the depth ordering that the isometric projection relies on. Because the one at the back needs to be drawn first (or have lowest depth), the one at the front last (or highest). If you see what I mean? I'm not sure if thats what OP was asking exactly though.
 

CedSharp

Member
Nice job! :) But it's not I that I am denying you can draw an iso grid with 1 object or using tiles. My point was that if you wanted to make say a cube at the front and a cube at the back draw with a different blend mode (which interrupts draw pipeline), you would need to do it when drawing each one right? You have to turn it on and back off in each case when drawing whatever is at that coordinate. Unlike, say, a pure 2d game where the draw order is less important, you can't change your blend mode, group your 'items' or coords that need that blend mode together, and draw them together then reset your blend mode (which would mean you could draw 20 things with you rblend mode with only one switch), because then you lose the depth ordering that the isometric projection relies on. Because the one at the back needs to be drawn first (or have lowest depth), the one at the front last (or highest). If you see what I mean? I'm not sure if thats what OP was asking exactly though.
Oh I see what you mean now.
You mean that each "batch" must be drawn in seperate "batch" when you change blend mode, and because we need the previous ( bigger depth ) items to be drawn first in order for
blend to work correctly, we can't "batch" all items with same blend mode together. I think I understood what you meant now.

Well there is something I don't understand right now... how in the hell would sending a couple of 2d blendng stuff to the video card lag your game then?
I don't think the problem comes from the actual blending problem. In such case I would think it actually comes from the way you draw stuff or the number of
objects ( this is my current prefered hypothesis ) running in the room at the same time.

If you run this: draw_text( 16, 16, string( instance_count( all ) ) )
how many instances are in the room?

If the number is higher than 60, you're game is not optimal.
If the number is higher than 100, you're game is currently running slower than it should
if the number is even higher, than something's wrong lol.

Maybe you could draw static stuff on surfaces? This could potentially remove a lot of drawing/blending from each render loop?
For instance, a shadow from a wall doesn't need to be rerendered all the time, try rendering it on a surface and wait until "something" makes
the shadow change. this is the only alternative I can suggest :/

Regards,
CedSharp
 

ph101

Member
Yeah that was what I was getting at. It was just a sort of side point really, but you raise a good point on number of objects no doubt.
 
A

anomalous

Guest
ph101, I think if that's true about blend modes and it destroying the frame rate, just use different tiles, one transparent for example, and one not.

I think if you detail the specific ways you use it, people can probably come up with some easy work arounds using tiles/drawn graphics.
Dynamic lighting may be trickier...
 

ph101

Member
@anomalous thanks. To be clear it's not that it's destroying the frame rate, it's just I can see the number of vertex batches in the debug overlay double when I apply, because as you correctly guess we are talking about a sort of dynamic lighting. In this case, I want to make a shadow per sprite. To do this I'm simply drawing the sprite, sheared at an angle, with a black blend and 0.5 alpha. I'm using a script from the ever excellent GMLscripts.com to do this:
draw_sprite_shear(sprite,subimg,x,y,xshear,yshear)
So the issue is that this uses d3d_transform_stack_push(); which evidently breaks the draw pipeline with each sprite and I so I came to realise above that there is basically no way to avoid this if you need to apply such an effect per depth per sprite. So I have decided that either I have to take the performance hit, and put it as an option, or draw a shadow in the the sprites manually with an alpha shadow beneath it. The latter being a shame because then I can't rotate the shadpow with the day night cycle. The game in question is likely to have dozens of such sprites on screen at any one time, each are individual agents, so it all becomes quite GPU (and CPU) intensive. If there were a way around it I would be happy to hear it but I'm pretty sure there logically can't be.. These sprites move on a floor which are currently objects though, so as @CedSharp says, if I cut down objects, and maybe supplant them with tiles, it could help in general on the CPU side at least.
 

Juju

Member
You could use 3D functions in 2D mode to draw stuff at different Z values, I guess. In 2D mode, there's no perspective effects, so no matter the Z value, things will be drawn at the same size. If you go with the priority queue approach, I'd try to sort the queue in the step event, or only when something is created or destroyed... if nothing changes, you don't need to waste CPU time re-sorting it every step.
Using the GPU's depth buffer to "sort" opaque sprites (i.e. 100% and 0% alpha only) is possible. When you want to alpha-blend, things get more complicated because draw order matters. You can render yourself a depth surface in a similar fashion to 3D shadow mapping and then apply alpha blended effects in a second pass, stencilled by the depth surface. This is a rather involved process.

It can also help to commit static geometry to vertex buffers.
 
Top