Smooth movement in 2d platformer

Hello
I am new to game dev and most stuff I learn is from online tutorials or finding it out myself.
I have a 2d platformer with a camera system, the problem is since I move in lets say 4 pixels per frame, even if I have 60 fps movement is not particularly smooth.
I think this is a movement speed issue rather than a camera issue.
One of the solutions I could think of: move in floats, and use AffineTransform when drawing images, problem is my collision system uses ints, I could cast or approximate.
Another solution is to interpolate movement, but that means making a more complex game engine, right now I do update render draw per cycle 60 times per second, and in case of overtime I update without rendering a few times (frame skip)

Are there other solutions?

Edit 1: Here is a demo: http://filebin.ca/iESvMvnv9Cc/Main.jar
Edit 2: I have added a scrollable background layer (sort of paralax scrolling) and the movement issue isn’t as noticeable now.

anyone guys?

If it actually is rendering @ 60 fps, you don’t have a problem.

Just make it move faster or slower.

but in that case I don’t get the movement speed I want?
I also have read and think part of my problem might be related to windowed mode as frames don’t sync with the display.

i don’t think your problem is a performance issue, i faced the same thing before, and what i noticed is 1st that the game run more “smoothly” outside of eclipse (exported jar) but more importantly the problem was because i didn’t write a good “smooth” movement,
if(right){ x+=4;} doesn’t gave me the result i was looking for, so i had to add more things to simulate “real” movements in a platformer game, i always tried to make the movement as close as possible to game like super meat boy (SkullBoy is my favorite, it’s free) and if you try my 1st WIP release and compare it with the 2nd one, you will absolutely know the difference by your self, here.
one last thing, make sure that you are not losing any frame rate, print it on console or in game just to keep track of what is happening.
i hope i helped, good luck

4 pixels at 60 fps means covering 240 pixels in one second.

Usually that would be okay, it seems to me. However, if you find you are skipping more than one frame, or even skipping a single frame frequently, then I would look to try and figure out what is consuming the most cpu’s per frame.

One way to do that would be to put in some System.nanoTime() tests into the game loop that report if a section of code takes longer than expected. There are also profiler tools.

that may be a bit too much maybe as I am not fullscreen, I am in a window with about ~600-700 width

I will take a look at the second one. Did you use float numbers for movement? cause I use integers so I am forced to move in 1 pixel.

I noticed you have the same bug I have, that is if you hold space/jump below a block you will get stuck to the ceiling as on collision you reset the jump flag,. In my case I do if falling and collision then reset but it still doesn’t work, I plan to improve my collision system so I notify the direction of the collision. I had another idea so the player includes multiple collision boxes, one say for feet, another for head and use that, but that would increase the complexity of collisions with enemies and so on.
Did you fix this bug?

yes i use float, and in the second one i almost fixed all the collision bugs (except when it jump and hit something from above)
question :
is your main problem is that your player get into the ground or other things sometimes ??

Float or not, there’s no way to move in half pixels. ::slight_smile:
Using a float is just more useful - say you want something to move 1 pixel per 2 frames - using floats it’s just 0.5 pixels per frame, can’t do that with ints because it’ll just round.

It would help if you would post a simple example program that demonstrates the problem. There could be several reasons. e.g lack of double buffering, rounding from float to int, etc.

Here is an example that shows movement interpolation. Java has support for that built in. But it can only be used when drawing Shape objects.
This draws two moving circles, the one at the bottom has it’s movement interpolated automatically, the other does not.

import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
import java.awt.geom.Ellipse2D;

public class Ball extends JPanel {
    static final int WIDTH = 800, HEIGHT = 600;
    BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
    volatile double x1, y1;
    volatile double x2, y2;
    
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        Ball panel = new Ball();
        panel.setPreferredSize(new Dimension(WIDTH, HEIGHT));
        frame.add(panel);
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
        panel.start();
    }
    
    public void start() {
        x1 = 0; y1 = 0;
        x2 = 0; y2 = 100;
        while(true) {
            x1 += 0.43;
            x2 += 0.43;
            y1 += 0.07;
            y2 += 0.07;
            repaint();
            try {
                Thread.sleep(16);
            } catch(Exception e) {
            }
        }
    }
    
    public void paint(Graphics _g) {
        Graphics2D g = image.createGraphics();
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, WIDTH, HEIGHT);
        g.setColor(Color.BLACK);
        g.fillOval((int)x1, (int)y1, 50, 50);
        g.fill(new Ellipse2D.Double(x2, y2, 50, 50));
        _g.drawImage(image, 0, 0, null);
    }
}

However this kind of movement interpolation is not the same as the interpolation used when scaling images.
If you want to interpolate the movement of images it get’s slightly more complicated since Graphics2D does not have a drawImage method that takes in floats or doubles. But it should be doable by scaling the graphics object e.g. by a factor of 0.1 and scaling the image you want to draw by 10 using scaling interpolation. That way you can specify the location to draw the image to 0.1 pixels.

How about AffineTransform translate on an image ? I read on stackoverflow that it can be used to do that or is it wrong?
Also I have edited my first post to include a demo as you asked ( http://filebin.ca/iESvMvnv9Cc/Main.jar )

Yes, a Graphics2D object uses AffineTransform internally.
I tried it out with a moving image and it works if you use the Graphics2D.translate method.

import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
import java.awt.geom.Ellipse2D;
import javax.imageio.ImageIO;
import java.io.File;

public class Ball extends JPanel {
    static final int WIDTH = 800, HEIGHT = 600;
    BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
    BufferedImage sprite;
    volatile double x, y;
    
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        Ball panel = new Ball();
        panel.setPreferredSize(new Dimension(WIDTH, HEIGHT));
        frame.add(panel);
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
        panel.start();
    }
    
    public void start() {
        try {
            sprite = ImageIO.read(new File("c:/sprite.jpg"));
        } catch(Exception e) {
        }
        x = 0; y = 0;
        while(true) {
            x += 0.43;
            y += 0.07;
            repaint();
            try {
                Thread.sleep(16);
            } catch(Exception e) {
            }                                                             
        }
    }
    
    public void paint(Graphics _g) {
        Graphics2D g = image.createGraphics();
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, WIDTH, HEIGHT);
        g.translate(x, y);
        g.drawImage(sprite, 0, 0, null);
        _g.drawImage(image, 0, 0, null);
    }
}

x and y have to be double or float. Also interpolation needs to be turned on explicitely. Try removing the line “g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);” and see what happens.

The movement looks smooth enough to me.

Well if you don’t look at the character in the center of the screen but at the blocks before him and focus on them, it looks as if they’re shaking since they move in fixed intervals.
I tried to play super mario bros as a comparison and it seems smother to me, but I think he moves slower as well not sure.
If you say it’s smooth I guess I am looking too much into it.
I’ll try to migrate everything to float/double movements and use AffineTransform instead as I really need subpixel movements and I don’t think casting to int is a good solution.

Yes, if you look closely you can see a slight shaking.
Anyway using doubles for movement is certainly better than ints, also you don’t need to use AffineTransform directly. It’s sufficient to use the Graphics2D.translate method as in my example.

I am wondering if my camera calculations are not wrong, since sprite movement looks good if you stay still.

I’m guessing your camera movement uses ints too. So it may be a rounding issue.

What I do is use division to find out the position inside the tile map so I know where to grab the tiles from, then I use modulo/% tile size to obtain the remainder and I draw all the tiles in the view and their position on screen I add the remainder player_position % tile size or substract it.

IMHO
Make movement by float
With synchronize per frame


float pos_X = 10;

pos_X += 4f * frame_SYN;// 60 fps base then if we have 30 frame_SYN = 0.5

Draw((int) pos_X, (int) pos_Y)

for collision system use (int)pos_X =)

I am sorry, what is frame synchronization ? I have never heard of this before.