So I’ve been trying to learn to use awt to make some basic graphics driven stuff, and managed to make a jumping ball that can be controlled with the keyboard. It’s functional enough, but the animation isn’t very smooth looking. I can’t seem to figure out how to use the frame delta that is generated by the variable timestep loop I implemented thanks to the incredibly useful post at http://www.java-gaming.org/topics/game-loops/24220/view.html
public class JumpingBall{
/**
* @param args
*/
//variables
//Methods
public static void main(String[] args) {
Screen screen = new Screen(Globals.DESIRED_FPS);
screen.run(true);
}
}
public class Ball {
// variables
private int Radius;
private double xLocation;
private double yLocation;
private double deltaX;
private double deltaY;
//Getters/Setters for the Balls features
public int getRadius() {
return Radius;
}
public void setRadius(int radius) {
this.Radius = radius;
}
public double getxLocation() {
return xLocation;
}
public void setxLocation(double xLocation) {
this.xLocation = xLocation;
}
public double getyLocation() {
return yLocation;
}
public void setyLocation(double yLocation) {
this.yLocation = yLocation;
}
public double getDeltaX() {
return deltaX;
}
public void setDeltaX(double deltaX) {
this.deltaX = deltaX;
}
public double getDeltaY() {
return deltaY;
}
public void setDeltaY(double deltaY) {
this.deltaY = deltaY;
}
public Ball(int radius, double xLocation, double yLocation) {
this.Radius = radius;
this.xLocation = xLocation;
this.yLocation = yLocation;
}
//Methods
}
public class Globals {
public final static int SCREEN_WIDTH = 800; // The screen width
public final static int SCREEN_HEIGHT = 600; // The screen height
public final static int DESIRED_FPS = 60; // Desired max FPS
}
package net.traverse.jumpingball;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferStrategy;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Screen extends Canvas{
private static final long serialVersionUID = 1L; //Version UID
//Variables used by the game loop itself
private int targetFPS; //The target FPS the game should run at, will be passed in the constructor
private int FPS; // The current actual frames per second being rendered
private long optimalTime; //the optimal time in nanoseconds
private long lastLoopTime; //time in nanoseconds of the last loop pass
Ball b; //
BufferStrategy strategy;
public Screen(int tFPS){
this.targetFPS = tFPS;
this.optimalTime = 1000000000 / tFPS;
this.FPS = 0;
this.lastLoopTime = System.nanoTime();
this.b= new Ball(30, 400, 300);
JFrame container = new JFrame("My first JFrame");
JPanel panel = (JPanel) container.getContentPane();
panel.setPreferredSize(new Dimension(Globals.SCREEN_WIDTH - 10, Globals.SCREEN_HEIGHT - 10));
panel.setLayout(null);
setBounds(0, 0, Globals.SCREEN_WIDTH, Globals.SCREEN_HEIGHT);
panel.add(this);
setIgnoreRepaint(true);
container.pack();
container.setLocationRelativeTo(null);
container.setResizable(false);
container.setVisible(true);
container.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
addKeyListener(new KeyboardHandler());
requestFocus();
setFocusable(true);
createBufferStrategy(2);
strategy = getBufferStrategy();
}
private class KeyboardHandler extends KeyAdapter
{
public void keyReleased(KeyEvent e)
{
int key = e.getKeyCode();
if (key == KeyEvent.VK_LEFT)
{
b.setDeltaX(0);
}
if (key == KeyEvent.VK_RIGHT)
{
b.setDeltaX(0);
}
}
public void keyPressed(KeyEvent e)
{
int key = e.getKeyCode();
if (key == KeyEvent.VK_LEFT)
{
b.setDeltaX(-10);
}
if (key == KeyEvent.VK_RIGHT)
{
b.setDeltaX(10);
}
if (key == KeyEvent.VK_UP)
{
if(b.getDeltaY()>=0){
b.setDeltaY(b.getDeltaY()+10);
}
else{
b.setDeltaY(b.getDeltaY()-10);
}
}
if (key == KeyEvent.VK_DOWN)
{
if(b.getDeltaY()>=0){
b.setDeltaY(b.getDeltaY()-10);
}
else{
b.setDeltaY(b.getDeltaY()+10);
}
}
}
public void keyTyped(KeyEvent e)
{
if (e.getKeyChar() == 27)
{
System.exit(0);
}
}
}
public void run(boolean gameRunning){
long lastFpsTime = 0;
int fpsUpdate=0;
while(gameRunning){
//this section works out how long it's been since the last update
// in order to calculate how far the entities should move on this pass
long now = System.nanoTime();
long updateLength = now - lastLoopTime;
lastLoopTime = now;
double delta = updateLength / ((double)optimalTime);
lastFpsTime += updateLength;
fpsUpdate++;
if (lastFpsTime >= 1000000000)
{
this.FPS = fpsUpdate;
lastFpsTime = 0;
fpsUpdate = 0;
}
//This is the game logic update function
gameRunning = doGameUpdates(delta);
//This function draws the updated entities to the screen
Graphics2D g2D = (Graphics2D) strategy.getDrawGraphics();
g2D.setColor(Color.BLACK);
g2D.fillRect(0, 0, Globals.SCREEN_WIDTH, Globals.SCREEN_HEIGHT);
renderEntities(g2D);
g2D.dispose();
strategy.show();
Toolkit.getDefaultToolkit().sync();
try{
Thread.sleep(((System.nanoTime()- lastLoopTime) + optimalTime)/1000000);
}
catch (InterruptedException e)
{
System.out.println("interrupted");
}
}
}
private boolean doGameUpdates(double delta){
boolean gameRunning = true;
/*
* Fill in with entities to be updated
*/
if (b.getyLocation() >= Globals.SCREEN_HEIGHT-(b.getRadius()*2)){
b.setDeltaY((-1*b.getDeltaY()));
}
else if(b.getyLocation() <= 0){
b.setDeltaY((-1*b.getDeltaY()));
}
else{
b.setDeltaY((b.getDeltaY()+5));
}
System.out.println(delta);
b.setyLocation(b.getyLocation()+ b.getDeltaY());
b.setxLocation(b.getxLocation()+b.getDeltaX());
return gameRunning;
}
private void renderEntities(Graphics2D g2D){
g2D.setFont(new Font("Courier New", Font.BOLD, 24));
g2D.setColor(Color.BLUE);
g2D.drawString("FPS : " + FPS, 0, 24);
g2D.fillOval((int)b.getxLocation(), (int)b.getyLocation(),b.getRadius()*2, b.getRadius()*2);
}
}
On the console I show the delta as outputting at 1. something fairly consistently, but when I run this the bouncing ball doesn’t seem smooth to me.