The Constructor Window() is not visible - java

iam just starting programming in Java. Have issue with classes. As i try to use Windows in my project it keep shows this errors: Multiple markers at this line
- The constructor Window() is not visible
- The constructor Window(GameContainer) is
undefined
i tried make my class public but it didnt help. Can anyone helps me? Error is showing on Window lines in main class. Thanks anyone for helpss :)
main class:
package g_core_engine;
import java.awt.Window;
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
public class GameContainer implements Runnable{
private Thread thread;
private Window window;
private boolean running = false;
private final double UPDATE_CAP = 1.0 / 60.0;
private int width = 320, height = 240;
private float Scale = 4;
private String title = "g_core_engine v1.0";
public GameContainer()
{
}
//Method that starts the actual computing of the game
public void start()
{
window = new Window();
thread = new Thread(this);
thread.run();
}
public void stop()
{
}
//Method that contains the update and render loops
public void run()
{
running = true;
boolean render = false;
enter code heredouble firstTime = 0;
double lastTime = System.nanoTime() / 1000000000.0;
double passedTime = 0;
double unprocessedTime = 0;
double frameTime = 0;
int frames = 0;
int fps = 0;
while(running)
{
render = false;
firstTime = System.nanoTime() / 1000000000.0;
passedTime = firstTime - lastTime;
lastTime = firstTime;
unprocessedTime += passedTime;
frameTime += passedTime;
while(unprocessedTime >= UPDATE_CAP)
{
unprocessedTime -= UPDATE_CAP;
render = true;
//TODO: Update Game
if(frameTime >= 1.0)
{
frameTime = 0;
fps = frames;
frames = 0;
System.out.println("FPS: "+fps);
}
}
if(render)
{
//TODO: Render Game
window.update();
frames++;
}
else
{
try
{
thread.sleep(1);
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
}
dispose();
}
public void dispose()
{
}
//Main method that runs on program start
public static void main(String[] args)
{
new GameContainer().start();
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getScale() {
// TODO Auto-generated method stub
return 0;
}
}
second class:
package g_core_engine;
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
public class Window
{
private JFrame frame;
private BufferedImage image;
private Canvas canvas;
private BufferStrategy bs;
private Graphics g;
public Window (GameContainer gc)
{
image = new BufferedImage(gc.getWidth(), gc.getHeight(), BufferedImage.TYPE_INT_RGB);
canvas = new Canvas();
Dimension s = new Dimension((int)(gc.getWidth() * gc.getScale()), (int)(gc.getHeight() * gc.getScale()));
canvas.setPreferredSize(s);
canvas.setMaximumSize(s);
canvas.setMinimumSize(s);
frame = new JFrame(gc.getTitle());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(canvas, BorderLayout.CENTER);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setVisible(true);
canvas.createBufferStrategy(2);
bs = canvas.getBufferStrategy();
g = bs.getDrawGraphics();
}
public void update()
{
g.drawImage(image, 0, 0, canvas.getWidth(), canvas.getHeight(), null);
bs.show();
}
}

Looks like you imported a Window class:
import java.awt.Window;
So when you're trying to make an instance of your "window" its actually trying to make a java.awt.Window. Be careful with class names. Change the name of your Window class (and then change that Window.java file of yours to the new name you pick / as well as anywhere else you were trying to use your Window) and it should work fine.

Related

Animate image list by group Java Swing

Currently my code animates an image from the list from initial size to final size and continues with the next one until it completes animating all images and all have their final size.
package ui;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.Timer;
import utils.SudokuGen;
// TODO: Auto-generated Javadoc
/**
* The Class ButtonPanel.
*/
public class ButtonPanel extends JPanel implements ActionListener{
/** The Constant serialVersionUID. */
private static final long serialVersionUID = 1L;
/** The buttons. */
private JButton[] buttons;
private int[] imgsize;
private int index,count;
private Timer timer;
private ImageButton images;
private static final int COUNT = 10;
private static final int INITSIZE = 32;
private static final int COLUMNS = 1;
private static final int ROWS = 9;
/**
* Instantiates a new button panel.
*
* #param puzzle The Sudoku matrix object
* #param images The images object
* #param sPanel The Sudoku JPanel
*/
public ButtonPanel(SudokuGen puzzle, ImageButton images, SudokuPanel sPanel){
//Create an array of JButton with 9 elements
this.buttons = new JButton[puzzle.getMyboard().getNewnumbers().size()];
this.images = images;
this.imgsize = new int[COUNT];
for (int i=0;i<COUNT;i++){
imgsize[i] = INITSIZE;
}
//Sets the layout to a 9*1 GridLayout with padding 5
this.setLayout(new GridLayout(ROWS,COLUMNS,5,5));
//Load an image, create the MouseListener and add it at index i
for (int i=1;i<this.images.getImagelist().size();i++){
ImageIcon image = new ImageIcon(this.images.getImagelist()
.get(i).getImage().getScaledInstance(32, 32, Image.SCALE_SMOOTH));
buttons[i] = createMouseListener(image, i, puzzle, sPanel);
buttons[i].setPreferredSize(new Dimension(100, 100));
this.add(buttons[i]);
}
index = 1; //first button
count = 0; //times to resize the button
timer = new Timer(5,this);
timer.start();
}
public void set(int X){
this.imgsize[index] = X;
}
/**
* Creates the mouse listener.
*
* #param image The image at index i
* #param i The index for the button i
* #param puzzle The Sudoku matrix object
* #param sPanel The Sudoku JPanel
* #return the JButton to add at index i
*/
private JButton createMouseListener(ImageIcon image, int i, SudokuGen puzzle, SudokuPanel sPanel){
JButton button = new JButton();
button.setIcon(image);
button.setActionCommand(Integer.toString(puzzle.getMyboard().getNewnumbers().get(i)));
button.addActionListener(sPanel.new ButtonActionListener());
return button;
}
#Override
public void actionPerformed(ActionEvent e) {
count++;
set(this.imgsize[index]+1); //increase the size of the image
if (this.imgsize[index] < 64){
ImageIcon image = new ImageIcon(this.images.getImagelist()
.get(index).getImage().getScaledInstance(this.imgsize[index], this.imgsize[index], Image.SCALE_SMOOTH));
buttons[index].setIcon(image);
if (count > 24){ //reached final size
count = 0; //start counting again
index++; //move to the next image
}
};
if (index == 10) timer.stop();
repaint();
}
}
I'm not able to provide an SSCCE given the amount of resources needed for it to work so apologizes for that.
My goal is to start animating the next image when the first animation finished half its process. So when the second image reaches its half animation the first one completes its full animation. Kind of a wave effect.
If there something else I could improve, I'm glad to hear suggestions.
Update: Updated method used to achieve the effect:
public void actionPerformed(ActionEvent e) {
count++;
this.imgsize[index]++;
ImageIcon image = new ImageIcon(this.images.getImagelist()
.get(index).getImage().getScaledInstance(this.imgsize[index], this.imgsize[index], Image.SCALE_SMOOTH));
buttons[index].setIcon(image);
if (count > 24){
count = 0;
index++;
}
if (count > 12 && index < 9)
{
this.imgsize[index+1]++;
image = new ImageIcon(this.images.getImagelist()
.get(index+1).getImage().getScaledInstance(this.imgsize[index+1], this.imgsize[index+1], Image.SCALE_SMOOTH));
buttons[index+1].setIcon(image);
}
if (index == 10) timer.stop();
repaint();
}
This is a somewhat overly simplified idea...
Basically, it sets up a normalised timeline (0-1) where events can occur along it, starting and ending at specified points. During these "periods" some "action" can occur.
In your case, these actions are image scaling actions, the scale of which is a proportion of the amount of time that a given event has been running, which is a proportion of the overall time line.
This all sounds wonderfully confusing, but the benefit of which is, you can change the duration of the timeline and the rest of the code will scale automatically
For example, you could take...
timeline = new Timeline(events, 10000);
which generates a timeline to run over 10 seconds and change it to
timeline = new Timeline(events, 5000);
which will generate a timeline to run over 5 seconds and you've not had to change a single line of code to make it work.
I've also spend some time working out how to automatically generate timeline events from a series of images, so you can supply n number of images, and it will generate the required events so that they overlap each other accordingly
Now, you're probably thinking that this is all overly complicated, but the point is, it's all variable, the duration over which the images are scaled, the number of images, it's all variable and you don't need to do much to add/remove images or change the duration
Cavert- I've used getScaledInstance for this example, it's neither fast nor does it generate a quality image
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import javax.imageio.ImageIO;
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 {
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);
} catch (IOException ex) {
ex.printStackTrace();
}
}
});
}
public class TestPane extends JPanel {
private Timeline timeline;
private Map<Object, Image> mapImages = new HashMap<>(5);
private List<Object> imageOrder = new ArrayList<>(5);
public TestPane() throws IOException {
BufferedImage[] images = new BufferedImage[]{
ImageIO.read(new File("...")),
ImageIO.read(new File("...")),
ImageIO.read(new File("...")),
ImageIO.read(new File("..."))
};
List<TimelineEvent> events = generateEvents(images, new ImageScaledObserver() {
#Override
public void imageScaled(Object id, Image image) {
if (!imageOrder.contains(id)) {
imageOrder.add(id);
}
mapImages.put(id, image);
repaint();
}
});
timeline = new Timeline(events, 10000);
Timer timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (timeline.update()) {
((Timer) e.getSource()).stop();
}
}
});
timer.setInitialDelay(5000);
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 500);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (Object id : imageOrder) {
Image image = mapImages.get(id);
int x = (getWidth() - image.getWidth(this)) / 2;
int y = (getHeight() - image.getHeight(this)) / 2;
g2d.drawImage(image, x, y, this);
}
g2d.dispose();
}
}
protected List<TimelineEvent> generateEvents(BufferedImage[] images, ImageScaledObserver observer) {
double length = 1.0 / (double) (images.length);
double overlap = length * 0.5;
List<TimelineEvent> events = new ArrayList<>(images.length);
double startAt = 0.0;
for (BufferedImage image : images) {
double endAt = Math.min(startAt + length + (overlap / 2.0), 1.0);
events.add(new ScaleImageTimelineEvent(image, observer, startAt, endAt));
startAt = Math.min(endAt - (overlap / 2.0), 1.0);
}
return events;
}
public interface TimelineEvent {
public double[] range();
public boolean isWithin(double timelineProgression);
public void performAction(double timelineProgression);
}
public abstract class AbstractTimelineEvent implements TimelineEvent {
private double from, to;
public AbstractTimelineEvent(double from, double to) {
this.from = from;
this.to = to;
}
#Override
public double[] range() {
return new double[]{from, to};
}
#Override
public boolean isWithin(double timelineProgression) {
boolean within = timelineProgression >= from && timelineProgression <= to;
return within;
}
protected double localisedProgression(double timelineProgression) {
double max = from - to;
double value = timelineProgression - to;
double weight = value / max;
return 1d - weight;
}
}
public interface ImageScaledObserver {
public void imageScaled(Object id, Image img);
}
public class ScaleImageTimelineEvent extends AbstractTimelineEvent {
private BufferedImage original;
private ImageScaledObserver observer;
private UUID id;
public ScaleImageTimelineEvent(BufferedImage image, ImageScaledObserver observer, double from, double to) {
super(from, to);
this.original = image;
this.observer = observer;
this.id = UUID.randomUUID();
}
#Override
public void performAction(double timelineProgression) {
double progress = localisedProgression(timelineProgression);
Image image = null;
if (progress < 1.0) {
int width = (int) (original.getWidth() * progress);
if (width > 0) {
image = original.getScaledInstance((int) (original.getWidth() * progress), -1, Image.SCALE_FAST);
}
} else {
image = original;
}
if (image != null) {
observer.imageScaled(id, image);
}
}
}
public static class Timeline {
private List<TimelineEvent> events;
private Long startTime;
private long duration;
public Timeline(List<TimelineEvent> events, long duration) {
this.events = events;
this.duration = duration;
}
public List<TimelineEvent> getEvents() {
return events;
}
public Long getStartTime() {
return startTime;
}
public long getDuration() {
return duration;
}
public void start() {
if (startTime == null) {
startTime = System.nanoTime();
}
}
public void stop() {
startTime = null;
}
public boolean update() {
if (startTime == null) {
start();
}
boolean completed = false;
long currentTime = System.nanoTime();
long diff = currentTime - getStartTime();
long nanoDuration = TimeUnit.NANOSECONDS.convert(getDuration(), TimeUnit.MILLISECONDS);
double progress = diff / (double) nanoDuration;
if (progress > 1.0d) {
progress = 1.0d;
completed = true;
stop();
}
for (TimelineEvent evt : getEvents()) {
if (evt.isWithin(progress)) {
evt.performAction(progress);
}
}
return completed;
}
}
}
I'd also suggest having a look at some of the animation frameworks which are available, which provide additional functionality, like easement. Maybe do a search for "Timing Framework" as a suggestion
The example uses a UUID to generate a unique identifier for the imageScaled event. I wouldn't be hard to make ScaleImageTimelineEvent take an identifier of your own which is linked back to a specific image, allowing you to determine which image was scaled and perform more accurate updates

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:

Animate a Java Line drawing using a timer

I am trying to draw two circle on a panel with a line joining them, all after a button is pressed. So far (apart from tweaking locations of the line) this is ok. However, I would like to animate it using a timer. The first circle should appear, then gradually the line will be revealed, and finally the second circle.
I have looked at many examples of timers, but I can't seem to get it to work for me. I must be misunderstanding something.
here is the ball class (for each circle):
package twoBalls;
import java.awt.Color;
import java.awt.Point;
public class Ball {
private int x;
private int y;
private int r;
private Color color;
private Point location;
private Ball parent;
public Ball(int x, int y, int r) {
this.x = x;
this.y = y;
this.r = r;
Point p = new Point(x, y);
setLocation(p);
}
public void setParent(Ball b) {
parent = b;
}
public Ball getParent() {
return parent;
}
public void setx(int x) {
this.x = x;
}
public void sety(int y) {
this.y = y;
}
public int getx() {
return x;
}
public int gety() {
return y;
}
public int getr() {
return r;
}
public void setPreferedSize() {
}
public void setLocation(Point p) {
setx(p.x);
sety(p.y);
location = p;
}
public Point getLocation() {
return location;
}
public void setColor(Color color) {
this.color = color;
}
public Color getColor() {
return color;
}
}
then the class that will store balls in an arrayList. And I think that this is where the actual drawing should take place, along with the timer.
I am trying to set the start and end point of the line to be the same, and increment the end point until it is where it should be, using the timer. I'm probably way of track, but that was the intention!
I have change this class, the if statements in the while loop can now be entered, as I am now comparing different point. But the line doesn't get drawn at all still.
package twoBalls;
import java.awt.Color;
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.util.ArrayList;
import javax.swing.JPanel;
import javax.swing.Timer;
public class BallsArray extends JPanel implements ActionListener {
private ArrayList<Ball> balls;
private Timer timer;
private final int DELAY = 25;
private int xDest;
private int yDest;
private Point dest;
private Point starts;
private int xStart;
private int yStart;
public BallsArray() {
balls = new ArrayList<Ball>();
timer = new Timer(DELAY, this);
yDest = 0;
xDest = 0;
dest = new Point(xDest, yDest);
starts = new Point(xStart, yStart);
}
public void setDestXY(int x, int y) {
xDest = x;
yDest = y;
dest = new Point(xDest, yDest);
setDest(dest);
}
public void setDest(Point p) {
dest = p;
}
public Point getDest() {
return dest;
}
public void setStartsXY(int x, int y) {
xStart = x;
yStart = y;
starts = new Point(xStart, yStart);
setStarts(starts);
}
public void setStarts(Point p) {
starts = p;
}
public Point getStarts() {
return starts;
}
public void addBall(Ball b) {
balls.add(b);
}
public void addBall(int x, int y, int r) {
balls.add(new Ball(x, y, r));
}
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
for (int i = 0; i < balls.size(); i++) {
if (i == 0) {
paintBall(balls.get(0), g2);
}
if (i != 0) {
int j = i - 1;
Ball bp = balls.get(j);
Ball bc = balls.get(i);
bc.setParent(bp);
paintLine(bc, g2);
paintBall(bc, g2);
}
}
}
public void paintBall(Ball b, Graphics2D g2d) {
Ellipse2D circ = new Ellipse2D.Float(b.getx(), b.gety(), b.getr(),
b.getr());
g2d.draw(circ);
}
public void paintLine(Ball b, Graphics2D g2d) {
timer.start();
if (b != null && b.getLocation() != null) {
Ball parent = b.getParent();
if (parent != null) {
g2d.setColor(Color.GRAY);
if (parent.getLocation() != null && b.getLocation() != null) {
setDest(parent.getLocation());
setStarts(parent.getLocation());
g2d.draw(new Line2D.Float(starts, dest));
}
}
}
}
#Override
public void actionPerformed(ActionEvent e) {
// Not sure what I need to do here
// increment second location somehow
// Point s = getStarts();
Point p = getDest();
Point t = this.getLocation();
while (p != t) {
if (p.x != t.x && p.y != t.y) {
System.out.println("hello");
int x = dest.x;
int y = dest.y;
x++;
y++;
setDestXY(x, y);
p = getDest();
repaint();
} else if (p.x == t.x && p.y != t.y) {
System.out.println("part 2");
int y = dest.y;
y++;
setDestXY(dest.x, y);
p = getDest();
repaint();
} else if (p.x != t.x && p.y == t.y) {
System.out.println("part 3");
int x = dest.x;
x++;
setDestXY(x, dest.y);
p = getDest();
repaint();
}
repaint();
}
}
}
I have had a lot of help online getting this far, I worry I am just beyond my depth now!. I am unsure about the EventQueue/run part below. Here is the class to set it all up:
package twoBalls;
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
public class Display implements ActionListener {
private JFrame frame;
private JButton button;
private BallsArray b;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
Display ex = new Display();
}
});
}
public Display() {
b = new BallsArray();
frame = new JFrame();
frame.setSize(800, 500);
frame.setTitle("Show balls");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
button = new JButton("New Ball");
frame.add(button, BorderLayout.SOUTH);
frame.setVisible(true);
button.addActionListener(this);
}
#Override
public void actionPerformed(ActionEvent e) {
Ball ball1 = new Ball(100, 100, 50);
b.addBall(ball1);
b.addBall(200, 200, 50);
frame.add(b, BorderLayout.CENTER);
frame.revalidate();
frame.repaint();
}
}
At the moment it draws the two circles, but not the line at all.
When you make an animation, it helps to use the model / view / controller pattern.
Here's the GUI I created from your code.
I simplified your Ball class. This is all you need to define a ball.
package twoBalls;
import java.awt.Color;
import java.awt.Point;
public class Ball {
private final int radius;
private final Color color;
private final Point center;
public Ball(int x, int y, int radius, Color color) {
this(new Point(x, y), radius, color);
}
public Ball(Point center, int radius, Color color) {
this.center = center;
this.radius = radius;
this.color = color;
}
public int getRadius() {
return radius;
}
public Color getColor() {
return color;
}
public Point getCenter() {
return center;
}
}
I created the GUIModel class to hold all of the information your GUI needs. This separates the model from the view.
package twoBalls;
import java.awt.Point;
import java.util.ArrayList;
import java.util.List;
public class GUIModel {
private double direction;
private double distance;
private List<Ball> balls;
private Point lineStartPoint;
private Point lineEndPoint;
public GUIModel() {
this.balls = new ArrayList<>();
}
public void addBall(Ball ball) {
this.balls.add(ball);
}
public List<Ball> getBalls() {
return balls;
}
public void calculatePoints() {
this.lineStartPoint = balls.get(0).getCenter();
this.lineEndPoint = balls.get(1).getCenter();
this.distance = Point.distance(lineStartPoint.x, lineStartPoint.y,
lineEndPoint.x, lineEndPoint.y);
this.direction = Math.atan2(lineEndPoint.y - lineStartPoint.y,
lineEndPoint.x - lineStartPoint.x);
}
public Point getCurrentPoint(int pos, int total) {
double increment = distance / total;
double length = increment * pos;
double x = lineStartPoint.x + Math.cos(direction) * length;
double y = lineStartPoint.y - Math.sin(direction) * length;
x = Math.round(x);
y = Math.round(y);
return new Point((int) x, (int) y);
}
public Point getLineStartPoint() {
return lineStartPoint;
}
}
This class holds the two Ball instances, and calculates the length and direction of the line, divided into total increments.
Now that we've defined the model classes, let's look at the view classes. The first is your Display class.
package twoBalls;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Display implements Runnable {
private GUIModel guiModel;
private JFrame frame;
public static void main(String[] args) {
EventQueue.invokeLater(new Display());
}
public Display() {
this.guiModel = new GUIModel();
Ball ball1 = new Ball(150, 200, 50, Color.BLUE);
Ball ball2 = new Ball(450, 200, 50, Color.GREEN);
guiModel.addBall(ball1);
guiModel.addBall(ball2);
guiModel.calculatePoints();
}
#Override
public void run() {
frame = new JFrame();
frame.setTitle("Show Balls Animation");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
DrawingPanel drawingPanel = new DrawingPanel(guiModel);
panel.add(drawingPanel, BorderLayout.CENTER);
panel.add(createButtonPanel(drawingPanel), BorderLayout.SOUTH);
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
private JPanel createButtonPanel(DrawingPanel drawingPanel) {
JPanel panel = new JPanel();
JButton startButton = new JButton("Start Animation");
startButton.addActionListener(new StartAnimation(drawingPanel));
panel.add(startButton);
return panel;
}
public class StartAnimation implements ActionListener {
private DrawingPanel drawingPanel;
public StartAnimation(DrawingPanel drawingPanel) {
this.drawingPanel = drawingPanel;
}
#Override
public void actionPerformed(ActionEvent event) {
LineRunnable runnable = new LineRunnable(drawingPanel);
new Thread(runnable).start();
}
}
}
The constructor of the Display class sets up the GUI model.
The run method of the Display class constructs the GUI, and starts the animation.
See how I've separated the model and view.
The StartAnimation class is your controller. It starts the animation when you left click on the JButton. I'll discuss the LineRunnable class later.
Next, let's take a look at the DrawingPanel class.
package twoBalls;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import javax.swing.JPanel;
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = -3709678584255542338L;
private boolean drawLine;
private int pos;
private int total;
private GUIModel guiModel;
public DrawingPanel(GUIModel guiModel) {
this.guiModel = guiModel;
this.drawLine = false;
this.setPreferredSize(new Dimension(600, 400));
}
public boolean isDrawLine() {
return drawLine;
}
public void setDrawLine(boolean drawLine) {
this.drawLine = drawLine;
}
public void setPos(int pos) {
this.pos = pos;
repaint();
}
public void setTotal(int total) {
this.total = total;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
for (Ball ball : guiModel.getBalls()) {
g2d.setColor(ball.getColor());
Point center = ball.getCenter();
int radius = ball.getRadius();
g2d.fillOval(center.x - radius, center.y - radius, radius + radius,
radius + radius);
}
if (isDrawLine()) {
g2d.setColor(Color.BLACK);
g2d.setStroke(new BasicStroke(5.0F));
Point a = guiModel.getLineStartPoint();
Point b = guiModel.getCurrentPoint(pos, total);
g2d.drawLine(a.x, a.y, b.x, b.y);
}
}
}
The only thing this view class does is draw the balls and the line. The responsibility for calculating the length of the line belongs in the model.
I set the preferred size here, and use the pack method in the Display class to get the size of the JFrame. You usually want to know the dimensions of the drawing area, rather than the entire window.
Finally, let's look at the LineRunnable class. This is the class that controls the animation.
package twoBalls;
import java.awt.EventQueue;
public class LineRunnable implements Runnable {
private int total;
private DrawingPanel drawingPanel;
public LineRunnable(DrawingPanel drawingPanel) {
this.drawingPanel = drawingPanel;
this.total = 240;
}
#Override
public void run() {
setDrawLine();
for (int pos = 0; pos <= total; pos++) {
setPos(pos);
sleep(50L);
}
}
private void setDrawLine() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
drawingPanel.setDrawLine(true);
drawingPanel.setTotal(total);
}
});
}
private void setPos(final int pos) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
drawingPanel.setPos(pos);
}
});
}
private void sleep(long delay) {
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
}
}
}
In the run method, we divide the line into 240 segments, and draw a segment every 50 milliseconds. It takes the GUI 12 seconds to draw the line. You can play with these numbers if you wish.
The for loop is a classic animation loop. First you update the model, which I'm doing through the drawing panel. Then you sleep.
This animation loop is running on a different thread from the GUI thread. This keeps the GUI responsive. Since the loop is running on a different thread, we have to use the invokeLater method to draw on the Event Dispatch thread.
I hope this was helpful to you. Divide and conquer. Don't let a class do more than one thing.

My first tries on a java game end me up with 7 fps when I read images?

Lately I've been trying to build a little game (2d, nothing big) off the knowledge I acquired in my CS class. After reading documentations of those Graphic-related classes for like 2 weeks, I ended up with this situation:
I have a running system that operates at 60 game logic updates/60 frames per second (that one works quite nicely :D). As a first little test, I wanted to make a image move on the screen. Thats the code (partly mine, partly from some tutorials):
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.io.File;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class Game extends Canvas implements Runnable{
private static final long serialVersionUID = 1L;
public static final String NAME= "PokeCraft PRE-ALPHA";
public static final int HEIGHT=720;
public static final int WIDTH=HEIGHT*16/9;
public static final int SCALE=1;
private int fps=0;
private int tps=0;
private boolean running;
private int tickCount;
public void start(){
running = true;
new Thread(this).start();
}
public void stop(){
running = false;
}
public void render(){
BufferStrategy bufferStrategy =getBufferStrategy();
if(bufferStrategy==null){
this.createBufferStrategy(3);
return;
}
/* render function */
Graphics g = (Graphics) bufferStrategy.getDrawGraphics();
g.clearRect(0, 0, super.getWidth(), super.getHeight());
Image img = null;
try{
String imgPath = "data/MF.png";
img = ImageIO.read(getClass().getResourceAsStream(imgPath));
} catch(Exception e){
System.out.println(e);
}
g.drawImage(img, tickCount, 0, null);
Font font = new Font("Verdana",0,11);
g.setFont(font);
g.setColor(Color.RED);
g.drawString(NAME+" / "+fps+" fps, "+tps+"tps", 5, 15);
g.dispose();
bufferStrategy.show();
}
public void run() {
long lastTime= System.nanoTime();
double unprocessed = 0;
double nsPerTick = 1000000000.0/60.0;
int frames = 0;
int ticks = 0;
long lastTimer1 = System.currentTimeMillis();
while(running){
long now = System.nanoTime();
unprocessed += (now-lastTime)/nsPerTick;
lastTime= now;
boolean shouldRender= false;
while(unprocessed >= 1){
ticks++;
tick();
unprocessed -= 1;
shouldRender = true;
}
if(shouldRender){
frames++;
render();
}
if(System.currentTimeMillis()-lastTimer1 > 1000){
lastTimer1 += 1000;
System.out.println(ticks+" ticks, "+frames + " fps");
fps=frames;
tps=ticks;
ticks = 0;
frames = 0;
}
}
}
public void tick(){
tickCount++;
}
public static void main(String[] args){
Game game= new Game();
game.setPreferredSize(new Dimension(WIDTH*SCALE, HEIGHT*SCALE));
game.setMinimumSize(new Dimension(WIDTH*SCALE, HEIGHT*SCALE));
game.setMaximumSize(new Dimension(WIDTH*SCALE, HEIGHT*SCALE));
JFrame frame = new JFrame(Game.NAME);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(game);
frame.pack();
frame.setResizable(true);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
game.start();
}
}
The ImageIO.read(...) is really hitting the performance really hard (according to VisualVM, it takes ~200ms/run). How can I tackle that problem?
Reading an image is an inherently costly operation.
Therefore, you should read the image once, when you start the game, and hold it in memory to access later.
Avoid loading the image each time it is rendered. Make it a class variable and load it only once. Like this:
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.io.File;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class Game extends Canvas implements Runnable {
private static final long serialVersionUID = 1L;
public static final String NAME = "PokeCraft PRE-ALPHA";
public static final int HEIGHT = 720;
public static final int WIDTH = HEIGHT * 16 / 9;
public static final int SCALE = 1;
private int fps = 0;
private int tps = 0;
private Image img = null;
private boolean running;
private int tickCount;
public void start() {
running = true;
new Thread(this).start();
}
public void stop() {
running = false;
}
public void render() {
BufferStrategy bufferStrategy = getBufferStrategy();
if (bufferStrategy == null) {
this.createBufferStrategy(3);
return;
}
/* render function */
Graphics g = (Graphics) bufferStrategy.getDrawGraphics();
g.clearRect(0, 0, super.getWidth(), super.getHeight());
if (img == null) {
try {
String imgPath = "data/MF.png";
img = ImageIO.read(getClass().getResourceAsStream(imgPath));
} catch (Exception e) {
System.out.println(e);
}
}
g.drawImage(img, tickCount, 0, null);
Font font = new Font("Verdana", 0, 11);
g.setFont(font);
g.setColor(Color.RED);
g.drawString(NAME + " / " + fps + " fps, " + tps + "tps", 5, 15);
g.dispose();
bufferStrategy.show();
}
public void run() {
long lastTime = System.nanoTime();
double unprocessed = 0;
double nsPerTick = 1000000000.0 / 60.0;
int frames = 0;
int ticks = 0;
long lastTimer1 = System.currentTimeMillis();
while (running) {
long now = System.nanoTime();
unprocessed += (now - lastTime) / nsPerTick;
lastTime = now;
boolean shouldRender = false;
while (unprocessed >= 1) {
ticks++;
tick();
unprocessed -= 1;
shouldRender = true;
}
if (shouldRender) {
frames++;
render();
}
if (System.currentTimeMillis() - lastTimer1 > 1000) {
lastTimer1 += 1000;
System.out.println(ticks + " ticks, " + frames + " fps");
fps = frames;
tps = ticks;
ticks = 0;
frames = 0;
}
}
}
public void tick() {
tickCount++;
}
public static void main(String[] args) {
Game game = new Game();
game.setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
game.setMinimumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
game.setMaximumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
JFrame frame = new JFrame(Game.NAME);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(game);
frame.pack();
frame.setResizable(true);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
game.start();
}
}
Same goes for the font, although that may not be impacting performance as much as the image loading.
Disk operations are incredibly slow, and there's no need to access the file each loop, which is what you're currently doing. Make your img variable a class variable and instantiate it before entering the while(running) loop in your run() method.

JPanel Layout Image Cutoff

I am adding images to a JPanel but the images are getting cut off. I was originally trying BorderLayout but that only worked for one image and adding others added image cut-off. So I switched to other layouts and the best and closest I could get was BoxLayout however that adds a very large cut-off which is not acceptable either.
So basically; How can I add images (from a custom JComponent) to a custom JPanel without bad effects such as the one present in the code.
Custom JPanel:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.BoxLayout;
import javax.swing.JPanel;
import javax.swing.Timer;
public class GraphicsPanel extends JPanel implements MouseListener {
private Entity test;
private Timer timer;
private long startTime = 0;
private int numFrames = 0;
private float fps = 0.0f;
GraphicsPanel() {
test = new Entity("test.png");
Thread t1 = new Thread(test);
t1.start();
Entity ent2 = new Entity("images.jpg");
ent2.setX(150);
ent2.setY(150);
Thread t2 = new Thread(ent2);
t2.start();
Entity ent3 = new Entity("test.png");
ent3.setX(0);
ent3.setY(150);
Thread t3 = new Thread(ent3);
t3.start();
//ESSENTIAL COMMENT ANY OF THESE and you will see the problem immediately
//You can use ANY image to reproduce the problem
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
add(test);
add(ent2);
add(ent3);
//GAMELOOP
timer = new Timer(30, new Gameloop(this));
timer.start();
addMouseListener(this);
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g.create();
g2.setClip(0, 0, getWidth(), getHeight());
g2.setColor(Color.BLACK);
g2.drawString("FPS: " + fps, 1, 15);
}
public void getFPS()
{
++numFrames;
if (startTime == 0) {
startTime = System.currentTimeMillis();
} else {
long currentTime = System.currentTimeMillis();
long delta = (currentTime - startTime);
if (delta > 1000) {
fps = (numFrames * 1000) / delta;
numFrames = 0;
startTime = currentTime;
}
}
}
public void mouseClicked(MouseEvent e) {}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
class Gameloop implements ActionListener
{
private GraphicsPanel gp;
Gameloop(GraphicsPanel gp) {
this.gp = gp;
}
public void actionPerformed(ActionEvent e) {
try {
gp.getFPS();
gp.repaint();
} catch (Exception ez) { }
}
}
}
Main class:
import java.awt.EventQueue;
import javax.swing.JFrame;
public class MainWindow
{
public static void main(String[] args)
{
new MainWindow();
}
private JFrame frame;
private GraphicsPanel gp = new GraphicsPanel();
MainWindow()
{
EventQueue.invokeLater(new Runnable() {
public void run() {
frame = new JFrame("Graphics Practice");
frame.setSize(680, 420);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(gp);
}
});
}
}
Custom JComponent
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import javax.swing.JComponent;
public class Entity extends JComponent implements Runnable {
private BufferedImage bImg;
private int x = 0;
private int y = 0;
private int entityWidth, entityHeight;
private String filename;
Entity(String filename) {
this.filename = filename;
}
public void run() {
bImg = loadBImage(filename);
entityWidth = bImg.getWidth();
entityHeight = bImg.getHeight();
setPreferredSize(new Dimension(entityWidth, entityHeight));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(bImg, x, y, null);
g2d.dispose();
}
public BufferedImage loadBImage(String filename) {
try {
bImg = ImageIO.read(getClass().getResource(filename));
} catch (Exception e) { }
return bImg;
}
public int getEntityWidth() { return entityWidth; }
public int getEntityHeight() { return entityHeight; }
public int getX() { return x; }
public int getY() { return y; }
public void setX(int x) { this.x = x; }
public void setY(int y) { this.y = y; }
}
One thing I notice is that your preferred size is calculated incorrectly. You set the preferred size to be the size of the image. The problem is you paint the image at (x, y). So the preferred size needs to take that into account.
Otherwise I don't understand the question and running the code doesn't help since I don't know the size of your images whether they should be large, small, same size etc..
#camickr is likely right about why your existing approach isn't getting the results you want.
As an alternative, you might consider using JInternalFrame within a JDesktopPane. In this way, your images would be documents that could be individually moved, resized and scrolled. The article How to Use Internal Frames gives an idea of how such an implementation might look. This example shows a simple approach to staggering the frames and selecting them from a menu.

Categories

Resources