So I've been trying to make a sprite object (called heart) that can be moved around by players on the JPanel "Battle box". Very similar code has worked for me in the past but weirdly not now. I need to clarify, this is PART of a project, not the whole thing. The BattleBox JPanel object gets called from a different part of the program, though technically it's possible the error isn't within these classes
Also btw not all the code is written for the new ones since I need to make it appear first before I can start making it move around and such xD Anyway any help people can give is appreciated and, just in case it wasn't obvious, I'm self taught and bad so sorry if the answer's super obvious
Also slight side note, the clock also doesn't show but I've kinda accepted fate with that one for now
Battle screen class
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
import MainMenu.MainMenu;
#SuppressWarnings("serial")
public class BattleScreen extends JPanel {
private Heart heart;
private Thread thread;
private Clock clock;
public BattleScreen() {
this.setBounds(0, 0, MainMenu.frame.getWidth(), MainMenu.frame.getHeight());
this.setBackground(Color.black);
clock = new Clock(this);
heart = new Heart();
heart.allowMovement(true);
MainMenu.con.add(this);
this.repaint();
this.setVisible(true);
thread = new Thread() {
public void run() {
repaint();
}
};
thread.start();
}
#Override
public void paintComponent (Graphics g) {
super.paintComponent(g);
heart.draw(g, this);
clock.draw(g, this);
}
}
Heart class
import java.awt.Component;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Heart {
int xPos, yPos, width, height;
Thread thread;
BufferedImage image;
public Heart() {
width = 44;
height = 44;
xPos = (MainMenu.MainMenu.con.getWidth() - width) / 2;
yPos = (MainMenu.MainMenu.con.getHeight() - height) / 2;
try {
image = ImageIO.read(new File("src/BattleScreen/heart.png"));
} catch (IOException e) {
e.printStackTrace();
}
this.setBackground(Color.BLUE);
this.repaint();
thread = new Thread() {
public void run() {
updateMovement();
}};
}
void draw (Graphics g, Component c) {
g.drawImage(image, xPos, yPos, width, height, c);
}
}
Classes that worked in the past:
OLD BattleScreen
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import MainMenu.MainMenu;
#SuppressWarnings("serial")
public class BattleScreen extends JPanel implements KeyListener {
private static JFrame frame = MainMenu.frame;
static Heart sprite = new Heart(frame.getWidth()/2, frame.getHeight()/2, 20, 20);
public BattleScreen() {
addKeyListener(this);
setFocusable(true);
setBounds(0, 0, frame.getWidth(), frame.getHeight());
setBackground(Color.black);
MainMenu.con.add(this);
new Thread(() -> {
while (true) {
sprite.updateMovement();
repaint();
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
public void paintComponent (Graphics g) {
super.paintComponent(g);
sprite.draw(g, this);
}
}
OLD Heart class
package DEADBattleScreen;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Image;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Heart {
Image img;
int xPos, yPos, width, height;
Thread thread;
Heart(int x, int y, int w, int h) {
this.xPos = x;
this.yPos = y;
this.width = w;
this.height = h;
try {
img = ImageIO.read(new File("src/battlescreen/heart.png"));
} catch (IOException e ) {
e.printStackTrace();
}
}
void draw (Graphics g, Component c) {
g.drawImage(img, xPos, yPos, width, height, c);
}
}
Related
when I call a repaint method its flickering, I was searching on the internet about doubleBuffered but its stil flickering all of objects on the same time,
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.ArrayList;
import java.util.Random;
public class GuiGame extends JFrame implements ActionListener {
private final Flower flower;
private final ArrayList<MyObjects> objekty;
private Image dbImage;
private Graphics dbGraphics;
public GuiGame() {
this.setFocusable(true);
Timer timer = new Timer(40, this);
timer.start();
this.setVisible(true);
/*
public void paint(Graphics g) {
this.dbImage = this.createImage(this.getWidth(), this.getHeight());
this.dbGraphics = this.dbImage.getGraphics();
this.paintComponents(g);
g.drawImage(this.dbImage, 0 ,0, this);
}
*/
public void paint(Graphics g) {
//when i use doubleBuffering this method was called paintComponents
g.drawImage(background.getImage(), 0, 0, this);
g.drawImage(flower.getImage(), flower.getPozX(), flower.getPozY(), this);
String skore = "Score: " + player.getScore() ;
g.drawString(skore, 20, 50);
this.paintObjects(g);
}
public void paintObjects(Graphics g) {
if (this.objekty != null) {
for (objekty o : this.objekty) {
o.move();
g.drawImage(o.getImage(), o.getPozX(), o.getPozY(), this);
}
}
}
when I used doubleBuffering I tried to slow down a timer e.g. to 1000, it was blank page for most of the time and my objects was there only for moment.
when I do not used it, only last objects which i draw were flickering.
how can I avoid?
I would recommend you start by taking a look at Painting in AWT and Swing and Performing Custom Painting.
Swing components, when used correctly, are double buffered by default.
Painting is very complex, there's a lot of work that goes into a paint pass. When you override a "paint" method, you either need to honour the existing workflow (ie, call the super.paintXxx method) or be prepared to take over the full responsibility of the method. Your code has circumvented whole sections of the paint workflow, this is never a good idea.
It's not generally recommend that you extend from top level containers like JFrame, you're not adding any new functionality and they are generally complex components to start with. JFrame for example, is actually a composite component, that is, it has a number of child components added to it that form it's core functionality
By overriding paint (of JFrame), you start competing with these components, and because a child component can be painted without the parent been involved, this can lead to any number of issues.
Instead, start with a JPanel and override it's paintComponent method, this guarantees that you will be operating in a double buffered workflow.
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.awt.image.BufferedImage;
import java.io.IOException;
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.Timer;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
JFrame frame = new JFrame();
frame.add(new MainPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
public class MainPane extends JPanel {
private int xDelta = 1;
private int xPos = 0;
private BufferedImage ufo;
public MainPane() throws IOException {
ufo = ImageIO.read(getClass().getResource("/images/ufo.png"));
Timer timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
xPos += xDelta;
if (xPos + ufo.getWidth() > getWidth()) {
xPos = getWidth() - ufo.getWidth();
xDelta *= -1;
} else if (xPos < 0) {
xPos = 0;
xDelta *= -1;
}
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
paintUFO(g2d);
g2d.dispose();
}
protected void paintUFO(Graphics2D g2d) {
int y = (getHeight() - ufo.getHeight()) / 2;
g2d.drawImage(ufo, xPos, y, this);
}
}
}
I dont know about you, but I tried so hard on how to create a news ticker. I found approaches that would append what was off the screen on to the back side of it. This caused the animation to jump by a whole character. Then I found methods that would not loop until the text was completely off the screen. After asking a ton of questions on this I thought it would be nice if I just posted my final solution for all the people wondering how to make it. Here you go.
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Scroll2 extends JPanel implements ActionListener{
private int x;
private int x2;
private int y;
private int width;
private String textIV;
private Color textColorIV;
private Color bGColorIV;
private Font f=(new Font("SansSerif", Font.BOLD,34));
Timer t;
// TODO font size
public Scroll2(String text, Color textColor, Color bGColor)
{
x=Integer.MIN_VALUE;
x2=Integer.MIN_VALUE;
y=0;
textIV=text;
this.setVisible(true);
Timer refreshTimer = new javax.swing.Timer(1000/50, this);
refreshTimer.start();
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.setFont(f);
if ( x == Integer.MIN_VALUE ){
width=g.getFontMetrics().stringWidth(textIV);
x = 0;
y = (int) (getHeight()*.6);
}
if ( x2 == Integer.MIN_VALUE ){
x2 = g.getFontMetrics().stringWidth(textIV);
}
g.setColor(bGColorIV);
g.fillRect(this.getX(), this.getY(), (int)this.getBounds().getWidth(), (int)this.getBounds().getHeight());
g.setColor(textColorIV);
g.drawString(textIV, x, y);
g.drawString(textIV, x2, y);
}
public void refresh ()
{
if(x>=(-width)&&x2!=Integer.MIN_VALUE)
{
x=x-2;
x2=x2-2;
}
if(x2<=-width&&x!=Integer.MIN_VALUE)
{
x2=width;
}
if(x<-width&&x!=Integer.MIN_VALUE)
{
x=width;
}
repaint();
}
#Override
public void actionPerformed(ActionEvent e) {
this.refresh();
}
}
I am trying to simply rotate an image in a for loop like so:
class MyCanvas extends JComponent {
AffineTransform identity = new AffineTransform();
Image arrow;
Double angle = -180.0;
public void spin() {
angle += 10.0;
for(int i = 0; i < 10; i++) {
repaint();
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
arrow = Toolkit.getDefaultToolkit().getImage("red-arrow-right-th.png");
// Rotate + translate
AffineTransform trans = new AffineTransform();
trans.setTransform(identity);
trans.translate(getWidth()/2, getHeight()/2);
trans.rotate(Math.toRadians(angle));
System.out.println(trans);
g2.drawImage(arrow, trans, this);
g2.finalize();
}
}
However when I run call spin() in main, it appears to apply only a single rotation, whilst still printing out the loop correctly. What am I overlooking something?
I've transformed your code using the recommendation of MadProgrammer:
Don't override paint, override paintComponent.
Call super.paint before performing any custom painting,
Never call finalize on anything and especially not on objects you didn't create yourself.
Use a Swing Timer
Note the following
A qualified this is used to access the ImageRotationView instance from the ActionListener inner class.
AffineTransform.getRotateInstance returns a transform that rotates coordinates around an anchor point.
Speed can be optimized but it works correctly like this.
This class works as a standalone application
A file named dice.png should be present in the base directory.
.
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.geom.AffineTransform;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.Timer;
public class ImageRotationFrame {
public static void main(String[] args) {
new ImageRotationFrame();
}
public ImageRotationFrame() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Testing");
frame.setSize(400, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new ImageRotationComponent());
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
private class ImageRotationComponent extends JComponent {
Image arrow;
double angle = 0.0;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
angle += 0.4;
AffineTransform trans = AffineTransform.getRotateInstance(angle, getWidth() / 2, getHeight() / 2);
((Graphics2D) g).drawImage(arrow, trans, this);
}
public ImageRotationComponent() {
try {
arrow = ImageIO.read(new File("dice.png"));
} catch (IOException e) {
e.printStackTrace();
}
int delay = 500; //milliseconds
ActionListener taskPerformer = new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
ImageRotationComponent.this.repaint();
}
};
new Timer(delay, taskPerformer).start();
}
}
}
class DrawIma extends JPanel{
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (int i=0;i<20;i++){
for (int j=0;j<20;j++) {
g.drawImage(BuArr[i*20+j], 20*i, 20*j, 20, 20, null);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
In this part, BuArr are the 400 blocks divided from a BufferedImage, now i want them to be draw one by one, but the method can not draw the blocks separately, how can i do this?
Swing is single thread and not thread safe.
This means that you should not perform any long running or blocking (Thread.sleep) operations within the IU thread (the Event Dispatching Thread). It also means that you can not update, modify or create UI elements outside of the EDT context.
Instead, use a Swing Timer to generate a repeated callback at a specified interval and render the portions of the image to something like a BufferedImage, which you can the paint to the component via its paintComponent method...
See Concurrency in Swing and How to use Swing Timers for more details
Because it was a good time waster
This generates a List of Rectangles which represent the individual blocks I want to paint, I then randomise the List and run the Timer, picking the top most Rectangle off the List and using BufferedImage#getSubImage to draw it from the master to the buffer, which gets painted to the screen...
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
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 TestImage {
public static void main(String[] args) {
new TestImage();
}
public TestImage() {
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 TestPane extends JPanel {
private BufferedImage master;
private BufferedImage copy;
private List<Rectangle> blocks;
public TestPane() {
setBackground(Color.BLACK);
try {
master = ImageIO.read(new File("..."));
copy = new BufferedImage(master.getWidth(), master.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = copy.createGraphics();
AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f);
g2d.setComposite(composite);
g2d.setColor(new Color(0, 0, 0, 0));
g2d.fillRect(0, 0, master.getWidth(), master.getHeight());
g2d.dispose();
int blockSize = 40;
int width = master.getWidth();
int height = master.getHeight();
float aspect = Math.min(width, height) / (float) Math.max(width, height);
int blockHeight = blockSize;
blocks = new ArrayList<>(blockSize * 2);
for (int y = 0; y < master.getHeight(); y += blockHeight) {
if (y + blockHeight > master.getHeight()) {
blockHeight = master.getHeight() - y;
}
int blockWidth = blockSize;
for (int x = 0; x < master.getWidth(); x += blockWidth) {
if (x + blockWidth > master.getWidth()) {
blockWidth = master.getWidth() - x;
}
Rectangle block = new Rectangle(x, y, blockWidth, blockHeight);
blocks.add(block);
}
}
Collections.shuffle(blocks);
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (blocks.isEmpty()) {
((Timer) e.getSource()).stop();
} else {
Graphics2D g2d = copy.createGraphics();
Rectangle block = blocks.remove(0);
g2d.drawImage(master.getSubimage(block.x, block.y, block.width, block.height), block.x, block.y, TestPane.this);
g2d.dispose();
repaint();
}
}
});
timer.start();
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return master == null ? new Dimension(200, 200) : new Dimension(master.getWidth(), master.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (copy != null) {
int x = (getWidth() - copy.getWidth()) / 2;
int y = (getHeight() - copy.getHeight()) / 2;
g2d.drawImage(copy, x, y, this);
}
g2d.dispose();
}
}
}
I want to display a GameOver image in a pacman game after lives are over. But I call the paintGameOverScreen(Graphics g) and then I need to initialize g. Is there any other way to do this?
This is my Lives class
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
public class Lives{
private int lives;
public Lives() {
lives = 1;
}
public void removeLife() {
lives--;
if(lives==0){
System.out.println("END GAME");
paintGameOverScreen(g);
System.exit(0);
}
}
public void paintGameOverScreen(Graphics g) {
ImageIcon i = new ImageIcon("src\image");
Image image = i.getImage();
int x = 0;
int y = 0;
g.drawImage(image, x, y, 100,100,null);
}
public void paint(Graphics g) {
g.setColor(Color.WHITE);
g.fillRect(5*20, 25*20, 100, 30);
g.setColor(Color.BLACK);
String result = "Lives: " + lives;
g.drawString(result, 6*20, 26*20);
}
}
You never call paint() or paintComponent() yourself, you always go through repaint() which will take care of setting up the appropriate Graphics
Just to show what #mKorbel is referring to:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Lives extends JPanel {
private int lives;
private ImageIcon gameOverImage;
public Lives() {
try {
gameOverImage = new ImageIcon(new URL("http://imgup.motion-twin.com/dinorpg/0/f/77acf80b_989624.jpg"));
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
lives = 5;
}
public void removeLife() {
if (lives > 0) {
lives--;
System.out.println("Left lives: " + lives);
repaint();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (lives > 0) {
System.out.println("Still have " + lives + " lives");
g.setColor(Color.WHITE);
g.fillRect(5 * 20, 25 * 20, 100, 30);
g.setColor(Color.BLACK);
String result = "Lives: " + lives;
g.drawString(result, 6 * 20, 26 * 20);
} else if (gameOverImage != null) {
System.out.println("Game over");
int x = (getWidth() - gameOverImage.getIconWidth()) / 2;
int y = (getHeight() - gameOverImage.getIconHeight()) / 2;
g.drawImage(gameOverImage.getImage(), x, y, gameOverImage.getIconWidth(), gameOverImage.getIconHeight(), this);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 600);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame(Lives.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final Lives lives = new Lives();
frame.add(lives);
frame.pack();
frame.setVisible(true);
// Dummy timer that reduces the lives every second. For demo purposes only of course
Timer t = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
lives.removeLife();
}
});
t.start();
}
});
}
}
for public void paint(Graphics g) { is there missed container,
JPanel (in some cases JComponent) could be container for todays Java
have to use paintComponent instead of paint()
inside paintComponent you can to flag for paintGameOverScreen, then there paint only BufferedImage
prepare all Objects before, as local variable, do not load any FileIO (load images) inside paint(), paintComponent()
Here is how I did:
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
public class InitializeGraphics
{
static BufferedImage buffer = null;
static int height = 10;
static int width = 10;
static Graphics2D g2;
public InitializeGraphics() {
buffer = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE);
g2 = buffer.createGraphics();
g2.fillOval(2, 2, 2, 2);
g2.dispose();
}
protected void paintComponent(Graphics g) {
int x = 0;
int y = 0;
g.drawImage(buffer, x, y, width, height, null);
}
public Graphics2D getGraphics(){
return g2;
}
}
Then somewhere:
InitializeGraphics instance=new InitializeGraphics();
Graphics2D gG2 = instance.getGraphics();