Delta timing and tile scrolling

BlitzMax Forums/BlitzMax Programming/Delta timing and tile scrolling

hub(Posted 2007) [#1]
Hi !

To Games coders gurus...

I use delta timing into my code. This works fine but, i've a pb with my smooth vertical 32x32px tiles scrolling.

Display tiles at _floating positions_ (scrollY# = ScrollY# + Delta# * ScrollingSpeed#) produce some tiles overlay or an 1 px blank line between two tiles rows (see picture). Do you think that i must choose another timing algorithm (avoid delta timing) to scroll the tiles at integers positions ?

This Screenshoot illustrate my problem (image heavy, so follow the link) :

Click here!

Many thanks for you help !


GfK(Posted 2007) [#2]
Its probably a float to int rounding problem. Try using floor(y) when drawing tiles, see if that helps - it'll force everything to round down to the nearest int, rather than just going to the nearest whole number.

[edit] Alternatively, calculate delta * scrollingspeed only once, then add the returned value to all your y coordinates, rather than calculating the whole lot for every individual tile (this'll give you a minor speed increase anyway)


hub(Posted 2007) [#3]
floor not fix the pb. Is someone have noticed the same pb with an floating tiles scrolling ?


tonyg(Posted 2007) [#4]
Scrolling with floating point coords
Aren't you Hub?

<edit> Also some talk here about Mesh code for Tilemaps.


hub(Posted 2007) [#5]
it's me. i have always my same pb ;-( Perhaps mesh code is the solution. i hope i've a day the skill to find how to code this solution.


tonyg(Posted 2007) [#6]
How about asking Tim or checking if it'll be in GA's Framework?


TartanTangerine (was Indiepath)(Posted 2007) [#7]
I'll get around to it sometime soon. I've got a contract to finish first though


_33(Posted 2007) [#8]
It's useless to use floats, from my 2D in 3D discoveries, as the alignments is still as precise in ints as it is in floats. PLus, you might save some CPU.


Gabriel(Posted 2007) [#9]
It's useless to use floats, from my 2D in 3D discoveries, as the alignments is still as precise in ints as it is in floats. PLus, you might save some CPU.

On the contrary, it's useless to use anything =other= than floats with delta time. What's the point of a delta multiplier if you're only able to judge it to the nearest integer? It's like measuring an atom with a yardstick. Even if you're not drawing at sub-pixel coordinates, you should still be calcuating at sub-pixel coordinates. And if you can notice the time difference between a handful integer multiplications and a handgful of floating point multiplications, there's something seriously wrong with your code.


Grey Alien(Posted 2007) [#10]
It is possible to do integer scrolling on tilemaps but as Gabriel says the internal values must be floats and then rounded to ints. You shouldn't get gaps unless something is going wrong with your code. Integers won't look that smooth though. Floating point will be smoother but will have "artifacts" on the edges of the tiles until I (or Indiepath or someone else) sorts out some easy to use mesh code.


MGE(Posted 2007) [#11]
"Integers won't look that smooth though." Worked fine on the Nintendo, Genesis, ST, Amiga, etc. ;)

I doubt mesh code will be something for general use. A mesh may work best for static, non dynamic maps. But if you want animated tiles, different alpha states, texture states, etc, etc, it may be quicker to just do what we're doing now. Plus I don't think a mesh will solve the artifacting either. If your top left destination pixel is a float then all tiles in the mesh will also be plotted at floating point values causing the artifact. I've experimented with both integer and float based map rendering, to be honest, I'm not seeing enough of a problem to worry about either method. In your (GA) map test, I've ran it on several computers and it looks fine.

The "artifacts" we talk about is simply the 3d card performing smoothing on the edges of the tile when rendering to floating point destinations.

In this example, I've faked a mesh render by placing tiles inside a 512x512 texture and then rendering this texure to the screen. In other words, the tiles are not individually rendered, the entire texture at once is rendered. Similar to how a mesh would render.

http://jgoware.com/mge/misc/tiletest.zip

In the demo you can press F to alternate between float/int based rendering. As you can see, with floating point rendering, you still get artifacting. While mesh rendering will indeed speed up certain game types, it will not solve the artifacting issue.

If you want pixel perfect tiles, sprites, etc, render to int coordinates. I would use this method in typical arcade platformers, etc. Other games like space shooters, etc, without tiled backgrounds may look better rendering to floats. ;)

SetGraphicsDriver D3D7Max2DDriver()
'SetGraphicsDriver GLMax2DDriver()
Graphics(640,480,32)
'
Global x:Float=0
Global fmode:Int=1
Global tiles:TImage = LoadImage("tiletest.png",FILTEREDIMAGE | MIPMAPPEDIMAGE)
'
HideMouse()
'
SetColor(255,255,255)
SetTransform(0,1,1)
SetAlpha(1)
'
While Not KeyHit(KEY_ESCAPE)
 '
 Cls()
 '
 If KeyHit(KEY_F) ; fmode = Not fmode
 '
 If fmode
  DrawText "(F)loating Point On",0,0
  DrawImage(Tiles,x,50,0)
 Else
  DrawText "(F)loating Point Off",0,0
  DrawImage(Tiles,Int(x),50,0)
 EndIf
 '
 x=x+.15
 If x>639 Then x = 0
 Flip(1)
 '
Wend
'
ShowMouse()
EndGraphics()



Grey Alien(Posted 2007) [#12]
MGE: couple of things:

"Integers won't look that smooth though." Worked fine on the Nintendo, Genesis, ST, Amiga, etc. ;)
. It looked smooth on those games because they always scrolled by an integer amount. So for example and Amiga game scrolled at 50 pixels per second which was the screen refresh rate (for PAL systems). So 1 pixel per frame = smooth. For modern games you want different scroll speeds and also the framerate is not fixed thus it will not look totally smooth. Even Turrican in an Amiga emulator no longer looks smooth unless you run your screen at 100Hz.

OK, I posted about the edge artifacts issue before. It's like this:

1) Integer coords, no artifacts, but won't look ultrasmooth when scrolling unless you lock refresh rate to 60 and move 60 pixels per second.
2) Float coords, individual tiles. Each tile will have an anti-aliased edge, this can't be avoided and is OK because it makes the scrolling look smoother BUT the anti-aliasing is a bit crappy due to lack of source data on adjoining tiles for the anti-aliasing so it can look bad on some tile edges.
3) Float coords, one big tile (or mesh). Aha, this looks best because the anti-aliasing takes into account the adjacent tiles. This is the one that I want to achieve.

There is a real different between 2 and 3, I've seen it with my own eyes, in fact I posted a demo about it in a thread a while back. Your 512 tile test is also better than if you rendered them individually, try it.


hub(Posted 2007) [#13]
Hi !
Today i use a solution like 1). i force the refresh rate to 60 and avoid delta timing as my game have 32x32 tiles scrolling. Result = no "artifacts". I know that it is not the commercial approach. But i'm hobbist, my game is a freeware... have a lot of work with the entire project (gfx, level design...) Avoid the 'artifacs' seems to me more important that very smooth scrolling game with tiles overlap... today i just hate limit the game fps to 60, but at his time i've not other solution. Hope GA could propose an demo with 3)


Grey Alien(Posted 2007) [#14]
fair enough. I can't do 3 at the moment MUST finish my current game. Shouldn't even be posting here....


MGE(Posted 2007) [#15]
Hi GA - "It looked smooth on those games because they always scrolled by an integer amount. So for example and Amiga game scrolled at 50 pixels per second which was the screen refresh rate (for PAL systems). So 1 pixel per frame = smooth."

wait...before I comment on that, I wanted to clarify your post. Are you saying Amiga games didn't scroll more than 1 scan line per vertical blank? All games scrolled at the same speed????

"OK, I posted about the edge artifacts issue before. It's like this..." Yep, been there done that...I was just trying to point out using a mesh on a 3d card to render a 2d tile map @ floats is still going to have artifacting. The demo I posted is basically identical to how a mesh would be rendered. And as you can see, on floating point sub pixel locations, you still get artifacting.

"Your 512 tile test is also better than if you rendered them individually, try it." On my tests, both methods on screen look identical. I may need to borrow your test box for a while because from all of our posts it seems our eyes are seeing differently. ;) lol...

NOTHING will look butter smooth if you miss a vertical blank due to the render loop taking too long. Motion blur would help but sub pixel rendering is not motion blur. ;)

@ hub - The fun thing about coding games on the pc is, there IS no right/wrong way to do it. Whatever works for your game is the way to do it. I personally would never force a refresh mode on the monitor, but locking your game to a fixed frame rate is sometimes desireable. For instance in a situation where vsync is turned off and the game's frame rate is jumping all around it may be better to lock at 30 or 60. Players get a better "feel" with a stable frame rate compared to one that flucuates wildly. ;)


Grey Alien(Posted 2007) [#16]
wait...before I comment on that, I wanted to clarify your post. Are you saying Amiga games didn't scroll more than 1 scan line per vertical blank? All games scrolled at the same speed????
of course I'm not saying that. But I'm saying the good ones tried to sync scrolling speed to refresh rate so like 0.5 pixels per frame (i.e. 1 pixel every 2 frames) or 2 pixels a frame etc. In fact it was hard NOT to sync because no-one really use delta-timing and floating point numbers were too CPU intensive. (info: I coded on tons of 8 bit computers and the Amiga for years in assembly and Blitz)

Yeah for sure when I ran the demo I made in that other thread, the individual tiles has worse edges that the combo tile (emulating a mesh), otherwise I wouldn't have made the post. Perhaps different video cards look different or you aren't looking closely enough ;-) Logically it WILL be worse without a mesh anyway because the tiles aren't anti-aliasing to each other, they are anti-aliasing to the background which may be a totally different colour...

Re: There is no correct way for PCs. True. This annoys me a bit as nothing is a simple as it used to be. Some ways may look better on certain PCs but worse on others. My mission is to find the method that looks the best on the widest range of PCs so more people can enjoy the games and therefore they will have more commericial success.


MGE(Posted 2007) [#17]
Good dialog, much appreciated. ;) I took the ST route, had no problem doing floating point scrolling in all directions, speeds, etc. It was all bitmapped so it was a little more difficult. ;) Yah, syncing to vblank is always preferred regardless of device. On the PC it's just not always possible. You have to plan for the worse, hope for the best. ;)

Just curious and then I'll let it rest, are you under the impression a mesh would render differently compared to the demo I uploaded with all the tiles in the texure? If so, why? Thanks.


hub(Posted 2007) [#18]
I personally would never force a refresh mode on the monitor, but locking your game to a fixed frame rate is sometimes desireable

sorry, translation error for me in fact i just limit the fps to 60,i no force it.


this algo is not as smooth as deltatime but it works with tiles. But i agree that deltatime is very good if you not use tiles into your project.


Grey Alien(Posted 2007) [#19]
Just curious and then I'll let it rest, are you under the impression a mesh would render differently compared to the demo I uploaded with all the tiles in the texure? If so, why? Thanks.
No, I'm saying that your demo with all tiles in texture will give the same result as a mesh (if my understanding of a mesh is correct) and that if the tiles were drawn separately, it would look worse on the edges of the tiles where they join. I found this to be the case in my own tests and was dissatisfied with the look and didn't want integer based coords so "thought up" the mesh idea and someone confirmed it could be done with 3D card code but I don't know how yet...


ImaginaryHuman(Posted 2007) [#20]
I don't understand why the drawing of your tiles needs to involve the delta value for every single tile. Can't you just use the delta to calculate the position of the tile row at the top of the screen, and then to draw all consecutive rows you add an integer value to that. ...... cus I think maybe it's just a matter of floating point numbers not being totally accurate, which may be causing the gaps. Have you tried using Doubles to see if that removes the problem?

As to the mesh idea, the concept is that basically instead of defining triangles you define strips of triangles and since the vertices are shared the API is able to take the adjacent triangles into account when figuring out antialiasing etc. However, this only applies to strips of triangles - what happens when you have a horizontal strip and you get to the end of a row and then you turn around and start a second row going back in the other direction? There is a gap between the rows. The triangles on the top row are not connected to the triangles on the second row, so surely you will still get the same artifacts vertically if not horizontally?


dmaz(Posted 2007) [#21]
what happens when you have a horizontal strip and you get to the end of a row and then you turn around and start a second row going back in the other direction?

if I understand what you mean, that won't happen. you want to have control of your uv's with this method. the way a scroll like this would work is basically the same as a old 2d hardware scroll. you set your mesh to be viewed 1 tile bigger on each side and when you scroll, you move your mesh. then when you push over an edge you reset all your uv's to the new map position and then move the mesh the opposite direction 1 tile length.


ImaginaryHuman(Posted 2007) [#22]
It doesn't matter what tile method you use. How does it avoid gaps between tiles? I was under the impression you had to draw a triangle strip or quad strip in order for it to know about adjacent/shared vertices, so that it can calculate colors properly. is that not the case?

Or are you saying that as well as keeping tiles in video ram you should also render the visible tile area into a texture and then render the texture using a grid of quads with appropriate texture coords so all quads have proper colored pixels around the edges? How do you draw tiles to the texture in such a way that the first time you draw them it does not draw artifacts at the edges? You have to draw them as individually textured quads at some point and you cant change what texture is used halfway through defining a strip.

So doesn't that mean you will still get gaps and artifacts? Do you have to render to integer coords perhaps in the texture and then draw the texture at float coords?


MGE(Posted 2007) [#23]
The mesh will not resolve the artifacting. A sub pixel plotted pixel will artifact regardless of the method used. The mesh will indeed help increase speed, depending on the complexity of the map, layers, etc, etc.

"you set your mesh to be viewed 1 tile bigger on each side and when you scroll, you move your mesh. then when you push over an edge you reset all your uv's to the new map position and then move the mesh the opposite direction 1 tile length."

But things get more complicated when you start wanting to animate tiles inside the mesh, add fx like rotation, scaling, alpha, tile animation, etc. I doubt it would be that much faster then regular tile plotting at that point.


hub(Posted 2007) [#24]
agree with this. i've expirimented that for my game. As you need some animated tiles to keep the level interesting.


Grey Alien(Posted 2007) [#25]
The mesh will not resolve the artifacting
There's 2 kinds of artifacting and it will solve one of them:

1) sub-pixel anti-aliasing between tiles. Won't be solved but that's fine because it looks fine when it's moving.
2) sub-pixel anti-aliasing to the BACKGROUND because when you draw a single tile, it anti-aliases the edge to the background not the adjacent tile which has not yet been drawn. So using a mesh (or a giant bitmap) would mean that the edge WILL be anti-aliased to the correct tile and not some black background, so it'll look better.

I don't know enough about MEsh's to comment on ImaginaryHuman's thing about having a strip. I was hopeing that I could just make a giant image with all the tiles on (slightly wider than the screen) and then draw it at sub-pixel coords. Perhaps I can't....


ImaginaryHuman(Posted 2007) [#26]
1) If you switch on polygon antialiasing, so the edges of polygons are antialiased, does this remove the problem?
Does the problem only occur with the pixels that are at the edge of the texture where the border is blank?

2) I'm not sure how you would generate the mesh so that it avoids this problem. With a triangle strip it would remove artifacts horizontally but not between rows. And again, would polygon antialiasing fix it?


dmaz(Posted 2007) [#27]
No, a strip wouldn't be the best solution because of exactly what you said... you would need to use a real mesh with shared vertices or you could do it with a flat evaluator map.


ImaginaryHuman(Posted 2007) [#28]
What do you mean by "a real mesh"? What do you suppose a mesh is composed of? It's made of triangle strips. Unless I'm mistaken there is no other way to define a series of conencted vertices other than a strip, or a triangle fan. There is no way to define a `grid` of vertices. Unless, perhaps, in some kind of vertex array?


Grey Alien(Posted 2007) [#29]
I "assumed" that it would be a grid, like a flat terrain in a 3D game. I never knew it had to be in a strip.


dmaz(Posted 2007) [#30]
What do you mean by "a real mesh"? What do you suppose a mesh is composed of?
right... but for this type of application an evaluator map would work well. I'm no expert that's for sure but from what I understand yes, you can define a mesh using a vertex array. While the triangle/quad strips will use truly share verts so does an evaluator map. If using a vertex array or for that matter just a bunch of quads, there should be no cracks if the corners are defined at the exact same vertexes.


ImaginaryHuman(Posted 2007) [#31]
Well, that's kind of what I thought - that if you position every corner at exactly the same floating point coordinates there shouldn't be any gaps.

The problem though really isn't about whether there are gaps. You shouldn't be getting any gaps if you do it right. The problem is that if you draw disconnected pieces of geometry they don't usually blend together properly at their edges. Each time you paste down a quad or even a strip, there are some edges which are not connected to any other triangles so they get antialiased to the background instead of to the triangles that are drawn later. And everything I've read about `mesh`es has talked about using triangle strips for optimum performance. I'm not sure what you mean by using an evaluator. I know you can convert splines to triangles with an evaluator but I'm not sure about making a 2d grid with it or whether they would even solve the issue.

I think this is what polygon antialiasing is supposed to be for. When you switch it on e.g. glEnable(GL_POLYGON_SMOOTH) and you have the correct blend mode to deal with it and your polygons are drawn in the correct sorted order, then it's supposed to accurately render the outer edges of each quad with floating point accuracy, combined with the background. A fractional portion of the quad's pixel is put into the background pixel so that when you come to draw the next quad it also draws a precice fraction to that pixel to take into account the previous quad. Or something like that. The only thing is, to make it blend properly you need a certain blend mode to be active which rules out using other blend modes at the same time, suggesting you can only antialiase polygons drawn in `solidblend` mode.

Even if you switch on filtering for an image, the pixels within the quad are filtered perfectly but any at the edges are not filtered against the background without this smoothing enabled.


Grey Alien(Posted 2007) [#32]
Hmm, it MUST be possible I see no reason why not. You say here is a load of textures, they are all joined "perfectly" together, please render them nice and anti-aliased without the black background being picked up in the anti-aliasing. I just don't have a clue how ;-) Maybe when I've finished my game I can look into it more for fun (for making a cool scroller)


ImaginaryHuman(Posted 2007) [#33]
I think in your earlier experiments you said that when you draw an irregular sprite to the screen with filtering enabled for that image, it blends with the background fine, but that the pixels at the extreme edges of the sprite do not, is that right? If you were to switch on polygon smoothing, those pixels at the edge of the image would blend just as well as those within the sprite.

The amount of color contribution from the sprite image is blended with the color in the background pixel so that the resulting background pixel is some result of the two combining, as a float value. It should then be perfectly possible to draw another sprite at the same coord, next to it, and it should also contribute to that pixel value, which means it also takes into account the other sprite's pixel. You shouldn't need anything else to make them appear to be perfectly lined up and antialiazed with each other.

It shouldn't even require a mesh. In fact, the idea of a mesh is just an idea of a number of joined together triangles, usually in a strip, where two of the previous vertexes for the previous triangle are shared by the next triangle. But that gives you rows of triangles, not a 2d grid. With polygon smoothing on you should get seamless rendering.

The question might be whether it's really viable to use polygon smoothing all the time since it overrides any other blend modes you might want to use. And then you might want to look at full screen antialiasing.


dmaz(Posted 2007) [#34]
yeah, but I don't think you want to use a strip. if you use horizontal strips the inner vertexes for the next strip may not line up and you can see cracks.... that's why I say a mesh, with a mesh you have complete control over your shared verts.


ImaginaryHuman(Posted 2007) [#35]
I just don't know that there is such a thing as a `mesh` - you're talking about some kind of grid of vertices - I have never seen such a thing - although I'm not totally expert. I know you can define arrays of vertices but these usually are used in conjunction with a specific type of primitive such as triangle strips. I don't know that using an array as traingle strips has any different result to just using a regular triangle strip in immediate mode.

This mystical `mesh` that you're talking about - maybe that's a DirectX concept or something. I know you think of a 3d object as being a mesh of triangles, but the question is, how do you really pass that to the graphics card with everything connected and all vertices shared in one go? I'd like someone to at least demonstrate or show reference to somewhere that it says that a mesh is anything other than a triangle strip. I know I'm sounding a bit combative or stubborn about it, but it's because I actually do want to know how to do this if it is possible and I want to make sure that we really look at this closely to know exactly what we're talking about.


Grey Alien(Posted 2007) [#36]
The amount of color contribution from the sprite image is blended with the color in the background pixel so that the resulting background pixel is some result of the two combining, as a float value. It should then be perfectly possible to draw another sprite at the same coord, next to it, and it should also contribute to that pixel value, which means it also takes into account the other sprite's pixel. You shouldn't need anything else to make them appear to be perfectly lined up and antialiazed with each other.
But it the two are supposed to be joined, you don't want *any* background value being used, jsut the values from the two tiles.


ImaginaryHuman(Posted 2007) [#37]
Hmm. I suppose you'd need a huge amount of supersampling full-screen antialiazing to be able to draw one object against the background and then draw another and have it properly join them.

So we have to avoid drawing things as separate images if we want them to blend together. At least that we know.


ImaginaryHuman(Posted 2007) [#38]
There seems to be a few more advanced features, in OpenGL at least, which provide more efficient rendering of lots of triangles with shared vertices, ie like a mesh - e.g. using glDrawElements which uses arrays of triangle data. I don't know HOW it renders or whether it overcomes any visual problems, but I guess we'll find out at some point. I'm not at the point yet where I want to spend time testing it.