Movement Speed

Hey guys, so I have another question.

Im trying to figure out how to deal with moving my characters faster. Right now my xSpeed, which I add to xPosition in my move() method is set to 1 or 2. So basically my character is moving 1 or 2 pixels per update. This looks really nice and smooth, but its obviously slow. If I want my character to go fast, how can I achieve this?? Setting my speed higher make him move fast, but it makes the animation very choppy, since its kind of jumping over 5 pixels or more each frame. How can I make him move faster but keep the smooth transition in the painting?

First determine how much time you want for that object to move 1pixel. Example you want it takes 2 seconds in every 1pixel move. Then see your delta update time and move your object on how much it need to move based on that time. I use this and move pretty smooth.


public void move(long delta){
    x += delta * speedOnMilisecond;
}

however, if your delta update time goes high, the animation will become choppy again as you said. So, you need to keep that delta small.

Sorry but not sure if I get that. Basically my game loop is controlled in a class that extends JPanel and implements runnable. Im that class I have the run method which basically calls update on all game entities (moves, shoot, etc) then calls repaint. This loop runs at about 100fps. since I call the move methods in here, basically whatever my move method does is going to be shown every frame. So If my move method moves by one pixel, each loop I will see a one pixel move. How can I move further per frame without causing a choppy jump? Or should I not actually be moving more per frame?

At a 100FPS, you can go pretty fast without seeing a lot of choppiness.
You can do 100 pixels per second (1 pixel per update) or 200 pixels per second (2 pixels per update).

If it’s 100fps then you call sleep at max 10ms. My previous reply is based on
http://www.cokeandcode.com/spaceinvaderstutorial
hope you can get it right from the source. It’s pretty good to update the gameloop. I think It’s hard to get it smoother on your current way because you already have high fps (100) and minimum length on screen (1pixel) as ra4king said.

This is more or less what you want done. Your update loop should be receiving the deltaTime it took to complete one loop in your game loop. For example I’m using


update((int) ((currentUpdateTime - lastUpdateTime)/(1000*1000)));

which I yanked up from http://www.java-gaming.org/index.php/topic,21919.0.html. It might be something to use as a reference for you to wrap the idea of using deltaTime to calculate your movement steps. It did take me a little while to understand how to get consistency between movement but once you understand the concept its actually pretty simple!

Good luck!

Ok I think I kinda get it… heres what my main game class is right now:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;


import javax.swing.JPanel;



public class GamePanel extends JPanel implements Runnable {

	private Thread animator;

	private Player player;

	private final int DELAY = 20;
	


	public GamePanel() {

		addKeyListener(new KAdapter());
		setFocusable(true);
		setBackground(Color.black);
		setDoubleBuffered(true);

		player = new Player();

	}

	@Override
	public void run() {

		long beforeTime, timeDiff, sleep;

		beforeTime = System.currentTimeMillis();

		while(true) {

			player.update();
			repaint();

			timeDiff = System.currentTimeMillis() - beforeTime;
			sleep = DELAY - timeDiff;

			if (sleep < 0)
				sleep = 2;
			try {
				Thread.sleep(sleep);
			} catch (InterruptedException e) {
				System.out.println("interrupted");
			}

			beforeTime = System.currentTimeMillis();
		}


	}

	public void paint(Graphics g) {
		
		super.paint(g);

		Graphics2D g2 = (Graphics2D)g;

		if(player.isVisible()) {
			g2.drawImage(player.getImage(), player.getXPosition(), player.getYPosition(), this);
		}

		Toolkit.getDefaultToolkit().sync();
		g.dispose();

	}

	private class KAdapter extends KeyAdapter {

		public void keyReleased(KeyEvent e) {
			player.keyReleased(e);
		}

		public void keyPressed(KeyEvent e) {
			player.keyPressed(e);
		}
	}

	public void addNotify() {

		super.addNotify();  

		animator = new Thread(this);
		animator.start();
	}



}

I got the loop idea from a tutorial I read. What should I be doing here???

No you should give timeDiff to update() and beforeTime = System.currentTimeMillis() should come before the sleep.


while(true) {
    timeDiff = System.currentTimeMillis()-beforeTime;
    beforeTime = System.currentTimeMillis();
    
    player.update(timeDiff);
    repaint();
    
    sleep = DELAY-timeDiff;
    
    if(sleep < 2) sleep = 2;
    
    try{
        Thread.sleep(sleep);
    } catch(Exception exc) {
        exc.printStackTrace();
    }
}

Ok I did that, and now hes moving way fast. Im going to post all of the code and maybe you guys can take a look at it? I understand everything well except for the run method. I dont understand exactly what I should be doing.

Im posting 3 classes and an Interface

import java.awt.Image;
import java.awt.Rectangle;


public interface Entity {
	
	public void move(long timeDiff);
	
	public void update(long timeDiff);
	
	public Image getImage();
	
	public int getXPosition();
	
	public int getYPosition();
	
	public boolean isVisible();
	
	public Rectangle getBounds();

}
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;

import javax.swing.ImageIcon;


public class Player implements Entity {

	private String player = "player.png";

	//x and y positions for the player
	private int xPosition;
	private int yPosition;

	//x and y speeds for the player
	private int xSpeed;
	private int ySpeed;

	//width and height of the player
	private int width;
	private int height;

	private Image image;

	private boolean wPressed;
	private boolean sPressed;
	private boolean aPressed;
	private boolean dPressed;

	private boolean visible;



	public Player() {

		ImageIcon playerIcon = new ImageIcon(this.getClass().getResource(player));
		image = playerIcon.getImage();

		//setting the players dimensions
		width = image.getWidth(null);
		height = image.getHeight(null);
		

		//setting the initial player position and speed
		xPosition = 0;
		yPosition = MainFrame.gameWindowHeight - 100;
		

		xSpeed = 1;
		ySpeed = 1;


		//setting visible
		visible = true;


	}

	@Override
	public void move(long timeDiff) {

		if(dPressed) {
			xPosition = xPosition + (int)(xSpeed * timeDiff);
			
		}
		if(aPressed) {
			xPosition = xPosition - (int) (xSpeed * timeDiff);

		}
		if(wPressed) {

			for(int j = 0; j < 50; j++) {
				if(j <= 25) yPosition += 1;//you'll go up when the jump is under halfway done
				if(j >= 25) yPosition -= 1;//you'll go down when the jump is over halfway done
			}
		
		}

	}


	@Override
	public void update(long timeDiff) {

		move(timeDiff);
	}

	@Override
	public Image getImage() {
		// TODO Auto-generated method stub
		return image;
	}

	@Override
	public int getXPosition() {
		// TODO Auto-generated method stub
		return xPosition;
	}

	@Override
	public int getYPosition() {
		// TODO Auto-generated method stub
		return yPosition;
	}

	@Override
	public boolean isVisible() {
		// TODO Auto-generated method stub
		return visible;
	}

	@Override
	public Rectangle getBounds() {
		// TODO Auto-generated method stub
		return new Rectangle(getXPosition(), getYPosition(), width, height);
	}

	public void keyPressed(KeyEvent e) {

		int key = e.getKeyCode();

		if(key == KeyEvent.VK_W) {

			wPressed = true;

		}

		if(key == KeyEvent.VK_S) {

			sPressed = true;

		}

		if(key == KeyEvent.VK_A) {

			aPressed = true;

		}

		if(key == KeyEvent.VK_D) {

			dPressed = true;

		}


	}

	public void keyReleased(KeyEvent e) {

		int key = e.getKeyCode();

		if(key == KeyEvent.VK_W) {

			wPressed = false;

		}

		if(key == KeyEvent.VK_S) {

			sPressed = false;

		}

		if(key == KeyEvent.VK_A) {

			aPressed = false;

		}

		if(key == KeyEvent.VK_D) {

			dPressed = false;

		}
	}
}

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;


import javax.swing.JPanel;



public class GamePanel extends JPanel implements Runnable {

	private Thread animator;

	private Player player;

	private final int DELAY = 50;



	public GamePanel() {

		addKeyListener(new KAdapter());
		setFocusable(true);
		setBackground(Color.black);
		setDoubleBuffered(true);

		player = new Player();

	}

	@Override
	public void run() {

		long beforeTime = 0, timeDiff, sleep;

		while(true) {

			timeDiff = System.currentTimeMillis() - beforeTime;
			beforeTime = System.currentTimeMillis();

			player.update(timeDiff);
			repaint();

			
			sleep = DELAY - timeDiff;

			if (sleep < 0)
				sleep = 2;
			try {
				Thread.sleep(sleep);
			} catch (InterruptedException e) {
				System.out.println("interrupted");
			}

		
		}


	}

	public void paint(Graphics g) {

		super.paint(g);

		Graphics2D g2 = (Graphics2D)g;

		if(player.isVisible()) {
			g2.drawImage(player.getImage(), player.getXPosition(), player.getYPosition(), this);
		}

		Toolkit.getDefaultToolkit().sync();
		g.dispose();

	}

	private class KAdapter extends KeyAdapter {

		public void keyReleased(KeyEvent e) {
			player.keyReleased(e);
		}

		public void keyPressed(KeyEvent e) {
			player.keyPressed(e);
		}
	}

	public void addNotify() {

		super.addNotify();  

		animator = new Thread(this);
		animator.start();
	}



}

import javax.swing.JFrame;


public class MainFrame extends JFrame {
	
private static final long serialVersionUID = -628360966210671157L;
	
	//using these to tell anyone the dimensions of the window
	protected static int gameWindowWidth = 640;
	protected static int gameWindowHeight = 480;
	
	public MainFrame() {

		add(new GamePanel());

		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setSize(gameWindowWidth, gameWindowHeight);
		setLocationRelativeTo(null);
		setTitle("...");
		setResizable(false);
		setVisible(true);
	

	}
	
	public static void main(String[] args) {
		
		new MainFrame();
	}


}

Hopefully one of you can run this and tell me where Im going wrong

From a quick glance it looks like you’re doing passive rendering, i.e., relying on the panel’s repaint() method.
The animation should be smoother if you do “active rendering” (search the forums, and look at how rendering is done in this tutorial).
Simon

Ok so does this mean I should entirely change the structure of my game? Should I drop the use of JPanels completely and use a canvas? Also, If I use a canvas, do I “add” this to an outer JFrame eventually? I never really used any other approach than Jpanel and Jframe so Im kinda confused. Im not trying to make an applet, so Im confused as to where the Canvas actually gets displayed

JPanel is not really well suited for drawing. I recommend JComponent if you want to use Swing.
If you’re fine use Frame instead of JFrame, I recommend Canvas since it was made for drawing.

On the repaint() subject, when you start doing double buffering, you should call createBufferStrategy(2) in your JComponent/Canvas (since this is a method in Component), then get the BufferStrategy by calling getBufferStrategy(). Your BufferStrategy will have getDrawGraphics() that returns the Graphics object to use, with which you manually call paint(g), then call show() on your BufferStrategy. You should also disable repaint by calling setRepaint(false) if you will do this.

On your code, you give your update() a long in milliseconds, so you need to convert this to seconds by dividing by 1000 in your move() so it would be:


double deltaTime = (double)timeDiff/1000;
//...
xPosition = xPosition + (xSpeed * deltaTime);
//...

Also, your position variables should be in double since you can also be moving in decimal values and those are significant as they add up.

Hope that helped ;D

EDIT: Never extend JFrame unless you really need to override one of it’s methods. If you don’t want to use an other component as your draw surface, then you may extend JFrame and override its paint(g).

Ok yeah Ive read that I should be using canvas instead of swing. Im gonna scrap this project and start using Canvas for all of my games. Im gonna most likely be very lost though because Im so used to just calling repaint. So is the idea that I do all of my drawing in the Canvas class? Does that not need to be added to some sort of frame to be able to have a toolbar on top and all that? Also, should this canvas be a Runnable so that I can have my loop in it? Actually, is there even supposed to be a canvas class, or do I just create a canvas instance in my main game class? confused!

Ok so I read over Kevs Space Invader tutorial again and used his method, but it wont work. Heres my code:

package platformer;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.image.BufferStrategy;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class Game extends Canvas implements Runnable {

	private BufferStrategy bufferStrategy;

	private Thread animator;

	private Player player;
	
	private final int DELAY = 50;


	public Game() {

		JFrame container = new JFrame("Platformer");

		

		JPanel panel = (JPanel)container.getContentPane();
		panel.setPreferredSize(new Dimension(800,600));
		panel.setLayout(null);



		setBounds(0,0,800,600);
		panel.add(this);


		setIgnoreRepaint(true);


		container.pack();
		container.setResizable(false);
		container.setVisible(true);



		addKeyListener(new KAdapter());



		requestFocus();

		player = new Player();

		createBufferStrategy(2);
		bufferStrategy = getBufferStrategy();
		
		animator = new Thread(this);
		animator.start();

	

	}

	@Override
	public void run() {
		
		long beforeTime = 0, timeDiff, sleep;

		while(true) {
			
			timeDiff = System.currentTimeMillis() - beforeTime;
			beforeTime = System.currentTimeMillis();

			
			player.update(timeDiff);
			render();


			sleep = DELAY - timeDiff;

			if (sleep < 0)
				sleep = 2;
			try {
				Thread.sleep(sleep);
			} catch (InterruptedException e) {
				System.out.println("interrupted");
			}

		
		}


	}
	
	public void render() {
		
		Graphics2D g = (Graphics2D) bufferStrategy.getDrawGraphics();
		g.setColor(Color.black);
		g.fillRect(0,0,800,600);
		
		if(player.isVisible()) {
			g.drawImage(player.getImage(), (int)player.getXPosition(),(int) player.getYPosition(), null);
		}

		
	}

	private class KAdapter extends KeyAdapter {

		public void keyPressed(KeyEvent e) {

			player.keyPressed(e);


		}
		public void keyReleased(KeyEvent e) {

			player.keyReleased(e);


		}
		
	}
}

	

Its not painting the background or my image. Do I need those JFrame and JPanels inside of my class? I know that the canvas needs to be put into some kind of frame though. What seems wrong with this?

Ok sorry I keep posting, but Ive gotten it to paint and move, but I still cant get it to be smooth. Theres got to be something screwey with either my delta or my move method. Im posting the player and game classes. PLEASEE HELP ME FROM GOING NUTS

package platformer;

import java.awt.Image;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;

import javax.swing.ImageIcon;


public class Player implements Entity {

	private String player = "player.png";

	//x and y positions for the player
	private double xPosition;
	private double yPosition;

	//x and y speeds for the player
	private double xSpeed;
	private double ySpeed;

	//width and height of the player
	private int width;
	private int height;

	private Image image;

	private boolean wPressed;
	private boolean sPressed;
	private boolean aPressed;
	private boolean dPressed;

	private boolean visible;



	public Player() {

		ImageIcon playerIcon = new ImageIcon(this.getClass().getResource(player));
		image = playerIcon.getImage();

		//setting the players dimensions
		width = image.getWidth(null);
		height = image.getHeight(null);
		

		//setting the initial player position and speed
		xPosition = 500;
		yPosition = 500;
		

		xSpeed = 50;
		ySpeed = 50;


		//setting visible
		visible = true;


	}

	@Override
	public void move(long timeDiff) {

		if(dPressed) {
			
			xPosition = (xPosition + ((xSpeed * timeDiff) / 1000));
		
		}
		if(aPressed) {
			xPosition = (xPosition - ((xSpeed * timeDiff) / 1000));

		}
		if(wPressed) {

			
		}

	}


	@Override
	public void update(long timeDiff) {

		move(timeDiff);
	}

	@Override
	public Image getImage() {
		// TODO Auto-generated method stub
		return image;
	}

	@Override
	public double getXPosition() {
		// TODO Auto-generated method stub
		return xPosition;
	}

	@Override
	public double getYPosition() {
		// TODO Auto-generated method stub
		return yPosition;
	}

	@Override
	public boolean isVisible() {
		// TODO Auto-generated method stub
		return visible;
	}

	@Override
	public Rectangle getBounds() {
		// TODO Auto-generated method stub
		return new Rectangle((int)getXPosition(),(int)getYPosition(), width, height);
	}

	public void keyPressed(KeyEvent e) {

		int key = e.getKeyCode();

		if(key == KeyEvent.VK_W) {

			wPressed = true;

		}

		if(key == KeyEvent.VK_S) {

			sPressed = true;

		}

		if(key == KeyEvent.VK_A) {

			aPressed = true;

		}

		if(key == KeyEvent.VK_D) {

			dPressed = true;

		}


	}

	public void keyReleased(KeyEvent e) {

		int key = e.getKeyCode();

		if(key == KeyEvent.VK_W) {

			wPressed = false;

		}

		if(key == KeyEvent.VK_S) {

			sPressed = false;

		}

		if(key == KeyEvent.VK_A) {

			aPressed = false;

		}

		if(key == KeyEvent.VK_D) {

			dPressed = false;

		}
	}
}

package platformer;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.image.BufferStrategy;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class Game extends Canvas implements Runnable {

	private BufferStrategy bufferStrategy;

	private Thread animator;

	private Player player;

	private final int DELAY = 50;


	public Game() {

		JFrame container = new JFrame("Platformer");



		JPanel panel = (JPanel)container.getContentPane();
		panel.setPreferredSize(new Dimension(800,600));
		panel.setLayout(null);



		setBounds(0,0,800,600);
		panel.add(this);


		setIgnoreRepaint(true);


		container.pack();
		container.setResizable(false);
		container.setVisible(true);



		addKeyListener(new KAdapter());



		requestFocus();

		player = new Player();

		createBufferStrategy(2);
		bufferStrategy = getBufferStrategy();

		animator = new Thread(this);
		animator.start();



	}

	@Override
	public void run() {

		long beforeTime = 0, timeDiff, sleep;

		while(true) {

			timeDiff = System.currentTimeMillis() - beforeTime;
			beforeTime = System.currentTimeMillis();


			player.update(timeDiff);
			render();


			sleep = DELAY - timeDiff;

			if (sleep < 0)
				sleep = 2;
			try {
				Thread.sleep(sleep);
			} catch (InterruptedException e) {
				System.out.println("interrupted");
			}
		}
	}

	public void render() {

		Graphics2D g = (Graphics2D) bufferStrategy.getDrawGraphics();
		g.setColor(Color.black);
		g.fillRect(0,0,800,600);

		if(player.isVisible()) {
			g.drawImage(player.getImage(), (int)player.getXPosition(),(int) player.getYPosition(), null);
		}

		g.dispose();
		bufferStrategy.show();


	}

	private class KAdapter extends KeyAdapter {

		public void keyPressed(KeyEvent e) {

			player.keyPressed(e);


		}
		public void keyReleased(KeyEvent e) {

			player.keyReleased(e);


		}

	}
}



I have all of the active rendering working, I just dont know what to do to make it look right. Plus Im not sure if I should still have instances of JFrame and JPanel… I really didnt know any other way

It’s running fine. The only problem is that you’ve set it to 20FPS, when I cranked it up to 100FPS it was smooth.
Otherwise, I see nothing wrong with your code (other than forgetting to use setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) if you ever want your program to end ;D )

Decrease the value of DELAY to increase your FPS score. And I also think that there is no need to sleep 2ms if you already pass the time limit. Just straight continue to next loop. It makes the codes shorter and neater :smiley:

Gotcha. What Im confused about is the calculation for the FPS. How does my DELAY relate to my fps? For instance my DEALY was set to 50, and you said thats 20fps? How did you figure that out

You have 1000 milliseconds in a minute. If your delay is 50, then 1000/50=20 updates per second. Decreasing the DELAY will increase your FPS and vice versa. If you want 100 FPS, set the DELAY to 10 since 1000/10=100 updates per second ;D

I did some thinking and realized that the sleep variable is done wrong.
You should do this:


sleep = DELAY - (System.currentTimeMillis() - beforeTime);

This is because your loop should sleep for your DELAY time minus however long your update code took so this would be constant.

Also


//CHANGE
if(sleep < 0)
    sleep = 2;

//TO
if(sleep < 0)
    continue;

Hope that helped ;D