Silly problem: Simple solution?

As most of us, I control nearly all of my images on my game screen via float*delta to get nice, smooth movement. I’ve ran into a rather awkward problem though. Slick2D’s TiledMap class demands a int value to render the actual tiled map. That’s fine and dandy, I just pass the valued cased with (int).

Well, that works great for moving up/down/left/right, but the problem is, when moving diagonally in any direction you get this very slight “zigzag” effect. It’s extremely subtle, hardly noticeable at all and quite frankly I may be the only one who ever notices it. But it’s there, and I assume it’s from the float values being rounded to an int when passed to the TiledMap class to render the map. When moving up, down, left or right it’s completely invisible. But diagonally you can tell that the movement is moving like this:

http://sixtygig.com/junk/post1.png

What I want, is this:

http://sixtygig.com/junk/post2.png

… and, I’m unsure how to solve the problem. I attempted to simply edit the slick source code to be able to pass a float to it instead, but the problem is the tiled class propagates the “need” for an int all the way deep into other classes I should not be messing with, I ended up changing about 6 or 7 different class files from int to floats before I started hitting walls of code where I’d have to reprogram some core slick methods and realized it probably wasn’t a good idea. So, I’m looking for alternative solutions.

One solution I could come up with in theory is to “magically” run both playerX and Y at the absolute exact same time, I believe the problem would go away.
But, I’m pretty sure that can’t be done, so… Any advice? :smiley:


	if(canMoveNorthEast){
		this.canMoveReset();
		playerX += 0.1f * delta;
		playerY -= 0.1f * delta;
		input.clearKeyPressedRecord();
		return;
	}

if its the tile map zig-zaging, perhaps you need a separate map position that stays as a float, and update the actual map position as an int of your float values each frame?

If the x and y in render(int x, int y) are tile coords, or world coords of some type, then what Liquid said: render map to a buffer and render that while adjusting a float offset.

If they are pixels, you might try moving playerX, playerY by Bresenham’s line algorithm (for int render coords), or a linear extrapolation (for float offsets).

Sorry, not very familiar with Slick.

Well to get technical, it’s actually the floats mapX and mapY that update the map’s position, but I didn’t want to overcomplicate the problem. :smiley: What actually happens is the engine detects when the player gets too close to the edge of the screen and updates mapX/Y as well (thus moving the map).

Example of the camera adjustment code that detects the player and “gently” moves the map around (for a fluid camera movement, so it isnt rigidly hardlocked on the player)


	if(!(mapX >= -10)){
		if(playerX < 200){
			if(playerX < 180){
				mapX += 0.1f * delta;
				playerX += 0.1f * delta;
			}else{
				mapX += 0.05f * delta;
				playerX += 0.05f * delta;
			}
		}
	}

But, wouldn’t passing the float to another float and then to an int actually be the same problem? :confused:

It’s by pixel. :wink:

You can look at my little engine demo video for an example of how this all looks in game. (the video stutters, but that’s just the shoddy recording. Don’t bother trying to look for the problem in the video)

I’ll checkout your link when my brain kicks in, I’m burned out and about ready for bed as of writing this, but I glanced at it, and it may be able to solve my problem. :wink:

A simple problem with no good solution from my experience.

If you move in floats and you have rather low res textures(1 texel more than 1 pixel) you will get texture shimmering.
There is no fix I found to texture shimmering other than use as high res textures as possible.

Actually, I’m pretty sure I see that, that subtle diagonal stutter-step?

Took a glance at this, might be helpful. Otherwise, trollwarrior might be right.

Perhaps since you have a map and player variable, they are rounding down out of phase with each other?
For a smooth non linear camera slide, I do this before each render:


     //these variables are all Point2D.Float.
     cameraTarget.x = playerPos.x - screenCenter.x;
     cameraTarget.y = playerPos.y - screenCenter.y;
     
     if (cameraPos.x - cameraTarget.x != 0){
         cameraPos.x -= (cameraPos.x - cameraTarget.x)/10;
     }
     if (cameraPos.y - cameraTarget.y != 0){
         cameraPos.y -= (cameraPos.y - cameraTarget.y)/10;
     }

Yeah, after thinking about the concepts I’m thinking Troll is indeed right.

The problem is, literally no matter what you do, when you’re working with low-resolution images you can only move exactly 1 pixel, even though I am using scaled images and there’s no way to make that a fractional move unless I make “faked” retro textures where I just scale up all the images in photoshop and import them 300% larger, with no scaling. If I did that, then in theory every pixel I move would only be 1/3rd a “fake” pixel and might make the diagonal movement look much smoother even if it is still technically zigzagging. Problem is, that increases the image memory usage of my game by 300% and that’s just insane to get rid of a “only the dev would ever know” kind of problem. :o

As long as it’s scaled, I’m pretty sure there’s no mathematical way to execute the code to run both a “X” and “Y” shift at the absolute exact same time, causing a perfect diagonal movement. It will always zigzag, higher resolutions (and no scaling) just make it mostly invisible. But at these “retro” resolutions, you can’t really hide it.

I’m going to carefully check out some old SNES/Sega games tomorrow and see if they do the same thing and I just never noticed.

Actually the only float that ever gets rounded is mapX and mapY, everything else across the entire program remains a float. mapX and Y only get cast when they get pushed into TiledMap.

Although I wonder if something along the lines of intToPassToTiled = (int)(playerX + mapY)/2 could help by giving the average of the 2 values? Hrm. I’ll have to give it a shot tomorrow. You guys are catching me as I’m literally just heading to bed now. :smiley:

Emm, silly question but shouldnt you move around the camera instead of the map? It seems a bit strange to move the world around instead of just looking at a different part of the world. (e.g. in LibGDX I just move around the OrtographicCamera smoothly).

However, I cant really remember or find whether Slick2D even uses a camera or viewport… If not then never mind.

When you’re ‘moving’ camera around, you’re actually moving around the map. Camera’s coordinates are subtracted from from each vertex you pass in, and you pass in different part of the map. So you’re really kinda moving around the map, not just moving camera.

Yeah I got that, I was just wondering if this is the optimal approach for the OP, as the integer coordinates used when rendering tiles means that camera panning will not be smooth.

Yeah, basically I am moving the map itself in the opposite direction the player is moving, not a third camera variable. Basically when the character moves on the screen, I just move around playerX/Y at 0.2fdelta, when he moves roughly 50 pixels from center the map also starts to move mapX/Y at a slightly slower rate of .1fdelta, then when the player gets about 100px~ from center, the mapX/Y and playerX/Y movement speed basically lock together. (Best way to see it in action is to watch the video out in the post above)

There’s a lot more confusing math that goes into this too, for example, I am constantly detecting where the player’s location is and changing the “start” and “end” points where the map will render tiles, so I am only rendering a 24x24 tile section of the map at once, thus I can support huge maps but only have the CPU process a much, much smaller chunk. I’ve tested this method all the way up to 1024x1024 maps (32768x32768 pixel map) smooth 60FPS all around! yay! :smiley:

But there is no third variable for the “camera” since essentially mapX/Y is the camera. Although if I could figure a way to filter mapX and playerX together and output that as a third variable to the map rendering, it may smooth it out a bit. I’m wondering if sending a third variable that is basically playerX+mapX/2 would give a smoother output since it would be the rounded int of the 2 floats combined, but I suspect that may still result in the same problem.

Another idea I had was to have a filter method that basically stored the floats of mapX and Y and waited until both increased by exactly one, if they both increase by one, then pass the casted version over. That may trigger it to move in both directions at the same time. Because instead of moving in a zigzag, I am simultaneously telling both to move. But who knows, that may still be the exact same result, because even in that situation one of the 2 adjustments will have to fire first.

It seems like no matter what happens, eventually you have to send a int to the map to move it, and you will always have to send one before the other. I checked a few old SNES/Sega games in slowmo on an emulator and they do not do this, so obviously there’s some voodoo magic that can correct the problem, I just have not figured it out yet. :smiley: It may be something as simple as what LiquidNitrogen said, I’m going to run over my code and see if there’s any situation where mapX is firing when playerY is and vice versa in diagonal movement as soon as I can get back to my IDE. :wink:

After fiddling with it for about 4 hours between trying some of your guy’s suggestions and some of my own ideas I decided to go ahead and modify Slick itself. I managed to break it all down and figured out I only had to edit 4 Slick files. Now it works like a champ, and my tiledmap class is properly taking floats instead of ints!

wohoo! Smooth-as-butter movement! :smiley:

Thanks for the help guys! :wink:

You should post a thread about how you did it, perhaps in Shared Code, others might like to know.

Sounds like a good idea. I’ll do that when I taking a coding break. :wink:

Posted. Oddly enough now that I cleaned up everything it ended up being amazingly simple, go figure. :smiley:

Good job, and nice out-of-your box thinking ;D