I am making an animation by drawing different images dozens of milliseconds after the former one. something like this:
drawimage(image1,0,0,null);
try {
Thread.sleep(100);
}catch(Exception e){//To do something
}
drawimage(image2,0,0,null);
But the first image doesn't show until the second one appear. That means they appear at the same time.
My question is why does this happen?
Me: Where exactly is this code? Is it in a paint/paintComponent method?
OP: it is in paintComponent. I use it to make animation, but I am not sure it is a good way.
You're right it isn't a good way. Don't ever call Thread.sleep in the paintComponent method. I would avoid the Thread.sleep all together and use a javax.swing.Timer. See more How to Use Swing Timers
See examples here and here and here and here.
You could...
Use a list of Images and every iteration firing of the Timer event, add another Image to the List<Image> and call repaint()
You could...
Have an MyImage object class that has an Image field and a boolean draw field. In the Timer, loop through the MyImage objects and do something like
for (MyImage image: images) {
if (!image.isDraw()) {
image.setDraw(true);
break;
}
}
repaint();
For the MyImage List just loop through them in the paintComponent method and call it drawImage method, that you create.
Run this exmaple, showing the first option
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class AnimateImages extends JPanel {
private static final int IMAGE_ROWS = 10;
private static final int IMAGE_COLS = 10;
private static final int IMAGE_SIZE = 50;
private static final int DIM_WIDTH = IMAGE_COLS * IMAGE_SIZE;
private final List<MyImage> images;
private Image image;
private int currX = -IMAGE_SIZE;
private int currY;
public AnimateImages() {
try {
image = ImageIO.read(new URL("http://swoo.co.uk/content/images/icons/stackoverflow.png"));
} catch (IOException ex) {
Logger.getLogger(AnimateImages.class.getName()).log(Level.SEVERE, null, ex);
}
images = createImages();
Timer timer = new Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (MyImage image : images) {
if (!image.isDraw()) {
image.setDraw(true);
break;
}
repaint();
}
}
});
timer.start();
}
private List<MyImage> createImages() {
List<MyImage> list = new ArrayList<>();
for (int i = 0; i < IMAGE_ROWS * IMAGE_COLS; i++) {
if (currX >= DIM_WIDTH) {
currX = 0;
currY += IMAGE_SIZE;
} else {
currX += IMAGE_SIZE;
}
list.add(new MyImage(image, currX, currY));
}
return list;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (MyImage img : images) {
img.draw(g);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(IMAGE_SIZE * IMAGE_COLS, IMAGE_SIZE * IMAGE_ROWS);
}
public class MyImage {
Image image;
int x, y;
boolean draw = false;
public MyImage(Image image, int x, int y) {
this.image = image;
this.x = x;
this.y = y;
}
public void setDraw(boolean draw) {
this.draw = draw;
}
public boolean isDraw() {
return draw;
}
public void draw(Graphics g) {
if (draw) {
g.drawImage(image, x, y, IMAGE_SIZE, IMAGE_SIZE, AnimateImages.this);
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.add(new AnimateImages());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
The program, in its current state, draws two images at the same time because there are two draw methods in the loop (if you are indeed using a loop).
In order to solve this issue, you should use only a single draw method and leave the delay in the same place. However, in order to cycle through all of the different image variables (if you have them named as numbers 1,2,3,4...etc in an array) you can use a for loop to draw them:
for (int i = 0; i< *however many images you have*; i++){
drawimage(image[i],0,0,null);
try {
Thread.sleep(100);
}catch(Exception e){//To do something
}
}
Edit
You do not use delays inside the paintComponent. Since you are, that is what is likely causing the issues. Move the delay into the main method of your program.
Your answer
You're getting this result because it is printing image1, waiting .1 seconds and then printing image2. Then it doesn't wait at all, the game is immediately updated and then prints image1. Your display time for image2 is microscopic and you're likely only seeing image1 as a result.
Furthermore, the images are never deleted or replaced and so you are constantly drawing images on top of each other which is causing a memory leak, and is likely the cause of you seeing both images.
Considerations
This is a handy little animation class that I got, and still use, from this tutorial. This class is pretty handy and you can add the animated sprites necessary for each animation.
With this class, when you do a drawImage() you can use object.getImage() to extract the current frame of the animation.
Just make sure that you're calling the animation's update() method in your game's main loop so that the animations are constantly being updated.
import java.awt.Image;
import java.util.ArrayList;
public class Animation {
private ArrayList frames;
private int currentFrame;
private long animTime;
private long totalDuration;
public Animation() {
frames = new ArrayList();
totalDuration = 0;
synchronized (this) {
animTime = 0;
currentFrame = 0;
}
}
public synchronized void addFrame(Image image, long duration) {
totalDuration += duration;
frames.add(new AnimFrame(image, totalDuration));
}
public synchronized void update(long elapsedTime) {
if (frames.size() > 1) {
animTime += elapsedTime;
if (animTime >= totalDuration) {
animTime = animTime % totalDuration;
currentFrame = 0;
}
while (animTime > getFrame(currentFrame).endTime) {
currentFrame++;
}
}
}
public synchronized Image getImage() {
if (frames.size() == 0) {
return null;
} else {
return getFrame(currentFrame).image;
}
}
private AnimFrame getFrame(int i) {
return (AnimFrame) frames.get(i);
}
private class AnimFrame {
Image image;
long endTime;
public AnimFrame(Image image, long endTime) {
this.image = image;
this.endTime = endTime;
}
}
}
Related
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.
I am currently working on Sorting Visualizer using Swing Components. First, here is the code to one of my chosen sorting algorithms:
package sortingAlgorithms;
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.geom.Rectangle2D;
import javax.swing.Timer;
import framework.sortingPanel.SortingPanel;
public class InsertionSort{
private Timer sortingTimer;
private int outerCtr, innerCtr;
private double[] bars;
private int iteration;
private double barWidth;
private int candidateBar, traversingBar;
private int delay;
private boolean proceedToOuter;
public InsertionSort(SortingPanel sortingPanel) {
sortingTimer = new Timer(delay, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(proceedToOuter) {
innerCtr = outerCtr;
candidateBar = outerCtr;
}
if(outerCtr < bars.length) {
if(innerCtr > 0 && bars[innerCtr - 1] > bars[candidateBar]) {
sortingPanel.setNumOfIteration(++iteration);
traversingBar = innerCtr - 1;
if(bars[candidateBar] < bars[traversingBar]) {
double tempBar = bars[candidateBar];
bars[candidateBar] = bars[traversingBar];
bars[traversingBar] = tempBar;
int temp = candidateBar;
candidateBar = traversingBar;
traversingBar = temp;
}
sortingPanel.repaint();
innerCtr--;
proceedToOuter = false;
}else {
proceedToOuter = true;
outerCtr++;
}
}else {
sortingTimer.stop();
sortingPanel.sortingIsDone();
System.out.println("SORTED");
//everything else after the algorithm
}
}
});
}
public void initComponents(double[] bars, double barWidth, int delay) {
iteration = 0;
this.bars = bars;
this.barWidth = barWidth;
this.delay = delay;
setDelay(delay);
outerCtr = 1;
innerCtr = outerCtr;
candidateBar = outerCtr;
traversingBar = 0;
proceedToOuter = true;
}
public void draw(Graphics g) {
g.setColor(Color.WHITE);
Graphics2D g2d = (Graphics2D) g;
Rectangle2D r2d;
for(int i = 0; i < bars.length; i++) {
r2d = new Rectangle2D.Double(i*barWidth, 0, barWidth, bars[i]);
g2d.fill(r2d);
}
g.setColor(Color.RED);
r2d = new Rectangle2D.Double(traversingBar*barWidth, 0, barWidth, bars[traversingBar]);
g2d.fill(r2d);
g.setColor(Color.GREEN);
r2d = new Rectangle2D.Double(candidateBar*barWidth, 0, barWidth, bars[candidateBar]);
g2d.fill(r2d);
}
public void setDelay(int delay) { sortingTimer.setDelay(delay); }
public void playSort() { sortingTimer.start(); }
public void stopSort() { sortingTimer.stop(); }
public void continueSort() { sortingTimer.start(); }
}
I used a JPanel to display the double bars[] which are the bars to be sorted. I created an method initComponents() to initialize the variables needed in the algorithm (I did this since I designed the project in such a way that the user could shuffle and re-sort another bars[] using the same algorithm; plus, I don't know why I can't access the values passed during instantiation of InsertionSort class so I've decided to create a separate method to handle the issue).
I draw() method accepts a Graphics from the sortingPanel in which all the bars are being displayed. I have overriden the paintComponent in sortingPanel and pass its Graphics to this draw method.
I just need your opinion whether I'm right in implementing Swing Timer.
ADDED NOTE:
This code works perfectly but I want to hear some opinion especially in the technical side of the code structure.
I also know that Thread.Sleep() is a no go so I just want to see if this is correct or are there WAY BETTER solution than this.
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
im making a side scroller and i dont know how to side scroll and im trying to scroll on the x and y axis
this is for a school project so i would like help as soon as possable
heres some of my code if you have question just ask. any advice is welcome.
source code http://www.mediafire.com/?fi1f9lv6qc2t5d7
gameCanvas.java
package Game;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferStrategy;
#SuppressWarnings("serial")
public abstract class GameCanvas extends Canvas implements Runnable,KeyListener{
public static final long ONE_SECOND_MILI = 1000;
protected int frameW;
protected int frameH;
protected long fps;
private long period = 15;
private BufferStrategy buff;
private Graphics graph;
private Color bckGround=(Color.GRAY);
private Image bckGround_img;
private Thread t;
boolean left;
boolean right;
boolean up;
boolean down;
int lastpressed;
int newlastpressed;
private String drawFps = "0";
public GameCanvas(int w,int h){
this.frameW=w;
this.frameH=h;
this.setIgnoreRepaint(true);
this.setBounds(0,0,frameW,frameH);
this.setBackground(Color.GREEN);
this.setVisible(true);
}
public GameCanvas(int w,int h,Color bck){
this.frameW=w;
this.frameH=h;
this.bckGround=bck;
this.setIgnoreRepaint(true);
this.setBounds(0,0,frameW,frameH);
this.setBackground(bckGround);
this.setVisible(true);
}
public void addNotify(){
super.addNotify();
this.createBufferStrategy(2);
this.buff=this.getBufferStrategy();
requestFocus();
startGame();
}
public void startGame(){
if (t==null){
t=new Thread(this);
t.start();
}
}
public void run(){
while(true){
long beginTime=System.currentTimeMillis();
// try {
// Thread.sleep(25);
// } catch (InterruptedException e1) {
// // TODO Auto-generated catch block
// e1.printStackTrace();
// }
Update();
Render();
Draw();
fps=System.currentTimeMillis() - beginTime ;
long sleepTime=period-fps;
if (sleepTime == 30) sleepTime = -1;
fps= ONE_SECOND_MILI / ((period * 2) - sleepTime);
try{
if (sleepTime > 0){
Thread.sleep(sleepTime);
}
}
catch(Exception e){
}
}
}
public void Render(){
graph = buff.getDrawGraphics();
if (!HasImgBackground()){
graph.setColor(bckGround);
graph.fillRect(0, 0, frameW, frameH);
}else{
graph.drawImage(bckGround_img, 0, 0, frameW, frameH,null);
}
graph.setColor(new Color(255,255,255));
graph.drawString("FPS: " + fps , 10, 15);
Paint(graph);
}
private void Draw(){
if(!buff.contentsLost()){
buff.show();
if(graph != null){
graph.dispose();
}
}
}
private boolean HasImgBackground(){
if (bckGround_img==null){
return false;
}
return true;
}
public void setBackgroundImg(Image image){
this.bckGround_img=image;
}
public void deleteBackground(){
this.bckGround_img=null;
}
public void keyPressed(KeyEvent e) {
if(e.getKeyCode()==KeyEvent.VK_LEFT){
left=true;
if(lastpressed!=4)
newlastpressed=lastpressed;
lastpressed=4;
}
if(e.getKeyCode()==KeyEvent.VK_UP){
up=true;
if(lastpressed!=1)
newlastpressed=lastpressed;
lastpressed=1;
}
if(e.getKeyCode()==KeyEvent.VK_RIGHT){
right=true;
if(lastpressed!=2)
newlastpressed=lastpressed;
lastpressed=2;
}
if(e.getKeyCode()==KeyEvent.VK_DOWN){
down=true;
if(lastpressed!=3)
newlastpressed=lastpressed;
lastpressed=3;
}
}
public void keyReleased(KeyEvent e) {
if(e.getKeyCode()==KeyEvent.VK_LEFT){
left=false;
if(up||right||down)
if(newlastpressed!=0)
lastpressed=newlastpressed;
}
if(e.getKeyCode()==KeyEvent.VK_UP){
up=false;
if(left||right||down)
if(newlastpressed!=0)
lastpressed=newlastpressed;
}
if(e.getKeyCode()==KeyEvent.VK_RIGHT){
right=false;
if(up||left||down)
if(newlastpressed!=0)
lastpressed=newlastpressed;
}
if(e.getKeyCode()==KeyEvent.VK_DOWN){
down=false;
if(up||right||left)
if(newlastpressed!=0)
lastpressed=newlastpressed;
}
}
public void keyTyped(KeyEvent e) {
}
abstract void Update();
abstract void Paint(Graphics g);
}
main.java
package Game;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import javax.swing.JFrame;
public class Mainth extends GameCanvas{
private long timeDown = 0;
// private StopWatch t = new StopWatch();
static Collision coll=new Collision();
Character character=new Character();
static Image wallImage=ImageLoader.getImg().getImage("data/images/objects/brick_wall.png");
static Image bushImage=ImageLoader.getImg().getImage("data/images/objects/hedge_ver.png");
static ArrayList<Image> wallArray=new ArrayList<Image>();
static ArrayList<Image> wall2Array=new ArrayList<Image>();
static Sprite wallSprite;
static Sprite wall2Sprite;
static IndexCounter devIndex;
static Dude dev;
static Wall wall;
static Wall bush;
static Wall wall2;
/**not used*/
int x=0,y=0;
static ArrayList objects=new ArrayList<Entity>();
static ArrayList chars=new ArrayList<Dude>();
static ArrayList projectiles=new ArrayList<>();
public static void main(String[] args){
Mainth Canvas=new Mainth(1500,1000);
JFrame frame=new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.add(Canvas);
frame.pack();
frame.setSize(750, 400);
frame.setVisible(true);
Mainth.chars.add(dev);
Mainth.objects.add(wall);
Mainth.objects.add(bush);
Mainth.objects.add(wall2);
}
public Mainth(int w,int h){
super(w,h);
this.addKeyListener(this);
CharacterCreation();
}
public void CharacterCreation(){
wallArray.add(wallImage);
wall2Array.add(bushImage);
wallSprite=new Sprite(wallArray,1);
wall2Sprite=new Sprite(wall2Array,1);
dev=new Dude(character.LinkStandingDown,character.LinkStandingDown.getID(),100,300);
devIndex=new IndexCounter(character.LinkWalkingDownArray.size(),3);
wall=new Wall(wallSprite,1,100,50){};
bush=new Wall(wall2Sprite,1,185,55){};
wall2=new Wall(wallSprite,1,100,225){};
bush.setHardness(1);
}
Movement movem=new Movement();
void Update() {
movem.movement(dev, character, left, right, up, down, lastpressed, devIndex);
dev.move();
coll.objectCollision(chars,objects);
devIndex.Counter();
}
void Paint(Graphics g) {
bush.Draw(g,0);
wall.Draw(g,0);
wall2.Draw(g,0);
dev.Draw(g,devIndex.getIndex());
Graphics2D g2d= (Graphics2D) g;
}
public void animation(){
String[] animationSprites=dev.getImgs("walk_down");
int aniTime=0;
aniTime++;
}
public ArrayList<Entity> exportObjects(){
return objects;
}
public ArrayList<Dude> getMainChar(){
return chars;
}
}
What exactly isn't working in the game?
Basic side scrolling logic consists of moving the background, the characters, and the obstacles a certain increment as movement keys are held down. If the screen is "moving right," we are actually moving all of these elements to the left. If the screen is "moving left," then we are doing the opposite. Also, if an entity is moving and has a certain target point, make sure and update the coordinates of this point as you scroll, or the entity will keep moving until it reaches its original on-screen destination. You should also implement code that stops the screen from scrolling too far (thus losing the game).
There are many ways to use side-scrolling, such as scrolling when a character moves a certain distance from the center of the screen, or having controls that move the screen independently from a character (such as in an rts when one scrolls around the map). An easier way might be to always have the player be in the center of the screen, and just have the background and other entities scroll back and forth around him.
You need to coordinate systems:
world coordinates - are always constant
(local) frame coordinate - where objects should be shown in your window now
every object has only world coordinates. And you have a window in this big world. This window has its coordinates in world coordinates. To scroll the view in your window you just need to change it's world coordinates.
Of course you need a code, that renders all objects in you window for any correct position of your movable window. It can be like:
void renderFrame(Rectangle frame) {
for(GameObject go : gameObjects) {
if(frame.contains(go.getGlobalCoordinates())) {
Rectangle windowCoordinates = new Rectangle();
windowCoordinates.x = go.getGlobalCoordinates().x - frame.x;
windowCoordinates.x = go.getGlobalCoordinates().y - frame.y;
windowCoordinates.x = go.getGlobalCoordinates().width - frame.width;
windowCoordinates.x = go.getGlobalCoordinates().height - frame.height;
go.paint(g2, windowCoordinates);
}
}
}
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.