I am currently working on a brick breaker game and have run into an issue with the paintComponent method. The first time repaint is called (to invoke paintComponent) everything works fine, but the second time it is called the program gets stuck on the last line of paintComponent.
Below is code and screenshots of the issue I am having.
MyPanel Code:
public class MyPanel extends JPanel {
private static Model model;
public MyPanel(){
setBorder(BorderFactory.createLineBorder(Color.black));
setBackground(Color.BLACK);
}
public void updateModel(Model modelArg){
model = modelArg;
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
draw(g2);
}
//Draw each game component
public static void draw(Graphics2D g2){
draw(g2, g, model.getPaddle());
draw(g2, g, model.getBall());
draw(g2, g, model.getBrickArray());
}
//Draw Paddle
public static void draw(Graphics2D g2, Paddle paddle){
Rectangle paddleImage = new Rectangle(paddle.getX(),paddle.getY(), paddle.getWidth(),paddle.getHeight());
g2.setColor(paddle.getColor());
g2.fill(paddleImage);
g2.draw(paddleImage);
}
//Draw Circle
public static void draw(Graphics2D g2, Ball ball){
Double x = Double.valueOf(ball.getX());
Double y = Double.valueOf(ball.getY());
Double height = Double.valueOf(ball.getHeight());
Double width = Double.valueOf(ball.getWidth());
Ellipse2D ballImage = new Ellipse2D.Double(x, y, height, width);
g2.setColor(ball.getColor());
g2.fill(ballImage);
g2.draw(ballImage);
}
//Draw Bricks
public static void draw(Graphics2D g2, Brick[] brickArray){
for(int i = 0; i<brickArray.length; i++){
Brick brick = brickArray[i];
if(!brick.isDestroyed()) {
Rectangle brickImage = new Rectangle(brick.getX(), brick.getY(), brick.getWidth(), brick.getHeight());
g2.setColor(brick.getColor());
//g2.setColor(Color.BLACK);
g2.fill(brickImage);
g2.draw(brickImage);
}
else{
g2.clearRect(brick.getX(), brick.getY(), brick.getWidth(), brick.getHeight());
}
}
}
}
Here is the View class that creates the MyPanel object and calls repaint
public class View extends JFrame {
private JFrame frame;
private static Model model;
private MyPanel panel;
public View() {
model = new Model();
}
//Creates Frame object
//Creates MyPanel object
//Paints MyPanel object
public void createFrame(Model modelArg) {
model = modelArg;
frame = new JFrame();
frame.setSize(500, 550);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("Breakout Ball");
frame.getContentPane().setBackground(Color.BLACK);
frame.setResizable(false);
panel = new MyPanel();
panel.updateModel(model);
frame.add(panel);
frame.setVisible(true);
}
public void updateView(Model modelArg) {
model = modelArg;
revalidate();
panel.updateModel(model);
panel.repaint();
}
}
And here is the controller
public class Controller {
private Model model;
public View view;
public Controller(Model model, View view){
this.model = model;
this.view = view;
initView();
int i = 10;
while(i>0){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
doCycle();
}});
model.getBrickArray()[0].setDestroyed(true);
i--;
}
}
public void initView(){
model.createGamePieces();
//view.initGamePieces(model.getPaddle());
view.createFrame(model);
}
public void doCycle(){
view.updateView(model);
}
}
Image of debugger when the code gets stuck
If the above image is not showing up, it displays how in the debugger under variables it just says "The application is running" and there are no options to step over, step into etc.
If anyone has any suggestions please let me know!
Edit:
I just cleaned up the code to make it a minimal reproducible example per request. I wasn't sure how much to remove because I'm not exactly sure the scope of what is causing this problem, so I leaned towards removing less code.
Thank you,
Jon
Related
I'm trying to draw a shape multiple times inside a JPanel to mimic an animation (a moving shape across it). The problem is that the shape is being redrawn and thus appearing on the screen multiple times on different places, when all i pretend is that only one shape is visible at a time when it's being redrawn.
The method super.paintComponent() should be responsible for repainting JPanel's background and thus, only one shape should be visible when the redrawing is being performed.
I have the following code (it has been reduced/simplified for the sake of simplicity and better understanding):
public class Animated_Shape_Test extends JFrame
{
public static void main(String args[]) throws IOException
{
new Animated_Shape_Test();
}
public Animated_Shape_Test() throws IOException
{
this.setSize(500, 500);
this.setPreferredSize(new Dimension(500, 500));
this.setLocation(new Point(430, 150));
this.setTitle("Database Launcher v1.0");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setResizable(false);
this.setVisible(true);
DBPanel panel = new DBPanel();
getContentPane().add(panel, BorderLayout.CENTER);
}
}
final class DBPanel extends JPanel implements Runnable
{
int musicShapePosX = 85;
int musicShapePosY = 100;
int SEGMENT_SHAPE_LENGTH = 50;
int SHAPE_HEIGHT = 10;
float SHAPE_SPEED = 7.5f;
CustomShapeButton musicShapeButton = new CustomShapeButton(musicShapePosX, musicShapePosY, SEGMENT_SHAPE_LENGTH, SHAPE_HEIGHT);
private ArrayList<Shape> shapes = null;
protected DBPanel() throws IOException
{
shapes = new ArrayList();
shapes.add(musicShapeButton);
this.setOpaque(true);
this.setFocusable(true);
startThread();
}
public void startThread()
{
Thread t = new Thread(this);
t.start();
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(getBackground().darker());
g2.fillRect(0, 0, getWidth(), getHeight());
g2.setColor(Color.BLACK);
g2.draw(musicShapeButton);
}
public void delay(int milliseconds)
{
try
{
Thread.sleep(milliseconds);
}
catch (InterruptedException e)
{
}
}
#Override
public void run()
{
while (true)
{
delay(35);
animateButtonShapeMusic();
repaint();
}
}
public void animateButtonShapeMusic()
{
if (musicShapePosY < 228)
{
musicShapePosY = (int)(musicShapePosY + SHAPE_SPEED);
musicShapeButton.drawShape(musicShapePosX, musicShapePosY, SEGMENT_SHAPE_LENGTH, SHAPE_HEIGHT);
}
}
}
I'm getting several shapes being redrawn across the screen (animation).
I expect a single shape being redrawn across the screen (animation).
Thank you in advance.
PaintComponent doest paint figures. Just nothing is happening, clean Jframe appear.
I think something is wrong with list or with the way i called method
List is in class with Paint Component
public class Paint extends JPanel implements ActionListener {
List<Figures> figuresList = new ArrayList<Figures>();
Timer t = new Timer(5, this);
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (Figures figure : figuresList) {
figure.drawItself(g, figure.getLocationX(), figure.getLocationY());
}
t.start();
}
#Override
public void actionPerformed(ActionEvent e) {
{
for (Figures figure : figuresList) {
if (figure.getLocationX() < 0 || figure.getLocationX() > 540) {
figure.setVelocityX(-figure.getVelocityX());
}
if (figure.getLocationY() < 0 || figure.getLocationX() > 220) {
figure.setVelocityY(-figure.getVelocityY());
}
figure.setLocationX(figure.getLocationX()
+ figure.getVelocityX());
figure.setLocationY(figure.getLocationY()
+ figure.getVelocityY());
}
}
repaint();
}
And drawitself:
public class Circle implements Figures {
public int locationX = 12;
public int locationY = 12;
public int velocityX =1;
public int velocityY =1;
public void drawItself(Graphics g, int locationX, int locationY){
this.locationX = locationX;
this.locationY = locationY;
g.drawOval(locationX, locationY, 40, 40);
g.fillOval(locationX, locationY, 40, 40);
}
Main:
public static void main(String[] args) {
Circle c = new Circle();
Quadrat q = new Quadrat();
Paint p = new Paint();
p.figuresList.add(c);
p.figuresList.add(q);
GUI.Configuration();
}
GUI
public class GUI {
public static void Configuration(){
JFrame frame = new JFrame("Figures Animation");
frame.setSize(600,300);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new Paint();
frame.getContentPane().add(BorderLayout.CENTER, panel);
}
You create and add a Paint instance here:
public class GUI {
public static void Configuration(){
JFrame frame = new JFrame("Figures Animation");
frame.setSize(600,300);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new Paint(); // *** new Paint is here, but nothing is added
frame.getContentPane().add(BorderLayout.CENTER, panel);
}
But nothing of use has been added to it. All the important stuff is added to a completely different Paint JPanel, one that is never displayed:
public static void main(String[] args) {
Circle c = new Circle();
Quadrat q = new Quadrat();
Paint p = new Paint(); // **** ANOTHER new Paint is here, and it gets goodies
p.figuresList.add(c);
p.figuresList.add(q);
// but is never added to a JFrame and is never displayed.
GUI.Configuration();
}
Don't do this. Create one Paint JPanel, one only, add the important components to it, and then only add that one to the JFrame. Most important, don't just type in code, think and plan your program before committing it to code, and you won't see errors like this.
Also, and again, do not start a Timer from within paintComponent and don't create Circle there. You can draw your Circle instance in paintComponent, but create it and start your Timer within the Paint constructor.
I am trying to draw a circle in the center of a window, and I can't seem to get it right, should be really easy! My understanding is that if you set a JPanel as the content pane of a JFrame, the default layout is a flowLayout and that drawing should start from the top left of the screen as 0,0. To try and figure out what's going on I drew a blue background filling the JPanel, but it seems to have a margin like so:
When the window gets smaller than the blue rectangle, the drawing starts to get clipped from the opposite side:
What's going on! Here is my code:
import javax.swing.*;
import java.awt.*;
public class Test extends JFrame {
public static void main(String args[])
{
Test test = new Test();
test.Start();
}
public void Start()
{
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(500, 500);
CirclePanel circlePanel = new CirclePanel();
this.setContentPane(circlePanel);
this.setVisible(true);
}
public class CirclePanel extends JPanel
{
private int radius = 200;
public void paintComponent(Graphics g) {
g.setColor(Color.blue);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
g.setColor(Color.red);
int diameter = radius * 2;
g.fillOval(getX(), getY(), diameter, diameter);
}
public int getX()
{
return (this.getWidth()/2) - radius;
}
public int getY()
{
return (this.getHeight()/2) - radius;
}
}
}
One big issue, you're unknowingly overriding two critical methods used by the layout managers to position components, the getX() and getY() methods, and thereby you're messing with the JPanel's placement.
So first and foremost, rename these methods so you don't accidentally move the JPanel.
Also, don't forget to call the super's paintComponent method, and avoid calling setSize(). Instead override getPreferredSize on your JPanel, and pack your JFrame.
e.g.,
public int getMyX() {
return myX;
}
public int getMyY() {
return myY;
}
For example
import javax.swing.*;
import java.awt.*;
public class Test extends JFrame {
public static void main(String args[]) {
//!!
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Test test = new Test();
test.Start();
}
});
}
public void Start() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// this.setSize(500, 500);
CirclePanel circlePanel = new CirclePanel();
setContentPane(circlePanel);
pack();
setVisible(true);
}
public class CirclePanel extends JPanel {
private static final int PREF_W = 500;
private static final int PREF_H = PREF_W;
private int radius = 200;
public void paintComponent(Graphics g) {
super.paintComponent(g); //!!
g.setColor(Color.blue);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
g.setColor(Color.red);
int diameter = radius * 2;
g.fillOval(getMyX(), getMyY(), diameter, diameter);
}
//!!
public int getMyX() {
return (this.getWidth() / 2) - radius;
}
//!!
public int getMyY() {
return (this.getHeight() / 2) - radius;
}
//!!
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
}
}
The problem is that you're overriding getX() and getY(). Rename those to something else, and your code will work as expected. Also, it's a good idea to turn on compiler warnings for missing #Override annotations, and heed those warnings. (That would have notified the methods override superclass methods).
Try using this.add(circlePanel) instead if this.setContentPane(circlePanel) and set the size of the JPanel to be the same size as the JFrame
My drawing class: for example I just want to draw a simple line
public class DrawNot1 extends JPanel {
private BasicStroke BS = new BasicStroke(2);
private int x;
private int y;
public DrawNot1(int x, int y){
setSize(100, 100);
this.x = x;
this.y = y;
}
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
doDrawing(g);
}
private void doDrawing(Graphics g){
Graphics2D g2d = (Graphics2D) g;
g2d.setStroke(BS);
g2d.drawLine(x, y, x, y+10);
}
my JFrame class:
public class Main extends JFrame{
private int x;
private int y;
public Main() {
initUI();
}
public void initUI() {
setSize(600, 500);
setTitle("Points");
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(new DrawNot1(20, 20));
add(new JButton("button1"));
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Main ex = new Main();
ex.setVisible(true);
}
});
}
}
I want to show my drawing next to the button but that doesn't appear the only component that appear is the button, my drawing does not.
My ultimate goal is when I press the button my drawing is appear near the button.
JFrame uses a BorderLayout by default, adding two components to the default (CENTRE) position means that only the last one added will be shown.
Try adding the button to the SOUTH position instead
add(new JButton("button1"), BorderLayout.SOUTH);
You may also find the overriding the getPreferredSize method of DrawDot1 and providing a suitable value will also result in a better output
Here is my code it is a guy and a background but only one thing shows up at a time or sometimes nothing at all.
I have a brain class, a frame class, a redPlayer class, and a background class.
The way it works is the brain makes a player and a brain and adds it to the frame.
I think it has something to do with layouts but I tried everything but nothing works.
Please Help!!!
Thanks in advance.
here is the brain:
public class Brain
{
private Frame frame;
private static RedPlayer redPlayer;
private Background background;
private SensorKeys sensor;
public Brain()
{
frame = new Frame();
redPlayer = new RedPlayer();
background = new Background();
sensor = new SensorKeys();
frame.addComponent(redPlayer);
frame.addComponent(background);
frame.addKeySensor(sensor);
redPlayer.revalidate();
}
public static void setRedPlayerVelX(double vx)
{
redPlayer.setVelX(vx);
}
public static void setRedPlayerVelY(double vy)
{
redPlayer.setVelY(vy);
}
public static void makeRedPlayerBullet()
{
}
}
`
here is the frame class
public class Frame
{
private JFrame jf;
public Frame()
{
drawFrame();
}
public void drawFrame()
{
jf = new JFrame();
jf.setSize(800, 600);
jf.setLocation(10, 10);
jf.setVisible(true);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setBackground(Color.WHITE);
jf.setLayout(null);
}
public void addComponent(JComponent jc)
{
jc.setBounds(jc.getX(), jc.getY(), 100, 100);
jf.add(jc);
}
public void addPanel(JPanel jp)
{
jf.add(jp);
}
public void addKeySensor(KeyListener kl)
{
jf.addKeyListener(kl);
}
}
here is the player class:
public class RedPlayer extends JComponent implements ActionListener
{
private int x,y;
private double velX = 0, velY = 0;
private Timer timer = new Timer(2,this);
private Image redplayer;
public RedPlayer()
{
x = 100;
y = 100;
ImageIcon II = new ImageIcon("redPlayerRight.png");
redplayer = II.getImage();
revalidate();
timer.start();
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D graphics = (Graphics2D) g;
graphics.drawImage(redplayer, x,y, null);
}
public void actionPerformed(ActionEvent arg0)
{
x += velX;
y += velY;
repaint();
revalidate();
}
public void setVelX(double vx)
{
velX = vx;
}
public void setVelY(double vy)
{
velY = vy;
}
}
And Lastly here is the background class:
public class Background extends JComponent
{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D graphics = (Graphics2D) g;
graphics.setColor(Color.green);
graphics.fillRect(0, 400, 500, 200);
}
}
I think it has something to do with layouts
Yes, you should read the Swing tutorial on Using Layout Managers to better understand how they work and for examples.
The default layout manager for a JFrame is a BorderLayout. You can't just add 3 components to the same area of the BorderLayout.
I don't know what you are trying to accomplish but start simple. Since you have a background the basic code should be something like:
redPlayer = new RedPlayer();
background = new Background();
background.add( redPlayer );
frame.addComponent(background);
So the red player should display on top of the background. And the background is added to the frame.
Of course you must use a proper layout manager for the background. And you must make sure you override the getPreferredSize() method when you do custom painting so the layout manager knows what the size of every component should be.
Get those two components working first, then move on to the 3rd component.