Image won't draw onto screen but no errors occur - java

I've started creating a simple java game and at the moment I have the Game Window created with a screen and a basic Player class. The player image however won't draw onto the screen despite the program not giving me any errors so I'm not sure where to start debugging for the problem maybe someone could help me out?
Here are the classes:
import java.awt.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class GameWindow extends JFrame {
public static int windowWidth = 600;
public static int windowHeight = 600;
public static void main(String[] args) {
new GameWindow();
}
public GameWindow() {
this.setSize(windowWidth, windowHeight);
this.setTitle("Berzerk Clone");
this.setVisible(true);
// Defaults the window to be set in the middle of the screen
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
GameDrawing drawGame = new GameDrawing();
this.add(drawGame, BorderLayout.CENTER);
}
}
Drawing Class:
import java.awt.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class GameDrawing extends JComponent {
PlayerHuman p;
public GameDrawing() {
p = new PlayerHuman(300, 300);
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D graphics = (Graphics2D)g;
graphics.setColor(Color.BLACK);
graphics.fillRect(0, 0, GameWindow.windowWidth,GameWindow.windowHeight);
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics.drawImage(p.getPlayerImage(), 300, 300, null);
}
}
Player class:
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
public class PlayerHuman extends GlobalPosition {
BufferedImage basicPlayer;
public PlayerHuman(int x, int y) {
super(x, y);
try {
basicPlayer = ImageIO.read(new File("Images/Player.png"));
} catch (IOException e) {
e.printStackTrace();
}
}
public void draw(Graphics2D g2d) {
g2d.drawImage(getPlayerImage(), x, y, null);
}
public BufferedImage getPlayerImage() {
return basicPlayer;
}
}
All help is appreciated.
EDIT:
My Apologies
GlobalPosition Class to give the player a starting point:
public class GlobalPosition {
public int x;
public int y;
public GlobalPosition() {
x = y = 0;
}
public GlobalPosition(int _x, int _y) {
x = _x;
y = _y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void setX(int newX) {
x = newX;
}
public void setY(int newY) {
y = newY;
}
}
I have a Game Loop class that repaints:
public class GameLoop implements Runnable {
GameWindow gWindow;
public GameLoop(GameWindow newGWindow) {
this.gWindow = newGWindow;
}
#Override
public void run() {
gWindow.repaint();
}
}

Suggestions:
Start small: test a very small program that does nothing but loads an image, puts it into an ImageIcon and displays the ImageIcon in a JOptionPane. Solve that first, and only then work on using it in the larger application.
You're better off reading the image in as a URL via getClass().getResource("....") than as a File.
don't call setVisible(true) on the top-level window until after adding all components.
Override the JComponent's paintComponent(Graphics g) method, not its paint method.
e.g.,
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
public class ShowImage {
// ******* add the path to the image resource below *****
private static final String IMAGE_RESOURCE_PATH = "";
public static void main(String[] args) {
URL imgUrl = ShowImage.class.getResource(IMAGE_RESOURCE_PATH);
BufferedImage img;
try {
img = ImageIO.read(imgUrl);
if (img == null) {
String message = "Image cannot be found at \""
+ IMAGE_RESOURCE_PATH + "\"";
JOptionPane.showMessageDialog(null, message);
System.exit(-1);
}
Icon icon = new ImageIcon(img);
JOptionPane.showMessageDialog(null, icon);
} catch (IOException e) {
e.printStackTrace();
}
}
}

Turns out that it was the
setVisible(true)
call that was causing the problem. I pushed it below all the other calls in the method and the screen and image appeared. Thanks for the help.
EDIT:
#Sage this is the location so the location is correct, however I will begin to use the
getClass().getResource(..)
method from now on.

Related

Creating endless number of objects in JPanel and draw them through PaintComponent in Java

I have one dilemma , how to realize application. I have JPanel with width 288 and height 512, then I created two objects ( images ) and drew them through paintComponent using coordinates
drawImage (Image1,288,128,this) ;
drawImage (Image2, 288, 384, this);
. They are decrementing simultaneously in the X axis and when it reaches x = 144 , new (same) images should be drawn at the coordinates β€˜( x = 288 , y = (int)Math.random()* 512 )’ and begin decrement as well as first ones should still decrements. And this process should be endless. Every new objects reaching x = 144 should build new ones . I tried to create ArrayList with adding coordinates in it
ArrayList arrayX = new ArrayList();
arrayX.add(288)
arrayY.add((int) Math.random()* 512 )
and then extract values through
array.get()
But that was unsuccessfully.
I saw video where man did it using JavaScript through the array
var position = []
position = ({
X : 288
Y : 256
})
And then implemented through the loop like this
function draw() {
for (int i = 0; i < position.length; i++ ){
cvs.drawImage(Image1,position[i].x , position[i].y)
cvs.drawImage(Image2,position[i].x , position[i].y + 50)
position [i] .x - -;
if(position[i].x == 128)
position.push({
X : 288
Y : Math.floor(Math.random()*512 })
})
}
}
I don’t know how to do this in Java.
May be I should use array too to keep variables with coordinates , or arraylist but in different way. Help me please .
Thanks in advance
Conceptually the idea is simple enough, the problem is, Swing is signal thread and NOT thread safe.
See Concurrency in Swing for more details.
This means you can run a long running or blocking operation (like a never ending loop) inside the Event Dispatching Thread, but also, you shouldn't update the UI (or properties the UI depends on) from outside the context of the EDT.
While there are a number of possible solutions to the problem, the simplest is probably to use a Swing Timer, which provides a means to schedule a delay safely (that won't block the EDT) and which will trigger it's updates within the context of the EDT, allowing you to update the UI from within it.
See How to Use Swing Timers for more details.
Now, because you're in a OO language, you should leverage the power it provides, to me, this means encapsulation.
You have a image, you want drawn at a specific location, but whose location change be changed based on some rules, this just screams Plain Old Java Old (POJO)
Normally, I'd start with a interface to describe the basic properties and operations, but for brevity, I've jumped straight for a class...
public class Drawable {
private int x, y;
private Color color;
public Drawable(int x, int y, Color color) {
this.x = x;
this.y = y;
this.color = color;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public Color getColor() {
return color;
}
public void update() {
x--;
if (x <= 144) {
reset();
}
}
protected void reset() {
x = 288;
y = (int) (Math.random() * (512 - 20));
}
public void paint(Graphics2D g2d) {
Graphics2D copy = (Graphics2D) g2d.create();
copy.translate(getX(), getY());
copy.setColor(getColor());
copy.drawOval(0, 0, 20, 20);
copy.dispose();
}
}
But wait, you say, it's using Color instead of image!? Yes, I didn't have any small images at hand, besides, I need to leave you something to do ;)
Now, the animation is a sequence of updating and painting repeatedly until a desired state is reached.
In this case, you don't care about the end state so much, so you can just keep it running.
The "update" cycle is handled by a Swing Timer, which loops over a List of Drawable objects, calls their update methods and then schedules a repaint, which triggers the JPanels paintComponent where by the Drawable objects are painted, simple 😝...
public class TestPane extends JPanel {
private List<Drawable> drawables;
public TestPane() {
drawables = new ArrayList<>(2);
drawables.add(new Drawable(288, 128, Color.RED));
drawables.add(new Drawable(288, 384, Color.RED));
Timer timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (Drawable drawable : drawables) {
drawable.update();
}
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(288, 512);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Drawable drawable : drawables) {
Graphics2D g2d = (Graphics2D) g.create();
drawable.paint(g2d);
g2d.dispose();
}
}
}
Putting it altogether - runnable example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
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 class Drawable {
private int x, y;
private Color color;
public Drawable(int x, int y, Color color) {
this.x = x;
this.y = y;
this.color = color;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public Color getColor() {
return color;
}
public void update() {
x--;
if (x <= 144) {
reset();
}
}
protected void reset() {
x = 288;
y = (int) (Math.random() * (512 - 20));
}
public void paint(Graphics2D g2d) {
Graphics2D copy = (Graphics2D) g2d.create();
copy.translate(getX(), getY());
copy.setColor(getColor());
copy.drawOval(0, 0, 20, 20);
copy.dispose();
}
}
public class TestPane extends JPanel {
private List<Drawable> drawables;
public TestPane() {
drawables = new ArrayList<>(2);
drawables.add(new Drawable(288, 128, Color.RED));
drawables.add(new Drawable(288, 384, Color.RED));
Timer timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (Drawable drawable : drawables) {
drawable.update();
}
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(288, 512);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Drawable drawable : drawables) {
Graphics2D g2d = (Graphics2D) g.create();
drawable.paint(g2d);
g2d.dispose();
}
}
}
}
"Is there a simpler solution"? Yes, of course, I always go to the hardest possible way to solve a problem first πŸ€ͺ. First, good animation is hard. Seriously. I've been playing around with this sought of thing more nearly 20 years, making a good animation engine which is flexible to meet all the possible needs it might be put to is near impossible mission, especially in a framework which isn't really designed for it.
If you don't belief me, you could have a look at
How do I change Swing Timer Delay inside actionPerformed()
How can I fade out or fade in by command JPanel, its components and its color
which are just a couple of examples how complicated animation can be
Sorry, you'd be amazed how often I get asked "can it be simpler" when it comes to animation ;)
"Every new objects reaching x = 144 should build new ones
So, apparently I may be confused about this particular point. If this means "adding new objects after reaching 144" then this raises some new issues. The primary issue is one over GC overhead, which cause slow downs in the animation. Sure, we're only dealing with about 4-6 objects, but it's one of those things which can come back to byte you if you're not careful.
So I took the above example and made some modifications to the update cycle. This adds a reusePool where old objects are placed and can be re-used, reducing the GC overhead of repeatedly creating and destroying short lived objects.
The decaying List simply ensures that once an object passes the swanPoint, it won't be consider for re-spawning new objects. Sure you could put a flag on the POJO itself, but I don't think this is part of the POJOs responsibility
public TestPane() {
drawables = new ArrayList<>(2);
reusePool = new ArrayList<>(2);
decaying = new ArrayList<>(2);
timer = new Timer(5, new ActionListener() {
private List<Drawable> spawned = new ArrayList<>(5);
#Override
public void actionPerformed(ActionEvent e) {
spawned.clear();
Iterator<Drawable> it = drawables.iterator();
int swapnPoint = getWidth() / 2;
while (it.hasNext()) {
Drawable drawable = it.next();
drawable.update();
if (drawable.getX() > 0 && drawable.getX() < swapnPoint) {
if (!decaying.contains(drawable)) {
decaying.add(drawable);
Drawable newDrawable = null;
if (reusePool.isEmpty()) {
newDrawable = new Drawable(
getWidth() - 20,
randomVerticalPosition(),
randomColor());
} else {
newDrawable = reusePool.remove(0);
newDrawable.reset(getWidth() - 20,
randomVerticalPosition(),
randomColor());
}
spawned.add(newDrawable);
}
} else if (drawable.getX() <= -20) {
System.out.println("Pop");
it.remove();
decaying.remove(drawable);
reusePool.add(drawable);
}
}
drawables.addAll(spawned);
repaint();
}
});
}
This will now allow objects to travel the whole width of the width, spawning new objects as they pass the half way point. Once they pass beyond the visual range of the view, they will be placed into the reuse List so they can be reused again when new objects are required.
Runnable example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
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();
}
TestPane testPane = new TestPane();
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(testPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
testPane.start();
}
});
}
});
}
public class Drawable {
private int x, y;
private Color color;
public Drawable(int x, int y, Color color) {
this.x = x;
this.y = y;
this.color = color;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public Color getColor() {
return color;
}
public void update() {
x--;
}
protected void reset(int x, int y, Color color) {
this.x = x;
this.y = y;
this.color = color;
}
public void paint(Graphics2D g2d) {
Graphics2D copy = (Graphics2D) g2d.create();
copy.translate(getX(), getY());
copy.setColor(getColor());
copy.fillOval(0, 0, 20, 20);
copy.setColor(Color.BLACK);
copy.drawOval(0, 0, 20, 20);
copy.dispose();
}
}
public class TestPane extends JPanel {
private List<Drawable> drawables;
private List<Drawable> decaying;
private List<Drawable> reusePool;
private Color[] colors = new Color[]{Color.BLUE, Color.CYAN, Color.DARK_GRAY, Color.GREEN, Color.MAGENTA, Color.ORANGE, Color.PINK, Color.RED, Color.YELLOW};
private Random rnd = new Random();
private Timer timer;
public TestPane() {
drawables = new ArrayList<>(2);
reusePool = new ArrayList<>(2);
decaying = new ArrayList<>(2);
timer = new Timer(40, new ActionListener() {
private List<Drawable> spawned = new ArrayList<>(5);
#Override
public void actionPerformed(ActionEvent e) {
spawned.clear();
Iterator<Drawable> it = drawables.iterator();
int swapnPoint = getWidth() / 2;
while (it.hasNext()) {
Drawable drawable = it.next();
drawable.update();
if (drawable.getX() > 0 && drawable.getX() < swapnPoint) {
if (!decaying.contains(drawable)) {
decaying.add(drawable);
Drawable newDrawable = null;
if (reusePool.isEmpty()) {
System.out.println("New");
newDrawable = new Drawable(
getWidth() - 20,
randomVerticalPosition(),
randomColor());
} else {
System.out.println("Reuse");
newDrawable = reusePool.remove(0);
newDrawable.reset(getWidth() - 20,
randomVerticalPosition(),
randomColor());
}
spawned.add(newDrawable);
}
} else if (drawable.getX() <= -20) {
System.out.println("Pop");
it.remove();
decaying.remove(drawable);
reusePool.add(drawable);
}
}
drawables.addAll(spawned);
repaint();
}
});
}
public void start() {
drawables.add(new Drawable(getWidth(), 128, randomColor()));
drawables.add(new Drawable(getWidth(), 384, randomColor()));
timer.start();
}
protected int randomVerticalPosition() {
return rnd.nextInt(getHeight() - 20);
}
protected Color randomColor() {
return colors[rnd.nextInt(colors.length - 1)];
}
#Override
public Dimension getPreferredSize() {
return new Dimension(288, 512);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Drawable drawable : drawables) {
Graphics2D g2d = (Graphics2D) g.create();
drawable.paint(g2d);
g2d.dispose();
}
}
}
}
My answer is completely based on MadProgrammer's answer (A comprehensive tutorial actually).
From what I read in the post : "Every new objects reaching x = 144 should build new ones", I think the desired implementation is slightly different:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class ImageAnimator {
public ImageAnimator() {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new AnimationPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
public static class Drawable {
private int x;
private final int y;
private static final Image image = image();
//construct with a random y value
public Drawable(int x) {
this(x, -1);
}
public Drawable(int x, int y) {
this.x = x;
this.y = y < 0 ? (int) (Math.random() * (512 - 20)) : y;
}
public int getX() { return x; }
public int getY() { return y; }
public void update() { x--; }
public Image getImage(){ return image; }
public static Image image() {
URL url = null;
try {
//5.SEP.2021 replaced dead link
//url = new URL("https://dl1.cbsistatic.com/i/r/2017/09/24/b2320b25-27f3-4059-938c-9ee4d4e5cadf/thumbnail/32x32/707de8365496c85e90c975cec8278ff5/iconimg241979.png");
url = new URL("https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/32x32/Circle_Green.png");
return ImageIO.read(url);
} catch ( IOException ex) {
ex.printStackTrace();
return null;
}
}
}
public class AnimationPane extends JPanel {
private final List<Drawable> drawables;
private static final int W = 288, H = 512, CYCLE_TIME = 5;
public AnimationPane() {
drawables = new ArrayList<>(2);
drawables.add(new Drawable(W, H/4));
drawables.add(new Drawable(W, 3*H/4));
Timer timer = new Timer(CYCLE_TIME, e -> animate());
timer.start();
}
private void animate() {
for (Drawable drawable : new ArrayList<>(drawables)) {
drawable.update();
if(drawable.getX() == W/2) {
drawables.add(new Drawable(W)); //random Y
}
if(drawable.getX() <= 0) {
drawables.remove(drawable);
}
}
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(W, H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Drawable drawable : drawables ) {
g.drawImage(drawable.getImage(),drawable.getX(), drawable.getY(), null);
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(()->new ImageAnimator());
}
}
The following solution is based on my previous answer.
I add it in response to MadProgrammer's comment: "A better solution is to pool the objects for re-use".
DrawAblesProducer produces drawable objects on-demand. It also stores surplus object, to prevent producing too many such objects.
I post it as a separate answer because the additional functionality comes with somewhat higher complexity:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class ImageAnimator {
private static final int W = 288, H = 512;
public ImageAnimator() {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new AnimationPane(new DrawAblesProducer()));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
public class AnimationPane extends JPanel {
private final List<Drawable> drawables;
private static final int CYCLE_TIME = 5;
private final DrawAblesProducer producer;
public AnimationPane(DrawAblesProducer producer) {
this.producer = producer;
drawables = new ArrayList<>(2);
drawables.add(producer.issue(W, H/4));
drawables.add(producer.issue(W, 3*H/4));
Timer timer = new Timer(CYCLE_TIME, e -> animate());
timer.start();
}
private void animate() {
for (Drawable drawable : new ArrayList<>(drawables)) {
drawable.update();
if(drawable.getX() == W/2) {
drawables.add(producer.issue(W)); //random Y
}else if(drawable.getX() <= 0) {
drawables.remove(drawable);
producer.retrn(drawable);
}
}
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(W, H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Drawable drawable : drawables ) {
g.drawImage(drawable.getImage(),drawable.getX(), drawable.getY(), null);
}
}
}
//produces `drawable` objects on-demand. stores surplus object, to prevent producing
//too many such objects
public class DrawAblesProducer {
private final Queue<Drawable> warehouse = new LinkedList<>();
public Drawable issue(int x){
return issue(x, -1);
}
public Drawable issue(int x, int y){
Drawable drawable = warehouse.poll();
if(drawable != null ) {
drawable.setX(x); drawable.setY(y);
return drawable;
}
return new Drawable(x, y);
}
public void retrn(Drawable drawable){
warehouse.add(drawable);
}
}
public static class Drawable {
//made static so image is reused for all instances
private static final Image image = image();
private int x, y;
//construct with a random y value
public Drawable(int x) {
this(x, -1);
}
public Drawable(int x, int y) {
setX(x);
setY(y);
}
public int getX() { return x; }
public void setX(int x) { this.x = x;}
public int getY() { return y; }
public void setY(int y) {
this.y = y < 0 ? randomY() : y ;
}
private int randomY() {
int iHeight = image.getHeight(null);
return iHeight + (int) (Math.random() * (H - iHeight));
}
public void update() { x--; }
public Image getImage(){ return image; }
public static Image image() {
URL url = null;
try {
//5.SEP.2021 replaced dead link
//url = new URL("https://dl1.cbsistatic.com/i/r/2017/09/24/b2320b25-27f3-4059-938c-9ee4d4e5cadf/thumbnail/32x32/707de8365496c85e90c975cec8278ff5/iconimg241979.png");
url = new URL("https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/32x32/Circle_Green.png");
return ImageIO.read(url);
} catch ( IOException ex) { ex.printStackTrace(); }
return null;
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(()->new ImageAnimator());
}
}

Java graphics - repaint an image without removing another

I am using Graphics g to draw an image for my player and a map. Whenever the player moves I want to update the player image to the new location without updating the map image. How can I do this?
Thanks in advance for any replies!
Sorry about my messy code.
Main:
package Main;
import java.awt.Dimension;
import javax.swing.JFrame;
public class Main {
public static JFrame frame;
static int size = 32;
static int width = size*20;
static int height = size*17;
static playerObj p = new playerObj(200,200);
static tileMap grid = new tileMap();
public static void main(String[] args) {
frame = new JFrame("Game");
frame.getContentPane().setPreferredSize(new Dimension(width-9, height-9));
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.add(new drawGraphics());
frame.setVisible(true);
}
}
drawGraphics:
package Main;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
public class drawGraphics extends JPanel implements Runnable {
private static final long serialVersionUID = 1L;
boolean running = true;
boolean renderMap = true;
playerObj p = Main.p;
int size = Main.size;
static int x = 0;
static int y = 0;
Thread thread = new Thread(this);
public drawGraphics() {
setFocusable(true);
addKeyListener(new controls());
start();
}
public void start() {
thread.start();
}
public void paint(Graphics g) {
Graphics2D d = (Graphics2D) g;
super.paintComponent(g);
if (renderMap) {
map(d);
}
player(g);
repaint();
}
public void player(Graphics g) {
try {
BufferedImage pImg = ImageIO.read(new File("images/player.png"));
g.drawImage(pImg, p.getX(), p.getY(), null);
} catch (IOException e) {
e.printStackTrace();
}
}
public void map(Graphics g) {
System.out.println("2dfsdf");
Graphics2D d = (Graphics2D) g;
for (int i = 0; i < tileMap.map.length; i++) {
for (int j = 0; j < tileMap.map[i].length; j++) {
d.setColor(tileMap.map[i][j].getC());
d.fillRect(tileMap.map[i][j].getX(), tileMap.map[i][j].getY(),
Main.size, Main.size);
if (tileMap.map[i][j].solid()) {
d.setColor(Color.BLACK);
d.drawRect(tileMap.map[i][j].getX(),
tileMap.map[i][j].getY(), Main.size, Main.size);
}
}
}
}
public void move() {
if (controls.up) {
controls.goUp();
y--;
repaint();
}
if (controls.down) {
controls.goDown();
y++;
repaint();
}
if (controls.left) {
controls.goLeft();
x--;
repaint();
}
if (controls.right) {
controls.goRight();
x++;
repaint();
}
if (controls.place) {
repaint();
}
}
public void run() {
while (running == true) {
move();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Don't draw directly in a JFrame but rather in a JPanel that is displayed within the JFrame.
If you're using Swing as your GUI library, then draw the background as a BufferedImage, and draw that first in your paintComponent method. Then draw your image sprite at whatever location needed next:
#Override // this is in a JPanel extended class
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (backgroundImg != null) {
g.drawImage(backgroundImg, 0, 0, null);
}
if (spriteImg != null) {
g.drawImage(spriteImg, spriteX, spriteY, null);
}
}
Some issues with your code:
Don't override paint and then call super.paintComponent inside of it.
Instead override paintComponent and call the same super method inside.
Never call repaint() inside of paint or paintComponent. Use a Swing Timer instead.
In your player(...) method, you re-read the image in each time the method is called, something that will slow your painting down to a crawl. Don't do this, but instead read the image in once, and save it to a variable.

animated background java game

I was programming a game similar to asteroid, but I do not understand how to spawn the asteroids in the background.
now i spawn an asteroid in the main class but i want create a class for the asteroid ho i do it?
MAIN CLASS
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
if(flag_img)
{
background(g2d);
logo(g2d);
menu(g2d);
spaceship(g2d);
crediti(g2d);
}
}
background function(now)
private void background(Graphics2D g2d)
{
asteroidi_g_x+=r.nextInt(4);
asteroidi_g_y+=r.nextInt(1);
g2d.drawImage(asteroidi_g[0], asteroidi_g_x,asteroidi_g_y,this);
}
background function(what i want)
private void background(Graphics2D g2d)
{
asteroid asteroid = new asteroid[10];
}
and class asteroid
public class asteroid extends JPanel implements ActionListener
{
private BufferedImage images_asteroid;
private boolean flag_img;
private JPanel jp;
private int x,y;
public asteroide_grande(JPanel jp)
{
flag_img = true;
x = (jp.getWidth()/2);
y = (jp.getHeight()/2);
this.jp = jp;
try {
images_asterod = ImageIO.read(this.getClass().getResource("images/asteroid/a1.png"));
} catch(IOException e){flag = false;}
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
if(flag_img)
{
g.drawImage(images_asteroid, 100, 100,this);
}
}
#Override
public void actionPerformed(ActionEvent e)
{
x=x-1;
y=y+1;
repaint();
}
method paintcomponent in class doesn't work
Don't have your Asteroid class extends JPanel. Instead have it as a class that model's asteroid data and has data manipulation methods. You'll also want to have a draw method that take a Graphic context. Something like
public class Asteroid {
Image asteroidImage;
JPanel panel;
int x, y;
public Asteroid(JPanel panel, Image image, int x, int y) {
this.panel = panel;
this.asteroidImage = image;
this.x = x;
this.y = y;
}
public void drawAsteroid(Graphics g) {
g.drawImage(asteroidImage, x, y, panel);
}
public void move() {
x += 5;
}
}
Now you have a model of an asteroid, you can create a List of Asteriod objects and iterate through them and use it's drawAsteroid method to paint them. Something like
public class GamePanel extends JPanel {
List<Asteroid> asteroids;
Image asteroidImage;
public GamePanel(){
asteroidImage = ...
asteroids = new ArrayList<>();
asteroids.add(new Asteroid(GamePanel.this, asteroidImage, 100, 100));
// add more asteriods
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Asteriod asteroid: asteriods) {
asteriod.drawAsteroid(g);
}
}
}
To animate them, you'll want to use a javax.swing.Timer. See more at How to Use Swing Timers. You'll want to manipulate the Asteriod data in the Timer. With the code provided above, you can just call it's move method, then call repaint(). Something like
public GamePanel(){
...
Timer timer = new Timer(30, new ActionListener(){
public void actionPerformed(ActionEvent e) {
Iterator it = asteroids.iterator();
while (it.hasNaext()) {
Asteroid asteriod = (Asteroid)it.next();
asteroid.move();
}
}
});
}
You can see a bunch more complete example of animating multiple objects here and here and here and here and here
Here's a full example. You'll see I included a Rectangle2D object in the Astreroid class. That's just if you want to check for collision detection. You should move the Rectangle2D x and/or y with every Asreroid movement of x and y. Then you can check if asteroid.rectangle.intersects(someOtherObject)
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
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 AsteroidBackground extends JPanel {
private static final int D_W = 400;
private static final int D_H = 600;
BufferedImage asteroidImage;
BufferedImage background;
List<Asteroid> asteroids;
Random random = new Random();
int countToAddAsteroid = 0;
int y;
public AsteroidBackground() {
try {
asteroidImage = ImageIO.read(getClass().getResource("/resources/small-asteroid.png"));
background = ImageIO.read(getClass().getResource("/resources/space.png"));
} catch (IOException ex) {
Logger.getLogger(AsteroidBackground.class.getName()).log(Level.SEVERE, null, ex);
}
asteroids = new ArrayList<>();
y = 0 - asteroidImage.getHeight();
Timer timer = new Timer(40, new ActionListener(){
public void actionPerformed(ActionEvent e) {
if (countToAddAsteroid >= 25) {
int randX = random.nextInt(D_W);
asteroids.add(new Asteroid(AsteroidBackground.this, asteroidImage, randX, y));
countToAddAsteroid = 0;
}
countToAddAsteroid++;
Iterator it = asteroids.iterator();
while (it.hasNext()) {
Asteroid asteroid = (Asteroid)it.next();
if (asteroid.y >= D_H) {
it.remove();
} else {
asteroid.move();
}
}
repaint();
}
});
timer.start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(background, 0, 0, this);
for (Asteroid asteroid : asteroids) {
asteroid.drawAsteroid(g);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(D_W, D_H);
}
public class Asteroid {
Rectangle2D rectangle;
Image asteroidImage;
JPanel panel;
int x, y;
public Asteroid(JPanel panel, Image image, int x, int y) {
this.panel = panel;
this.asteroidImage = image;
this.x = x;
this.y = y;
rectangle = new Rectangle2D.Double(
x, y, image.getWidth(panel), image.getHeight(panel));
}
public void drawAsteroid(Graphics g) {
g.drawImage(asteroidImage, x, y, panel);
}
public void move() {
y += 5;
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.add(new AsteroidBackground());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}

How do I get my image to follow my mouse?

How do I get my image to follow my mouse anywhere on the screen?
The below code makes the image move along the x axis.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
public class PlayerTwo implements KeyListener, MouseListener, MouseMotionListener{
public static int PLAYER_HEIGHT = 15;
public static int PLAYER_WIDTH = 15;
private Image p2Image = null;
private static int x = 0;
private static int y = 0;
private int heightPosition = 0;
Main main = null;
public PlayerTwo(Image pi, Main m ){
main = m;
p2Image = pi;
y = (int)((Main.WIDTH*2)+(PLAYER_WIDTH*2));
heightPosition = Main.HEIGHT-PLAYER_HEIGHT-20;
}
public void drawPlayer(Graphics g){
g.drawImage(p2Image, y, heightPosition, main);
}
public void keyTyped(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
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) {
}
public void mouseDragged(MouseEvent e) {
}
public void mouseMoved(MouseEvent me) {
int newX = me.getX();
int newY = me.getY();
if(newY > (Main.HEIGHT+PLAYER_HEIGHT+10)){
y = Main.HEIGHT+PLAYER_HEIGHT+10;
}else{
y = newY;
}
// if (newX > (Main.WIDTH-PLAYER_WIDTH-10)){
// x = Main.WIDTH-PLAYER_WIDTH-10;
// }else{
// x = newX;
// }
}
}
Updated with Main...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
public class Main extends JFrame implements Runnable {
public static int WIDTH = 600;
public static int HEIGHT = 600;
private int gameSpeed = 100;
PlayerOne playOne = null;
PlayerTwo playTwo = null;
Image p1Image = null;
Image p2Image = null;
Image backImage = null;
Graphics offscreen_high;
BufferedImage offscreen;
public Main(String frameTitle) {
super(frameTitle);
p1Image = new javax.swing.ImageIcon("src/resources/player1.gif").getImage();
p2Image = new javax.swing.ImageIcon("src/resources/player2.gif").getImage();
backImage = new javax.swing.ImageIcon("src/resources/back.png").getImage();
offscreen = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
offscreen_high = offscreen.createGraphics();
playOne = new PlayerOne(p1Image, this);
playTwo = new PlayerTwo(p2Image, this);
addKeyListener(playOne);
addKeyListener(playTwo);
addMouseListener(playTwo);
addMouseMotionListener(playTwo);
setSize(WIDTH, HEIGHT);
setVisible(true);
startGame();
}
public void startGame() {
Thread thread = new Thread(this);
thread.start();
}
public void paint(Graphics g) {
offscreen_high.setColor(Color.black);
offscreen_high.fillRect(0, 0, WIDTH, HEIGHT);
offscreen_high.drawImage(backImage, 0, 0, this);
playOne.drawPlayer(offscreen_high);
playTwo.drawPlayer(offscreen_high);
g.drawImage(offscreen, 0, 0, this);
}
// public void update(Graphics g){
// paint(g);
// }
public void run() {
int count = 0;
while (true) {
try {
Thread.sleep(gameSpeed);
} catch (InterruptedException ie) {
}
repaint();
count++;
}
}
public static void main(String[] args) {
Main main = new Main("Game On!");
}
}
Generally, you need some way to tell the UI that it should be updated.
Assuming that Main is some kind of component (and it's also responsible for painting the Player), you should be calling its repaint method in the mouseListener
But without more details, this is more of a guess
Updated
After a muck around with the code, the main problem, as I see it, is your trying to draw the image only the horizontal axis (x) using the vertical position (y)...
public void drawPlayer(Graphics g){
//g.drawImage(p2Image, y, heightPosition, main);
g.drawImage(p2Image, x, heightPosition, main);
}
To get it to work, you're going to have to uncomment the code in you mouseMoved method so that the x position updates.
You should also avoid painting to top level containers, the main reason (apart from the fact that you can screw up the paint process) is that top level containers are not double buffered.
Instead, you should move your entire game container over to something like a JPanel and override it's paintComponent method (and don't for get to call super.paintComponent)

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