Finished project, how to add a background in NetBeans - java

I am using NetBeans IDE 8.2 and I created a simple clone game from tutorials.
I am looking to add a background to the app and my research keeps pointing to using JFrame Forms and a JLabel.
None of the tutorials touched on backgrounds or JFrame Forms/JLabels. So I am uncertain how to take my completed project and add a background. I have attempted to reproduce JFrame Forms and JLabel code only to be unable to put my classes/interfaces "inside?" or "on top?" of the JFrame Form/JLabel.
I apologize if this really isn't an ideal first question, I just signed up and this is my first dip into the Java pool. Game class with JFrame (not Form) settings
EDIT: Adding full paste of my Game.java class.
package game;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.image.BufferStrategy;
import java.util.ArrayList;
import javax.swing.*;
public class Game {
public final static int WIDTH = 1920, HEIGHT = 1080;
private String gameName = "Tutorial Game";
private Canvas game = new Canvas();
private Input input;
private ArrayList<Updatable> updatables = new ArrayList<>();
private ArrayList<Renderable> renderables = new ArrayList<>();
// Helper methods for update/render Arrays
public void addUpdatable(Updatable u) {
updatables.add(u);
}
public void removeUpdatable(Updatable u) {
updatables.remove(u);
}
public void addRenderable(Renderable r) {
renderables.add(r);
}
public void removeRenderable(Renderable r) {
renderables.remove(r);
}
public void start() {
// Initialize windows
Dimension gameSize = new Dimension(Game.WIDTH, Game.HEIGHT);
JFrame gameWindow = new JFrame(gameName);
gameWindow.setDefaultCloseOperation(3);
gameWindow.setSize(gameSize);
gameWindow.setResizable(false);
gameWindow.setLocationRelativeTo(null);
gameWindow.add(game);
game.setSize(gameSize);
game.setMinimumSize(gameSize);
game.setMaximumSize(gameSize);
game.setPreferredSize(gameSize);
gameWindow.setVisible(true);
// Initialize Input
input = new Input();
game.addKeyListener(input);
// Game loop
final int TICKS_PER_SECOND = 60;
final int TIME_PER_TICK = 1000 / TICKS_PER_SECOND;
final int MAX_FRAMESKIPS = 5;
long nextGameTick = System.currentTimeMillis();
int loops;
float interpolation;
long timeAtLastFPSCheck = 0;
int ticks = 0;
boolean running = true;
while(running) {
// Updating
loops = 0;
while(System.currentTimeMillis() > nextGameTick && loops < MAX_FRAMESKIPS) {
update();
ticks++;
nextGameTick += TIME_PER_TICK;
loops++;
}
// Rendering
interpolation = (float) (System.currentTimeMillis() + TIME_PER_TICK - nextGameTick)
/ (float) TIME_PER_TICK;
render(interpolation);
// FPS Check
if(System.currentTimeMillis() - timeAtLastFPSCheck >= 1000) {
System.out.println("FPS: " + ticks);
gameWindow.setTitle(gameName + " - FPS: " + ticks);
ticks = 0;
timeAtLastFPSCheck = System.currentTimeMillis();
}
}
}
private void update() {
for(Updatable u : updatables) {
u.update(input);
}
}
private void render(float interpolation) {
BufferStrategy b = game.getBufferStrategy();
if(b == null) {
game.createBufferStrategy(2);
return;
}
Graphics2D g = (Graphics2D) b.getDrawGraphics();
g.clearRect(0, 0, game.getWidth(), game.getHeight());
for(Renderable r : renderables) {
r.render(g, interpolation);
}
g.dispose();
b.show();
}
}

Related

Why is my GPU over 75°C when I run an undemanding Java game?

I have recently moved to Linux Mint 19.3 and run my Java game. In one hand I have FPS that's higher than on Windows 10 (~150 FPS VS ~4000 FPS). But in another hand my game is very lag. And I don't understand why because I don't have this problem in Windows 10. Actually, I am not good at computer graphics, but I think it's problem in VSync or so. That's the screenshot of my GPU temperature and FPS.
GameContainer.java
import javax.swing.JFrame;
import java.awt.*;
import java.awt.image.*;
public class GameContainer {
public static int width = 320;
public static int height = 240;
public static int scale = 3;
public static String title = "Space Adventure";
public Window window;
public BufferStrategy bs;
public Graphics2D g;
public GameManager gm;
public KeyInput ki;
public void start() {
window.canvas.requestFocus();
long previous = System.currentTimeMillis();
long current = 0;
double elapsed = 0;
double timePerTick = 1000.0 / 60;
int ticks = 0;
int frames = 0;
long timer = previous;
while (true) {
current = System.currentTimeMillis();
elapsed += (current - previous) / timePerTick;
previous = current;
while (elapsed >= 1) {
tick(timePerTick);
ticks++;
elapsed -= 1;
}
render();
frames++;
if (System.currentTimeMillis() - timer >= 1000) {
System.out.printf("UPS: %3d, FPS: %3d%n", ticks, frames);
timer += 1000;
ticks = 0;
frames = 0;
}
}
}
public void tick(double delta) {
gm.tick(delta);
}
public void render() {
bs = window.canvas.getBufferStrategy();
if (bs == null) {
window.canvas.createBufferStrategy(2);
return;
}
g = (Graphics2D) bs.getDrawGraphics();
g.scale(scale, scale);
gm.render(g);
g.dispose();
bs.show();
}
public void init(GameManager gm) {
window = new Window();
window.create(width , height, scale, title);
this.gm = gm;
ki = new KeyInput();
window.canvas.addKeyListener(ki);
}
}

Created a game where the Frame's width is accurate after I run it but the height never updates

I'm learning to make games using a a tutorial on youtube. Everything seems fine except when I run the program. A frame shows up with the accurate width I want but the height looks like it sets to a default no matter what value I give it.
package ca.vanzeben.game;
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import javax.swing.JFrame;
public class Game extends Canvas implements Runnable {
private static final long serialVersionUID = 1L;
public static final int WIDTH = 160;
public static final int Height = WIDTH/ 12*9;
public static final int SCALE = 3; // able to move screen
public static final String NAME = "Juego";
private JFrame frame;
public boolean running = false;
public int tickCount = 0;
private BufferedImage image = new BufferedImage(WIDTH,HEIGHT,BufferedImage.TYPE_INT_RGB);
private int[] pixels = ((DataBufferInt)image.getRaster().getDataBuffer()).getData();
public Game() { //game constructor
setMinimumSize(new Dimension(WIDTH*SCALE,HEIGHT*SCALE));
setMaximumSize(new Dimension(WIDTH*SCALE,HEIGHT*SCALE));
setPreferredSize(new Dimension(WIDTH*SCALE,HEIGHT*SCALE));
frame = new JFrame(NAME);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // closes game completely
frame.setLayout(new BorderLayout());
frame.add(this,BorderLayout.CENTER); //adds canvas to JFrame and centers it
frame.pack();//keeps everything sized correctly
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public synchronized void start() {//so u can start from the applet
running = true;
new Thread(this).start();
}
public synchronized void stop(){
running = false;
}
public void run() {
long lastTime = System.nanoTime();
double nsPerTick = 1000000000.0/60; //nanoseconds per tick or per update
int ticks = 0;
int frames = 0;
long lastTimer = System.currentTimeMillis();
double delta = 0.0; //how many unprocessed nano seconds
while(running){
long now = System.nanoTime();
delta+=(now-lastTime)/nsPerTick;
lastTime = now;
boolean shouldRender = true;
while(delta>=1){
ticks++;
tick();
delta -= 1;
shouldRender = true;
}
try{
Thread.sleep(2);
}catch(InterruptedException e){
e.printStackTrace();
}
if(shouldRender){
frames++;
render();
}
if(System.currentTimeMillis()-lastTimer>=1000){
lastTimer += 1000;
System.out.println(frames + " frames " + ticks + " ticks ");
frames = 0;
ticks = 0;
}
}
}
public void tick(){ //updates the game, updates the logic
tickCount++;
for(int i =0;i<pixels.length;i++){
pixels[i] = i + tickCount;
}
}
public void render(){ //prints out ^
BufferStrategy bs = getBufferStrategy();
if(bs == null){
createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
g.setColor(Color.BLACK);
g.drawRect(0, 0, getWidth(), getHeight());
g.dispose();
bs.show();
}
public static void main(String[] args){
new Game().start();
}
}
You defined Height and used HEIGHT. Change it to:
public static final int HEIGHT = WIDTH / 12 * 9;
This is how it looked like to me:
Before:
After:

JPanel image flies from the screen

I'm trying to make my Pedestrian object move, and it moves but at a certain point it flies away from the screen. The Pedestrian moves by a List of points. First the Pedestrian is added to toDraw to paint it and in startAndCreateTimer I loop through the same list to move the Vehicles Maybe it's because of this line i = (double) diff / (double) playTime; I actually don't want to set a playtime how not to do that, could this be the problem or is it something else? Here a link with the point where the Pedestrian flies away (starts north of left roundabout) http://gyazo.com/23171a6106c88f1ba8ca438598ff4153.
class Surface extends JPanel{
Track track=new Track();
public List<Vehicle> toDraw = new ArrayList<>();
private Long startTime;
private long playTime = 4000;
private double i;
public Surface(){
startAndCreateTimer();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
//Make sure the track is painted first
track.paint(g);
for (Vehicle v : toDraw) {
v.paint(g);
}
}
public void repaintPanel(){
this.repaint();
}
private void startAndCreateTimer(){
Timer timer = new Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (startTime == null) {
startTime = System.currentTimeMillis();
}
long now = System.currentTimeMillis();
long diff = now - startTime;
i = (double) diff / (double) playTime;
for (Vehicle v : toDraw){
v.update(i);
}
repaintPanel();
}
});
timer.start();
}
}
Pedestrian java
public class Pedestrian extends Vehicle {
BufferedImage pedestrian;
Point pedestrianPosition;
double pedestrianRotation = 0;
int pedestrianW, pedestrianH;
int counter=0;
List<LanePoint>pedestrianPath;
boolean lockCounter=false;
public Pedestrian(int x, int y){
try {
pedestrian = ImageIO.read(Car.class.getResource("images/human.png"));
} catch (IOException e) {
System.out.println("Problem loading pedestrian images: " + e);
}
pedestrianPosition = new Point(x,y);
pedestrianW = pedestrian.getWidth();
pedestrianH = pedestrian.getHeight();
}
#Override
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.rotate(Math.toRadians(pedestrianRotation), pedestrianPosition.x, pedestrianPosition.y);
g2d.drawImage(pedestrian, pedestrianPosition.x, pedestrianPosition.y, null);
}
#Override
public void setPath(List<LanePoint> path) {
pedestrianPath=path;
}
/*Update*/
#Override
public void update(double i){
if (counter < pedestrianPath.size()) {
Point startPoint = new Point(pedestrianPosition.x, pedestrianPosition.y);
LanePoint endPoint = new LanePoint(pedestrianPath.get(counter).x, pedestrianPath.get(counter).y,pedestrianPath.get(counter).lanePointType,pedestrianPath.get(counter).lanePointToTrafficLight,pedestrianPath.get(counter).laneTrafficLightId,pedestrianPath.get(counter).degreesRotation);
pedestrianPosition.x=(int)Maths.lerp(startPoint.x,endPoint.x,i);
pedestrianPosition.y=(int)Maths.lerp(startPoint.y,endPoint.y,i);
pedestrianRotation=endPoint.degreesRotation;
if(pedestrianPosition.equals(new Point(endPoint.x,endPoint.y))){
/*PEDESTRIAN SIGN UP*/
if (endPoint.lanePointType.equals(LanePoint.PointType.TRAFFICLIGHT) && endPoint.lanePointToTrafficLight.equals(LanePoint.PointToTrafficLight.INFRONTOF)){
try {
Roundabout.client.sendBytes(new byte []{0x03,endPoint.laneTrafficLightId.byteValue(),0x01,0x00});
} catch (IOException ex) {
ex.printStackTrace();
}
}
/*PEDESTRIAN SIGN OFF*/
else if (endPoint.lanePointType.equals(LanePoint.PointType.TRAFFICLIGHT) && endPoint.lanePointToTrafficLight.equals(LanePoint.PointToTrafficLight.UNDERNEATH)) {
if (Surface.trafficLights.get(endPoint.laneTrafficLightId).red) {
lockCounter = true;
} else {
try {
Roundabout.client.sendBytes(new byte[]{0x03, endPoint.laneTrafficLightId.byteValue(), 0x00, 0x00});
lockCounter=false;
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
if (!lockCounter) {
counter++; //Increment counter > sets next point
}
}
}
}
}
Maths.java
public class Maths {
//Lineat interpolation
public static double lerp(double a, double b, double t) {
return a + (b - a) * t;
}
}
So, basically you are calculating the position of the object between to points based on the amount of time that has passed. This is good.
So at t = 0, the object will be at the start point, at t = 0.5, it will be halfway between the start and end point, at t = 1.0 it will be at the end point.
What happens when t > 1.0? Where should the object be? - hint, it should be nowhere as it should have been removed or reset...
This and this are basic examples of "time line" based animation, meaning that, over a period of time, the position of the object is determined by using different points (along a time line)
So, in order to calculate the position along a line, you need three things, the point you started at, the point you want to end at and the duration (between 0-1)
Using these, you can calculate the point along the line between these two points based on the amount of time.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class TestPane extends JPanel {
protected static final double PLAY_TIME = 4000.0;
private Point2D startAt = new Point(0, 0);
private Point2D endAt = new Point(200, 200);
private Point2D current = startAt;
private Long startTime;
public TestPane() {
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (startTime == null) {
startTime = System.currentTimeMillis();
}
long time = System.currentTimeMillis() - startTime;
double percent = (double) time / PLAY_TIME;
if (percent > 1.0) {
percent = 1.0;
((Timer) e.getSource()).stop();
}
current = calculateProgress(startAt, endAt, percent);
repaint();
}
});
timer.start();
}
protected Point2D calculateProgress(Point2D startPoint, Point2D targetPoint, double progress) {
Point2D point = new Point2D.Double();
if (startPoint != null && targetPoint != null) {
point.setLocation(
calculateProgress(startPoint.getX(), targetPoint.getY(), progress),
calculateProgress(startPoint.getX(), targetPoint.getY(), progress));
}
return point;
}
protected double calculateProgress(double startValue, double endValue, double fraction) {
return startValue + ((endValue - startValue) * fraction);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.GREEN);
g2d.draw(new Line2D.Double(startAt, endAt));
g2d.setColor(Color.RED);
g2d.fill(new Ellipse2D.Double(current.getX() - 5, current.getY() - 5, 10, 10));
g2d.dispose();
}
}
}
So, using current = calculateProgress(startAt, endAt, percent);,
you can see that the dot moves evenly between the start and end points.
If we change it to something more like what you seem to be doing, current = calculateProgress(current, endAt, percent);,
you can see that it speeds down the line and finally eases out, which isn't what you really want...
Updated with time line theory
Let's imagine you have a time line, which has a length of t and along this time line, you have 5 events (or key frames) (e1 - e5), each occurring after each other.
e1 starts at 0 and e5 ends at 1
As you can see, the events occur at irregular intervals and run for different lengths of time.
t1 runs for 25% of the time line
t2 runs for 25% of the time line
t3 runs for 12.5% of the time line
t3 runs for 37.5% of the time line
So, based on t, you need to determine which events are been executed. So when t is 0.12, we are running about half way through t1 (between e1 & e2).
You then need to calculate local time/difference between the key frames (0-0.25 along the timeline)
localTime = 1.0 - ((t - e1) / (e2 - e1))
= 1.0 - ((0.12 - 0) / (0.25 - 0))
= 1.0 - (0.12 / 0.25)
= 1.0 - 0.48
= 0.52
Where t is the time along the time line, e1 is the time of the first event (0) and e2 is the time of the second event (0.25), which gives us the duration along the t1 (in this example)
This is then the value of your linear interpolation for the given time slice.
Runnable example...
I took a look at your code, but there's a lot of work that needs to be done to get this to work.
Basically, you need to know how long the path is and the amount that each segment is of that path (as a percentage). With this, we can create a "time line" of "key frames" which determines how far along the "path" your object is based on the amount of time that has passed and the amount of time it "should" take to travel.
So, the first thing I did was create a Path class (kind of mimics your Lists, but has some additional methods)
public class Path implements Iterable<Point> {
private List<Point> points;
private double totalLength = 0;
public Path(Point... points) {
this.points = new ArrayList<>(Arrays.asList(points));
for (int index = 0; index < size() - 1; index++) {
Point a = get(index);
Point b = get(index + 1);
double length = lengthBetween(a, b);
totalLength += length;
}
}
public double getTotalLength() {
return totalLength;
}
public int size() {
return points.size();
}
public Point get(int index) {
return points.get(index);
}
public double lengthBetween(Point a, Point b) {
return Math.sqrt(
(a.getX() - b.getX()) * (a.getX() - b.getX())
+ (a.getY() - b.getY()) * (a.getY() - b.getY()));
}
#Override
public Iterator<Point> iterator() {
return points.iterator();
}
}
Mostly, this provides the totalLength of the path. We use this to calculate how much each segment takes up later
I then borrowed the TimeLine class from this previous answer
public class Timeline {
private Map<Double, KeyFrame> mapEvents;
public Timeline() {
mapEvents = new TreeMap<>();
}
public void add(double progress, Point p) {
mapEvents.put(progress, new KeyFrame(progress, p));
}
public Point getPointAt(double progress) {
if (progress < 0) {
progress = 0;
} else if (progress > 1) {
progress = 1;
}
KeyFrame[] keyFrames = getKeyFramesBetween(progress);
double max = keyFrames[1].progress - keyFrames[0].progress;
double value = progress - keyFrames[0].progress;
double weight = value / max;
return blend(keyFrames[0].getPoint(), keyFrames[1].getPoint(), 1f - weight);
}
public KeyFrame[] getKeyFramesBetween(double progress) {
KeyFrame[] frames = new KeyFrame[2];
int startAt = 0;
Double[] keyFrames = mapEvents.keySet().toArray(new Double[mapEvents.size()]);
while (startAt < keyFrames.length && keyFrames[startAt] <= progress) {
startAt++;
}
if (startAt >= keyFrames.length) {
startAt = keyFrames.length - 1;
}
frames[0] = mapEvents.get(keyFrames[startAt - 1]);
frames[1] = mapEvents.get(keyFrames[startAt]);
return frames;
}
protected Point blend(Point start, Point end, double ratio) {
Point blend = new Point();
double ir = (float) 1.0 - ratio;
blend.x = (int) (start.x * ratio + end.x * ir);
blend.y = (int) (start.y * ratio + end.y * ir);
return blend;
}
public class KeyFrame {
private double progress;
private Point point;
public KeyFrame(double progress, Point point) {
this.progress = progress;
this.point = point;
}
public double getProgress() {
return progress;
}
public Point getPoint() {
return point;
}
}
}
Now, as they stand, they are not compatible, we need to take each segment and calculate the length of the segment as a percentage of the total length of the path and create a key frame for the specified point along the time line...
double totalLength = path.getTotalLength();
timeLine = new Timeline();
timeLine.add(0, path.get(0));
// Point on time line...
double potl = 0;
for (int index = 1; index < path.size(); index++) {
Point a = path.get(index - 1);
Point b = path.get(index);
double length = path.lengthBetween(a, b);
double normalised = length / totalLength;
// Normalised gives as the percentage of this segment, we need to
// translate that to a point on the time line, so we just add
// it to the "point on time line" value to move to the next point :)
potl += normalised;
timeLine.add(potl, b);
}
I did this deliberately, to show the work you are going to need to do.
Need, I create a Ticker, which just runs a Swing Timer and reports ticks to Animations
public enum Ticker {
INSTANCE;
private Timer timer;
private List<Animation> animations;
private Ticker() {
animations = new ArrayList<>(25);
timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// Prevent possible mutatation issues...
Animation[] anims = animations.toArray(new Animation[animations.size()]);
for (Animation animation : anims) {
animation.tick();
}
}
});
}
public void add(Animation animation) {
animations.add(animation);
}
public void remove(Animation animation) {
animations.remove(animation);
}
public void start() {
timer.start();
}
public void stop() {
timer.stop();
}
}
public interface Animation {
public void tick();
}
This centralises the "clock", be allows Animations to determine what they would like to do on each tick. This should be more scalable then creating dozens of Timers
Okay, that's all fun and games, but how does it work together? Well, here's a complete runnable example.
It takes one of your own paths and creates a TimeLine out of it and animates a object moving along it.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
Path path = new Path(
new Point(440, 40),
new Point(440, 120),
new Point(465, 90),
new Point(600, 180),
new Point(940, 165),
new Point(940, 145),
new Point(1045, 105),
new Point(1080, 120),
new Point(1170, 120),
new Point(1200, 120),
new Point(1360, 123),
new Point(1365, 135),
new Point(1450, 170),
new Point(1457, 160),
new Point(1557, 160));
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane(path));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Ticker.INSTANCE.start();
}
});
}
public enum Ticker {
INSTANCE;
private Timer timer;
private List<Animation> animations;
private Ticker() {
animations = new ArrayList<>(25);
timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// Prevent possible mutatation issues...
Animation[] anims = animations.toArray(new Animation[animations.size()]);
for (Animation animation : anims) {
animation.tick();
}
}
});
}
public void add(Animation animation) {
animations.add(animation);
}
public void remove(Animation animation) {
animations.remove(animation);
}
public void start() {
timer.start();
}
public void stop() {
timer.stop();
}
}
public interface Animation {
public void tick();
}
public static final double PLAY_TIME = 4000d;
public class TestPane extends JPanel implements Animation {
private Path path;
private Path2D pathShape;
private Timeline timeLine;
private Long startTime;
private Point currentPoint;
public TestPane(Path path) {
this.path = path;
// Build the "path" shape, we can render this, but more importantally
// it allows use to determine the preferred size of the panel :P
pathShape = new Path2D.Double();
pathShape.moveTo(path.get(0).x, path.get(0).y);
for (int index = 1; index < path.size(); index++) {
Point p = path.get(index);
pathShape.lineTo(p.x, p.y);
}
// Build the time line. Each segemnt (the line between any two points)
// makes up a percentage of the time travelled, we need to calculate
// the amount of time that it would take to travel that segement as
// a percentage of the overall length of the path...this
// allows us to even out the time...
double totalLength = path.getTotalLength();
timeLine = new Timeline();
timeLine.add(0, path.get(0));
// Point on time line...
double potl = 0;
for (int index = 1; index < path.size(); index++) {
Point a = path.get(index - 1);
Point b = path.get(index);
double length = path.lengthBetween(a, b);
double normalised = length / totalLength;
// Normalised gives as the percentage of this segment, we need to
// translate that to a point on the time line, so we just add
// it to the "point on time line" value to move to the next point :)
potl += normalised;
timeLine.add(potl, b);
}
currentPoint = path.get(0);
Ticker.INSTANCE.add(this);
}
#Override
public Dimension getPreferredSize() {
Dimension size = pathShape.getBounds().getSize();
size.width += pathShape.getBounds().x;
size.height += pathShape.getBounds().y;
return size;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.GREEN);
g2d.draw(pathShape);
g2d.setColor(Color.RED);
g2d.fill(new Ellipse2D.Double(currentPoint.x - 5, currentPoint.y - 5, 10, 10));
g2d.dispose();
}
#Override
public void tick() {
if (startTime == null) {
startTime = System.currentTimeMillis();
}
long diff = System.currentTimeMillis() - startTime;
double t = (double)diff / PLAY_TIME;
if (t > 1.0) {
t = 1.0d;
// Don't call me any more, I'm already home
Ticker.INSTANCE.remove(this);
}
currentPoint = timeLine.getPointAt(t);
repaint();
}
}
public class Path implements Iterable<Point> {
private List<Point> points;
private double totalLength = 0;
public Path(Point... points) {
this.points = new ArrayList<>(Arrays.asList(points));
for (int index = 0; index < size() - 1; index++) {
Point a = get(index);
Point b = get(index + 1);
double length = lengthBetween(a, b);
totalLength += length;
}
}
public double getTotalLength() {
return totalLength;
}
public int size() {
return points.size();
}
public Point get(int index) {
return points.get(index);
}
public double lengthBetween(Point a, Point b) {
return Math.sqrt(
(a.getX() - b.getX()) * (a.getX() - b.getX())
+ (a.getY() - b.getY()) * (a.getY() - b.getY()));
}
#Override
public Iterator<Point> iterator() {
return points.iterator();
}
}
public class Timeline {
private Map<Double, KeyFrame> mapEvents;
public Timeline() {
mapEvents = new TreeMap<>();
}
public void add(double progress, Point p) {
mapEvents.put(progress, new KeyFrame(progress, p));
}
public Point getPointAt(double progress) {
if (progress < 0) {
progress = 0;
} else if (progress > 1) {
progress = 1;
}
KeyFrame[] keyFrames = getKeyFramesBetween(progress);
double max = keyFrames[1].progress - keyFrames[0].progress;
double value = progress - keyFrames[0].progress;
double weight = value / max;
return blend(keyFrames[0].getPoint(), keyFrames[1].getPoint(), 1f - weight);
}
public KeyFrame[] getKeyFramesBetween(double progress) {
KeyFrame[] frames = new KeyFrame[2];
int startAt = 0;
Double[] keyFrames = mapEvents.keySet().toArray(new Double[mapEvents.size()]);
while (startAt < keyFrames.length && keyFrames[startAt] <= progress) {
startAt++;
}
if (startAt >= keyFrames.length) {
startAt = keyFrames.length - 1;
}
frames[0] = mapEvents.get(keyFrames[startAt - 1]);
frames[1] = mapEvents.get(keyFrames[startAt]);
return frames;
}
protected Point blend(Point start, Point end, double ratio) {
Point blend = new Point();
double ir = (float) 1.0 - ratio;
blend.x = (int) (start.x * ratio + end.x * ir);
blend.y = (int) (start.y * ratio + end.y * ir);
return blend;
}
public class KeyFrame {
private double progress;
private Point point;
public KeyFrame(double progress, Point point) {
this.progress = progress;
this.point = point;
}
public double getProgress() {
return progress;
}
public Point getPoint() {
return point;
}
}
}
}
Now, if I was doing this, I would create a method either in Path or as a static utility method, that took a Path and returned a TimeLine automatically ;)

Testing out simple features for a game, cannot get an object to jump

I am trying to get a ball to bounce up and down when you press the up arrow key. I can get the ball to move up but it will not stop moving up. I have written the code for the ball movement in my update method. I am trying to get the ball to stop at y coordinate 400, but it just passes it up.
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
public class Game extends Canvas implements Runnable {
int x,y;
public static int width = 300;
public static int height = width / 16 * 9;
public static int scale = 3;
private boolean running = false;
public Thread thread;
public JFrame frame;
public Keyboard key;
private BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
private int[] pixels = ((DataBufferInt)image.getRaster().getDataBuffer()).getData();
public Game() {
Dimension size = new Dimension(width * scale, height * scale);
setPreferredSize(size);
frame = new JFrame();
key = new Keyboard();
addKeyListener(key);
}
public synchronized void start() {
running = true;
thread = new Thread(this, "Display");
thread.start();
}
public synchronized void stop(){
running = false;
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
#Override
public void run() {
long lastTime = System.nanoTime();
final double nanoSeconds = 1000000000.0 / 60.0;
double delta = 0;
int frames = 0;
int gameUpdates = 0;
long timer = System.currentTimeMillis();
requestFocus();
while(running){
long now = System.nanoTime();
delta += (now - lastTime) / nanoSeconds;
lastTime = now;
//this ensures that delta only updates the screen 60 times each second
while (delta >= 1){
update();
gameUpdates++;
delta--;
}
render();
frames++;
//this if statement happens once a second
if (System.currentTimeMillis() - timer > 1000){
timer += 1000;
frame.setTitle("Bouncy Ball! ~ ~ ~ Updates per second: " + gameUpdates + ". Frames per second: " + frames + ".");
gameUpdates = 0;
frames = 0;
}
}
stop();
}
int yy;
public void update() {
key.update();
y = y + yy;
if(key.up){
yy = -5;
if(y == 400){
yy = 0;}
}
}
public void render() {
BufferStrategy bs = getBufferStrategy();
if (bs == null){
createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
g.drawImage(image,0,0,getWidth(),getHeight(),null);
g.setColor(Color.DARK_GRAY);
g.fillRect(0,0,getWidth(),getHeight());
g.setColor(Color.MAGENTA);
g.fillOval(300,y+435,50,50);
g.dispose();
bs.show();
}
public static void main(String[] args) {
Game game = new Game();
game.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
game.frame.setResizable(false);
game.frame.setVisible(true);
game.frame.add(game);
game.frame.pack();
game.frame.setTitle("Bouncy Ball");
game.start();
}
}
This is my keylisener class
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class Keyboard implements KeyListener{
private boolean[] keys = new boolean[120];
public boolean up, down, left, right;
public void update(){
up = keys[KeyEvent.VK_UP];
down = keys[KeyEvent.VK_DOWN];
left = keys[KeyEvent.VK_LEFT];
right = keys[KeyEvent.VK_RIGHT];
}
#Override
public void keyPressed(KeyEvent e) {
keys[e.getKeyCode()] = true;
}
#Override
public void keyReleased(KeyEvent e) {
keys[e.getKeyCode()] = false;
}
#Override
public void keyTyped(KeyEvent e) {
}
}
Your issue is in the key.update() method. key.up will only be true while you're holding the up key, not just if you press the up key once. If, for now, you only care about a single button press, change it to:
public void update() {
if (keys[KeyEvent.VK_UP]) {
up = true;
}
}
Which will only update once, once you press up, and keys.up will stay true.
Also, y is decreasing, not increasing (you're subtracting 5 from it), so you want to change
== 400 to == 400, or even better, <= 400, in case you don't increment with whole numbers (as per the other answer).
As a general debugging idea: At that if statement, I printed out the value of y, and noticed it only printed out 3 or 4 values (corresponding to how long I was holding it down), which meant that the if statement wasn't being checked, or wasn't returning true (which was the case here).
Instead of:
if(key.up){
yy = -5;
if(y == 400){
yy = 0;}
}
try:
if(key.up){
yy = -5;
if(y >= 400){
yy = 0;}
}
If your ball is moving at 5px per update then it may pass 400px without actually
equaling 400px, this is why you check if it is greater than 400px.

Java javax.swing.Timer duration

I need to execute N calculations (with graphics updates) in X seconds using javax.swing.Timer.
What's the best way for do this?
If I set a fixed delay for the timer I exceed the X seconds, because the time required for each execution is delay + calculations.
To work around this I've tried to set the delay of the timer dynamically, but still the time is not accurate.
As a last chance I've tried to set the dalay to 1ms and use Thread.sleep(sleepTime) for control the duration and this work perfectly, without affect the animations.
My question is:
Is this a good solution? Can I use Thread.sleep(sleepTime) inside the javax.swing.Timer?
Edit, here's some code to better understand. I want to say that the graphics is needed only to see if the movements are correct, in the final version i only need to update the game and generate a report of the match result.
Main frame with "game loop":
package engine.test;
import engine.entity.Skill;
import engine.math.Point;
import engine.math.Vector;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.Timer;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
public final class EngineFrame extends JFrame implements ActionListener
{
public static final int SCALE = 6;
public static final int WIDTH = 100 * SCALE;
public static final int HEIGHT = 60 * SCALE;
public static final int DURATION = 2; // animation duration in seconds
public static final int FPS = 60;
public static final int SKIP_TICKS = 1000 / FPS;
private final JSONObject data;
private final ArrayList<Player> players;
private PlayersPanel playersPanel;
public EngineFrame(String title) throws JSONException, FileNotFoundException
{
super(title);
setSize(WIDTH, HEIGHT);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
createMenu();
data = new JSONObject(loadData());
players = buildPlayers(data.getJSONArray("teams").getJSONObject(0).getJSONArray("players"));
playersPanel = new PlayersPanel(players);
add(playersPanel);
}
public ArrayList<Player> getPlayers()
{
return players;
}
/**
* Returns the data required to run a match.
*
* #return a json string representing the match data
* #throws FileNotFoundException
*/
private String loadData() throws FileNotFoundException
{
Scanner fileInput = new Scanner(new File(
"C:\\Users\\packard bell\\Documents\\JavaProjects\\Engine\\src\\resources\\data.json"
));
String jsonText = "";
while (fileInput.hasNextLine()) {
jsonText += fileInput.nextLine();
}
return jsonText;
}
/**
* Creates and returns the player entities.
*
* #param playersData
* #return
*/
private ArrayList<Player> buildPlayers(JSONArray playersData) throws JSONException
{
ArrayList<Player> players = new ArrayList<Player>();
JSONObject playerData;
Player player;
for (int i = 0, l = playersData.length(); i < l; i++) {
playerData = playersData.getJSONObject(i);
player = new Player.Builder()
.setId(playerData.getInt("id"))
.setFirstName(playerData.getString("first_name"))
.setLastName(playerData.getString("last_name"))
.setMass(playerData.getInt("weight"))
.setSkills(buildSkills(playerData.getJSONObject("skills")))
.setInitialPosition(new Point(0, i * 10 + 20))
.setInitialVelocity(new Vector(0, 0))
.build();
players.add(player);
}
return players;
}
/**
*
*/
private Map<Skill, Double> buildSkills(JSONObject skillsData) throws JSONException
{
Map<Skill, Double> skills = new HashMap();
for (Skill skill : Skill.values()) {
skills.put(
skill,
skill.getMinValue() + (skill.getMaxValue() - skill.getMinValue()) * (skillsData.getDouble(skill.getName()) / 100)
);
}
return skills;
}
/**
*
*/
private void createMenu()
{
JMenu seekMenu = new JMenu("Seek behavior");
JMenuItem initSeek = new JMenuItem("Init seek");
initSeek.addActionListener(this);
JMenuItem runSeek = new JMenuItem("Run seek");
runSeek.addActionListener(this);
JMenuItem stopSeek = new JMenuItem("Stop seek");
stopSeek.addActionListener(this);
seekMenu.add(initSeek);
seekMenu.add(runSeek);
seekMenu.add(stopSeek);
JMenuBar bar = new JMenuBar();
bar.add(seekMenu);
setJMenuBar(bar);
}
public static void main(String[] args) throws JSONException, FileNotFoundException, InterruptedException
{
EngineFrame frame = new EngineFrame("Engine");
}
#Override
public void actionPerformed(ActionEvent e)
{
String menuString = e.getActionCommand();
if (menuString.equalsIgnoreCase("init seek")) {
Player player1 = getPlayers().get(0);
Player player2 = getPlayers().get(1);
player1.setPosition(new Point(0, 20));
player1.setVelocity(new Vector(0, 0));
player2.setPosition(new Point(0, 30));
player2.setVelocity(new Vector(0, 0));
repaint();
}
else if (menuString.equalsIgnoreCase("run seek")) {
Timer t = new Timer(1, new ActionListener() {
private final long start = System.currentTimeMillis();
private long nextGameUpdate = start;
private long sleepTime = 0;
private int loops = DURATION * 1000 / SKIP_TICKS;
#Override
public void actionPerformed(ActionEvent e) {
Player player1 = getPlayers().get(0);
Player player2 = getPlayers().get(1);
//System.out.println("Position: " + player1.getPosition());
//System.out.println("Velocity: " + player1.getVelocity());
System.out.println();
player1.getSteering().seek(new Point(50, 20));
player2.getSteering().seek(new Point(50, 30));
player1.update();
player2.update();
repaint();
nextGameUpdate += SKIP_TICKS;
sleepTime = nextGameUpdate - System.currentTimeMillis();
//System.out.println(nextGameUpdate);
//System.out.println(sleepTime);
loops--;
if (sleepTime >= 0) {
try {
Thread.sleep(sleepTime);
} catch (InterruptedException ex) {
Logger.getLogger(EngineFrame.class.getName()).log(Level.SEVERE, null, ex);
}
}
if (loops <= 0) {
((Timer)e.getSource()).stop();
long end = System.currentTimeMillis();
// should be 2000ms (equals to DURATION constant)
System.out.println("Duration: " + (end - start) + "ms");
}
}
});
t.setInitialDelay(0);
t.start();
}
}
// version without swing timer (it works if called in the main method)
private void runSeek() throws InterruptedException
{
Player player1 = getPlayers().get(0);
Player player2 = getPlayers().get(1);
player1.setPosition(new Point(0, 20));
player2.setPosition(new Point(0, 30));
// run
long start = System.currentTimeMillis();
long nextGameUpdate = start;
long sleepTime = 0;
int i = DURATION * 1000 / SKIP_TICKS;
System.out.println("Loop executions: " + i);
int steps = 0;
String stepsCode = "[";
String velocitiesCode = "[";
String positionsCode = "[";
while (i > 0) {
stepsCode += steps + ", ";
velocitiesCode += player1.getVelocity().len() + ", ";
positionsCode += player1.getPosition().toVector().len() + ", ";
System.out.println("Position: " + player1.getPosition());
System.out.println("Velocity: " + player1.getVelocity());
System.out.println();
player1.getSteering().seek(new Point(50, 20));
player2.getSteering().seek(new Point(50, 30));
player1.update();
player2.update();
repaint();
nextGameUpdate += SKIP_TICKS;
sleepTime = nextGameUpdate - System.currentTimeMillis();
steps += sleepTime;
//System.out.println(sleepTime);
if (sleepTime >= 0) {
Thread.sleep(sleepTime);
}
i--;
}
stepsCode = stepsCode.substring(0, stepsCode.length() - 2) + "]";
velocitiesCode = velocitiesCode.substring(0, velocitiesCode.length() - 2) + "]";
positionsCode = positionsCode.substring(0, positionsCode.length() - 2) + "]";
long end = System.currentTimeMillis();
System.out.println("Duration: " + (end - start) + "ms");
System.out.println("Steps:");
System.out.println(stepsCode);
System.out.println("Positions:");
System.out.println(positionsCode);
System.out.println("Velocities:");
System.out.println(velocitiesCode);
}
}
Here's the JPanel that draw the entities:
package engine.test;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.util.ArrayList;
import javax.swing.JPanel;
public class PlayersPanel extends JPanel
{
private ArrayList<Player> players;
public PlayersPanel(ArrayList<Player> players)
{
this.players = players;
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for (Player player : players) {
int x = (int) (player.getPosition().x() * EngineFrame.SCALE);
int y = (int) (player.getPosition().y() * EngineFrame.SCALE);
g2.setColor(Color.BLACK);
g2.fillArc(x, y, 18, 18, 0, 360);
g2.setColor(new Color(0x11539f));
g2.fillArc(x + 2, y + 2, 14, 14, 0, 360);
}
}
}
Is this a good solution? Can I use Thread.sleep(sleepTime) inside the javax.swing.Timer?
No, never do this. Don't worry about changing the delay. Instead consider what it is you want to draw and when you want to draw them. You can create a class for the different object to draw, and have then maintain a delayed state that will determine when they should be drawn. If you want to change an object velocity, then increase the number of pixels you increment it's movement.
Here's an example you may find useful, that shows my first point of maintaining a delayed state. You can see the balls are thrown at different times.
Other than that, you should post some code that show's exactly what you're trying to do. Your question is somewhat vague.

Categories

Resources