I'm learning some swing and awt programming in Java so I decided to make Pong. The Main class is the parent JFrame. It instantiates a Ball and a Paddle and adds them (they are JPanels). However, only the last one added is displayed. How do I fix this?
Code:
public class Main extends JFrame {
public Main() {
super("Pong");
add(new Ball());
add(new Paddle());
setSize(500, 500);
setBackground(Color.orange);
setLocationRelativeTo(null);
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public static void main(String[] args) {
new Main().setVisible(true);
}
}
The Ball class:
public class Ball extends JPanel implements ActionListener {
Timer timer;
private Vec2d position;
private Vec2d velocity;
private Dimension ballSize;
public Ball() {
super();
position = new Vec2d(50, 50);
velocity = new Vec2d(2, 3);
timer = new Timer(25, this);
ballSize = new Dimension(40, 40);
timer.start();
}
#Override
public void actionPerformed(ActionEvent ae) {
//Perform game frame logic
bounceBall();
repaint();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillArc((int)position.x, (int)position.y, ballSize.width,
ballSize.height, 0, 360);
position.add(velocity);
}
private void bounceBall() {
if(position.x < 0 || position.x > getWidth() - ballSize.width) {
velocity.x *= -1;
}
if (position.y < 0|| position.y > getHeight() - ballSize.height) {
velocity.y *= -1;
}
}
}
And finally, the Paddle class:
public class Paddle extends JPanel implements ActionListener {
private Vec2d position;
private double yVelocity;
private Rectangle rect;
private Timer timer;
public Paddle() {
super();
position = new Vec2d(30, 250);
yVelocity = 0;
timer = new Timer(25, this);
timer.start();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillRect((int) position.x, (int) position.y, 20, 40);
}
#Override
public void actionPerformed(ActionEvent ae) {
repaint();
}
}
Note that Vec2d is simply a small two dimensional Vector class I threw together. Also, the Pong logic (collision between Paddle and ball, scoring, etc) is not implemented. I just want to get it to draw correctly
Thanks in advance for the help!
The first thing to do is to add the JPanels to the content pane of your window, not to the window itself. I'm surprised you didn't get a runtime warning about that.
Also, it looks like you are planning to have each of your panels fill the screen, but paint only a small section of it. If that is really the way you want to do it, then you need to do setOpaque(false) on them so the panels under them can show through. But probably a better solution would be to have a single JPanel that is the drawing surface, and have its paintComponent() pass off the Graphics to each of the game objects to let them draw themselves.
add(new Ball());
add(new Paddle());
By default, the layout manager of a JFrame is a BorderLayout. And if you don't specify where you want to add a component (BorderLayout.WEST, or EAST, etc.), the it's added at the center. So you add two components at the same place: in the center. So, only one of them is displayed.
You are adding the Ball and Paddle to the same BorderLayout.CENTER position so only the last one added, (i.e. the Paddle) is displayed. You could use a GridLayout here to display:
setLayout(new GridLayout(1, 2));
add(new Paddle());
add(new Ball());
in the Paddle class, you never add the velocity to position with position.add(velocity) like you do in your ball class..
Related
I am working on a simple object drawing program using Swing in Java.
My program simply should draw shapes according to buttons when clicked, and move any shapes with the mouse. I have four buttons which draw rectangle, circle and square on screen. So far I did managed to draw to shapes when you click on buttons. but i want to move the shapes on screen which it did not work out.
The problem is this: When I click on circle shape to drag it around with mouse, it clears all the screen and noting is on the screen.
And, is there a way to clean all the screen when I click on clear button?
Thank you?
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class PaintProject extends JComponent implements ActionListener,
MouseMotionListener {
private int CircleX=0;
private int CircleY=0;
private int RectX=100;
private int RectY=100;
private int SquareX=300;
private int SquareY=200;
public static void main(String[] args) {
JFrame frame = new JFrame("NEW PAINT PROGRAME!");
JButton CircleButton = new JButton("Circle");
CircleButton.setActionCommand("Circle");
JButton RectangleButton = new JButton("Rectangle");
RectangleButton.setActionCommand("Rectangle");
JButton SquareButton = new JButton("Square");
SquareButton.setActionCommand("Square");
PaintProject paint = new PaintProject();
CircleButton.addActionListener(paint);
RectangleButton.addActionListener(paint);
SquareButton.addActionListener(paint);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setLayout(new FlowLayout());
frame.add(paint);
frame.add(CircleButton);
frame.add(RectangleButton);
frame.add(SquareButton);
frame.addMouseMotionListener(paint);
frame.pack();
frame.setVisible(true);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 500);
}
private void drawCircle() {
Graphics g = this.getGraphics();
g.setColor(Color.red);
g.fillOval(CircleX, CircleY, 100, 100);
}
private void drawRectangle() {
Graphics g = this.getGraphics();
g.setColor(Color.green);
g.fillRect(RectX, RectY, 100, 300);
}
private void drawSquare() {
Graphics g = this.getGraphics();
g.setColor(Color.blue);
g.fillRect(SquareX, SquareY, 100, 100);
}
#Override
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
if (command.equals("Circle")) {
drawCircle();
}
else if (command.equals("Rectangle")) {
drawRectangle();
}
else if (command.equals("Square")) {
drawSquare();
}
}
#Override
public void mouseDragged(MouseEvent e) {
CircleX=e.getX();
CircleY=e.getY();
repaint();
}
}
As noted here and here, getGraphics() is not how to perform custom painting in Swing. Instead, override paintComponent() to render the desired content. It looks like you want to drag shapes using the mouse. A basic approach to moving a selected object is shown here; substitute your fillXxx() invocation for the drawString() shown there. For multiple shapes, use a List<Shape>; the clear command then becomes simply List::clear. A complete example is cited here; it features moveable, selectable, resizable, colored nodes connected by edges.
I'm making a GUI application which finds and draws convex hull for a set of points.
Here's the frame:
public class Frame extends JFrame{
Panel panel = new Panel();
JButton drawHull = new JButton("Draw Convex Hull");
Frame(String title) {
super(title);
// setLayout
setLayout(new BorderLayout());
// add components to the frame
add(panel, BorderLayout.CENTER);
add(drawHull, BorderLayout.SOUTH);
// add actionListener for drawHull button
drawHull.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
panel.drawConvexHull();
}
}
);
}
}
The frame contains a button (at the bottom of the frame) and a Panel object.
Here's the Panel class:
public class Panel extends JPanel{
ArrayList<Point> points = new ArrayList<Point>();
public Panel() {
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
points.add(new Point(e.getX(), e.getY()));
repaint();
}
});
}
#Override
protected void paintComponent(Graphics g) {
if(!points.isEmpty()) {
Point p = points.get(points.size()-1);
g.fillOval(p.x-2, p.y-2, 4, 4);
}
}
public void drawConvexHull() {
// code for finding convex hull
}
}
I have added a mouseListener to the Panel class so that when the user clicks anywhere on the panel, a point is drawn.
When I run the code, everything looks fine. Here's a sample run.
Now here's the problem:
As soon as I click the panel, this happens
A button appears on the top of the frame. Why is this happening?
One more thing. When I click this button on the top, a point is drawn. Here, have a look:
Without calling super.paintComponent(g) in paintComponent the background is not repainted leading to unpredictable results on the panel component.
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Point point: points) {
g.fillOval(point.x - 2, point.y - 2, 4, 4);
}
}
Am new to Java GUI Games, therefore, question might be trivial. Am trying to make a foosball game of sort.
I have been trying to add Ball JComp on top of my FoosballTable Jcomp.
There are two table settings.
What it does is, adds Ball Component to bottom of the Table.
I change the table setting and ball is visible. Otherwise not.
How do I add ball to the top? I have been trying to search online to no avail.
Thank you.
Edit 1
The ball class has overridden paintComponent method. and Frame class has various panels. one panel is the Start button i.e supposed to display the ball
....
jp7.add(Start);
final Ball b = new Ball();
Start.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
JPanel contentball = (JPanel) getContentPane();
contentball.add(b);
contentball.revalidate();
contentball.repaint();
}
});
playersOnString.addItemListener(new ItemListener() {
#Override
public void itemStateChanged(ItemEvent e) {
JPanel contentgr = (JPanel) getContentPane();
if(e.getItem() == "[1] [2] [4] [4]"){
System.out.println("First choice");
remove(ground2);
contentgr.add(ground1);
}
else if(e.getItem()== "[1] [2] [5] [3]"){
System.out.println("Second choice");
remove(ground1);
contentgr.add(ground2);
}
contentgr.repaint();
setVisible(true);
}
});
public class Ball extends JComponent{
/**
*
*/
private static final long serialVersionUID = 1L;
private static Ball ball;
int xAxisSpeed , yAxisSpeed;
Team lastContactTeam;
private int size = 15;
public Dimension getPreferredSize()
{
return (new Dimension(100, 500));
}
public static Ball getInstance(){
Random r = new Random();
if(ball == null){
ball = new Ball();
ball.xAxisSpeed = r.nextInt(10)+1;
ball.yAxisSpeed = r.nextInt(10)+0;
}
return ball;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillOval(190, 355, size, size);
setVisible(true);
}
}
Edit 2
I wish to add ball to the top of the Table that already exists. (a rectangle drawn with PaintComponent method on a panel)
I'm trying to make a pong game in Java but it doesn't work.
I've done some testing and it seems that the variables are updating but that when I do
repaint(); in the timers actionPerformed(ActionEvent e) doesn't call the paintComponent() method
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class PongGame extends JComponent implements ActionListener, MouseMotionListener{
public int state = 1;
public int paddleX;
public String buttonColor = "blue";
public int mouseX, mouseY;
private int ballX = 400;
private int ballY = 150;
public static void main(String[] args){
JFrame window = new JFrame("Pong");
PongGame game = new PongGame();
window.add(new PongGame());
window.pack();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setLocationRelativeTo(null);
window.setResizable(false);
window.setVisible(true);
Timer t = new Timer(20, game);
t.start();
}
public Dimension getPreferredSize(){
return new Dimension(800, 600);
}
public void paintComponent(Graphics g){
paddleX = mouseX;
g.setColor(Color.WHITE);
g.fillRect(0,0, 800, 600);
g.setColor(Color.BLACK);
g.fillRect(paddleX, 550, 150, 15);
g.fillOval(ballX, ballY, 30, 30);
}
#Override
public void actionPerformed(ActionEvent e) {
ballX = ballX + 10;
ballY = ballY + 10;
System.out.println(ballX + " " + ballY);
}
#Override
public void mouseDragged(MouseEvent e) {
}
#Override
public void mouseMoved(MouseEvent e) {
mouseX = e.getX();
repaint();
}
}
you haven't registered the implemented MouseMotionListener to any component:
game.addMouseMotionListener(game);
you are not adding your first created instance of PongGame to the frame rather added a new one producing bug:
PongGame game = new PongGame();
window.add(new PongGame()); // <<--- why creating the new instance ?
// it should be window.add(game);
As a good programming practice: try putting the add listener code in the component's own creating context i.e., in their constructor to make your code better readable.
The problem is:
PongGame game = new PongGame();
window.add(new PongGame());
You have two instances of PongGame. One added to the frame (new PongGame()) and the other (game) that actually reacts to the timer. Change this row to:
window.add(game);
To correct the actual problem. Add a constructor (tested locally):
PongGame() {
addMouseMotionListener(this);
}
repaint() does not invoke paint() directly. It schedules a call to an intermediate method, update(). Finally, update() calls paint() (unless you override update).
The reason for this complexity is Java's support for concurrent programming. It does this using threads.
Using repaint() can be tricky for at least three reasons.
the interaction between repaint() and the spontaneous painting done by the GUI thread
the fact that repaint() just asks the thread system to schedule a call to update()/paint() and then exits. The repaint() method is asynchronous.
the problem of preventing your drawing from being erased when being updated.
I suggest you to try the same with update().
Useful link : http://www.scs.ryerson.ca/~mes/courses/cps530/programs/threads/Repaint/index.html
I have the folowing custom JPanel and I have aded it to my frame using Netbeans GUI builder but the background won't change! I can see the circle, drawing with g.fillOval(). What's wrong?
public class Board extends JPanel{
private Player player;
public Board(){
setOpaque(false);
setBackground(Color.BLACK);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.red);
g.fillOval(player.getxCenter(), player.getyCenter(), player.getRadius(), player.getRadius());
}
public void updatePlayer(Player player){
this.player=player;
}
}
If your panel is 'not opaque' (transparent) you wont see your background color.
You have to call the super.paintComponent(); as well, to allow the Java API draw the original background. The super refers to the original JPanel code.
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.red);
g.fillOval(player.getxCenter(), player.getyCenter(), player.getRadius(), player.getRadius());
}
You need to create a new Jpanel object in the Board constructor.
for example
public Board(){
JPanel pane = new JPanel();
pane.setBackground(Color.ORANGE);// sets the background to orange
}
setOpaque(false);
CHANGED to
setOpaque(true);
I just tried a bare-bones implementation and it just works:
public class Test {
public static void main(String[] args) {
JFrame frame = new JFrame("Hello");
frame.setPreferredSize(new Dimension(200, 200));
frame.add(new Board());
frame.pack();
frame.setVisible(true);
}
}
public class Board extends JPanel {
private Player player = new Player();
public Board(){
setBackground(Color.BLACK);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.red);
g.fillOval(player.getCenter().x, player.getCenter().y,
player.getRadius(), player.getRadius());
}
}
public class Player {
private Point center = new Point(50, 50);
public Point getCenter() {
return center;
}
private int radius = 10;
public int getRadius() {
return radius;
}
}
In order to completely set the background to a given color :
1) set first the background color
2) call method "Clear(0,0,this.getWidth(),this.getHeight())" (width and height of the component paint area)
I think it is the basic procedure to set the background...
I've had the same problem.
Another usefull hint : if you want to draw BUT NOT in a specific zone (something like a mask or a "hole"), call the setClip() method of the graphics with the "hole" shape (any shape) and then call the Clear() method (background should previously be set to the "hole" color).
You can make more complicated clip zones by calling method clip() (any times you want) AFTER calling method setClip() to have intersections of clipping shapes.
I didn't find any method for unions or inversions of clip zones, only intersections, too bad...
Hope it helps