I've been working on an "elevator simulator" where I have to animate "elevators". I was able to create different elevator objects, which I did by having each Elevator Object have the parameters of width, height, and coordinates. I then stored them all into an array and used a for loop to draw them into my frame using JPanel's PaintComponent method.
Can you guys suggest a way or give me advice to animate them separately from each other, like say move them up and down independently? I was able to make it move when I only had ONE elevator, but when I tried to apply it to multiple elevators, it did not work.
My previous attempt involved an ActionListener (that listens to a button press to "start" the animation) that simply changes the x and y coordinates of the SINGLE elevator. So How do I go and do that with several elevators (the number of elevators is arbitrary to the user). Do I have to make as many ActionListeners as there are elevators, so it can listen to them independently?
Here's the ActionListener that worked when there's only ONE elevator. It only moves up so far.
private ActionListener timerActionUp = new ActionListener()
{
private int iterator = 0;
private int top = 0;
public void actionPerformed(ActionEvent e)
{
if(top<floorQueue.length){
if(iterator<floorQueue[top]){ //floorQueue is so that the elevator knows where to stop
if(movefloorup<VERTICALFLOORDISTANCE*6){ //this is when the elevator reaches the "top" floor that can fit into the screen, and moves to the next column representing the floors further up
repaint();
movefloorup = movefloorup + VERTICALFLOORDISTANCE;
System.out.println(movefloorup);
iterator++;
}
else{
//timer.stop();
repaint();
switchmarker = 1; //makes elevator moves to the next column
movefloorup = 0;
iterator++;
}
}
else
{
System.out.println("Picking up passengers...");
elevatorCapacity = elevatorCapacity + peopleQueue[floorQueue[top]];
System.out.println("Passengers in elevator: "+elevatorCapacity);
peopleQueue[floorQueue[top]] = 0; //Updates the number of people in the elevator
top++;
if(elevatorCapacity >= 5)
{
System.out.println("WARNING! ELEVATOR FULL!");
elevfull = 1;
}
//timer.stop();
}
}
else
{
System.out.println("Done picking up passengers.");
timer.stop();
}
}
};
Thank you very much!
"Do I have to make as many ActionListeners as there are elevators, so it can listen to them independently?"
No, that would require multiple Timers. Avoid doing this whenever you can.
"Can you guys suggest a way or give me advice to animate them separately from each other, like say move them up and down independently?"
What you should do is try and implement the business logic in methods within your Elevator class and just call the those methods while looping through all the Elevators in your array.
Two make the Elevators to appear to move independently, you can have flags, say in your move method, like
public void move() {
if (move) {
// do something
}
}
What ever is your reason for making the elevator move, that will be the reason the raise the flag. And vice versa. Maybe something like if (onFloor) { elevator.move = false }, maybe for a duration of 20 timer "iterations", and keep a count in the elevator class, that will reset back to 0 when the count hits 20, then move will be back at true.
Here's an example you can play with. I was working on it for about 20 minutes then gave up. The logic is a bit off, but it basically points out the ideas i mentioned. Maybe you'll have better luck with it. You can also see a good working example of moving different object here
import java.awt.Dimension;
import java.awt.Graphics;
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.SwingUtilities;
import javax.swing.Timer;
public class ElevatorAnimate extends JPanel {
private static final int D_W = 300;
private static final int FLOORS = 6;
private static final int FLOOR_HEIGHT = 100;
private static final int BUILDING_HEIGHT = FLOORS * FLOOR_HEIGHT;
private static final int D_H = BUILDING_HEIGHT;
private static final int BUILDING_BASE = BUILDING_HEIGHT;
private static final int ELEVATOR_HEIGHT = 60;
private static final int ELEVATOR_WIDTH = 30;
private final List<Elevator> elevators;
public ElevatorAnimate() {
elevators = createElevators();
Timer timer = new Timer(50, new ActionListener() {
public void actionPerformed(ActionEvent e) {
for (Elevator el : elevators) {
el.move();
}
repaint();
}
});
timer.start();
}
private List<Elevator> createElevators() {
List<Elevator> list = new ArrayList<>();
list.add(new Elevator(6, 30));
list.add(new Elevator(4, 90));
list.add(new Elevator(2, 150));
return list;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
drawFloors(g);
for (Elevator el : elevators) {
el.drawElevator(g);
}
}
private void drawFloors(Graphics g) {
for (int i = 1; i <= FLOORS; i++) {
g.drawLine(0, FLOOR_HEIGHT * i, D_W, FLOOR_HEIGHT * i);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(D_W, D_H);
}
public class Elevator {
int floor, x, y;
boolean move = false;
boolean up = true;
int stopCount = 0;
public Elevator(int floor, int x) {
this.floor = floor;
y = BUILDING_HEIGHT - (floor * FLOOR_HEIGHT);
this.x = x;
}
public void drawElevator(Graphics g) {
g.fillRect(x, y, ELEVATOR_WIDTH, ELEVATOR_HEIGHT);
}
public void move() {
if (y <= 0) {
up = false;
} else if (y >= BUILDING_BASE + ELEVATOR_HEIGHT) {
up = true;
}
if (isOnFloor()) {
move = false;
}
if (move) {
if (up) {
y -= 2;
} else {
y += 2;
}
} else {
if (stopCount >= 20) {
move = true;
stopCount = 0;
} else {
stopCount++;
}
}
}
private boolean isOnFloor() {
return y / FLOOR_HEIGHT == 100;
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.add(new ElevatorAnimate());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
Starting from this related Subway simulation, the following variation adds two independent panels, each of which contains its own view and control panel.
// Common initialization for either JApplet or JFrame
private static void initContainer(Container container) {
container.add(createPanel(), BorderLayout.NORTH);
container.add(createPanel(), BorderLayout.SOUTH);
}
private static JPanel createPanel() {
JPanel panel = new JPanel(new BorderLayout());
ButtonPanel control = new ButtonPanel();
SubwayPanel subway = new SubwayPanel(control);
panel.add(subway, BorderLayout.NORTH);
panel.add(control, BorderLayout.SOUTH);
subway.beginOperation();
return panel;
}
Related
I am in the middle of doing a project and I need some help with make the circles that I am drawing to respond to my timer. I want to make them go slower and faster using my slider that I put in. Attached is the code below. (I know that my code is going to be a jumbled mess, I am using some source code that our professor provide to get the slider on my screen. I am just having a hard time getting the slider/timer talk to my circles.)
import java.util.ArrayList;
import java.util.Random;
import java.awt.*;
import java.awt.event.*;
import javax.swing. *;
import javax.swing.event.*;
import SpeedControlPanel.SlideListener;
public class DotsPanel extends JPanel
{
private final int SIZE = 6; // radius of each dot
private static final long serialVersionUID = 1L;
private ArrayList<Point> pointList;
private Timer timer;
private JLabel lLabel;
private JSlider sSlider;
private JPanel pPanel;
private Circle bouncingBall;
int sSliderHt;
private int moveX, moveY;
AnimationListener animationList;
//-----------------------------------------------------------------
// Constructor: Sets up this panel to listen for mouse events.
//-----------------------------------------------------------------
public DotsPanel()
{
timer = new Timer(30, new AnimationListener());
this.setLayout(new BorderLayout());
bouncingBall = new Circle(SIZE);
Random rand = new Random();
pointList = new ArrayList<Point>();
addMouseListener (new DotsListener());
addMouseMotionListener( new DotsListener());
setBackground(Color.black);
setPreferredSize(new Dimension(500, 300));
lLabel= new JLabel("Timer Delay");
lLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
sSlider = new JSlider(JSlider.HORIZONTAL, 0, 200, 30);
sSlider.setMajorTickSpacing(40);
sSlider.setMinorTickSpacing(10);
sSlider.setPaintTicks(true);
sSlider.setPaintLabels(true);
sSlider.setAlignmentX(Component.LEFT_ALIGNMENT);
sSlider.addChangeListener(new SlideListener());
pPanel= new JPanel();
pPanel.add(lLabel);
pPanel.add(sSlider);
add(pPanel, BorderLayout.SOUTH);
animationList = new AnimationListener();
animationList.timer.start();
/*int[] xArray = new int[1000];
int[] yArray = new int[1000];
for(int i = 0; i < xArray.length; i++)
{
xArray[i] = rand.nextInt(10) + 1;
yArray[i] = rand.nextInt(10) + 1;
}*/
}
//-----------------------------------------------------------------
// Draws all of the dots stored in the list.
//-----------------------------------------------------------------
public void paintComponent(Graphics page)
{
Random rand = new Random();
int R = rand.nextInt(256);
int G = rand.nextInt(256);
int B = rand.nextInt(256);
super.paintComponent(page);
page.setColor(new Color(R%255, (G*3)%255, (B+128)%255));
for (Point spot : pointList)
//page.fillOval(spot.x-SIZE, spot.y-SIZE, SIZE*2, SIZE*2);
page.fillOval(spot.x, spot.y, SIZE*2, SIZE*2);
page.drawString("Count: " + pointList.size(), 5, 15);
}
private class AnimationListener implements ActionListener {
int xRand[];
int yRand[];
Random rand;
public AnimationListener() {
rand = new Random();
xRand = new int[1000];
yRand = new int[1000];
//Filling random values between 0 to 10
for (int i = 0; i < 1000; i++) {
xRand[i] = rand.nextInt(10) + 1;
yRand[i] = rand.nextInt(10) + 1;
}
}
private Timer timer = new Timer(50, this);
#Override
public void actionPerformed(ActionEvent e) {
//Put here for the bounce off of the wall
Rectangle window = getBounds();
for (int i = 0; i < pointList.size(); i++) {
Point spot = pointList.get(i);
spot.x += xRand[i];
spot.y += yRand[i];
if (spot.x <= 0) {
xRand[i] = Math.abs(xRand[i]);
} else if (spot.x >= window.width - (SIZE*2)) {
xRand[i] = -Math.abs(xRand[i]);
}
if (spot.y <= 0) {
yRand[i] = Math.abs(yRand[i]);
} else if (spot.y >= window.height - (SIZE*2)) {
yRand[i] = -Math.abs(yRand[i]);
}
}
bouncingBall.move(moveX, moveY);
// change direction if ball hits a side
int x = bouncingBall.getX();
int y = bouncingBall.getY();
if (x < 0 || x >= WIDTH - SIZE)
{
moveX = moveX * -1;
}
if (y <= 0 || y >= HEIGHT - SIZE)
{
moveY = moveY * -1;
}
sSliderHt =sSlider.getSize().height;
repaint();
repaint();
}
}
//*****************************************************************
// Represents the listener for mouse events.
//*****************************************************************
private class DotsListener implements MouseListener, MouseMotionListener
{
//--------------------------------------------------------------
// Adds the current point to the list of points and redraws
// the panel whenever the mouse button is pressed.
//--------------------------------------------------------------
public void mousePressed(MouseEvent event)
{
pointList.add(event.getPoint());
repaint();
}
public void mouseDragged(MouseEvent event) {
pointList.add(event.getPoint());
repaint();
}
//--------------------------------------------------------------
// Provide empty definitions for unused event methods.
//--------------------------------------------------------------
public void mouseClicked(MouseEvent event) {}
public void mouseReleased(MouseEvent event) {}
public void mouseEntered(MouseEvent event) {}
public void mouseExited(MouseEvent event) {}
}
private class SlideListener implements ChangeListener
{
// ------------------------------------------------
// Called when the state of the slider has changed;
// resets the delay on the timer.
// ------------------------------------------------
public void stateChanged (ChangeEvent event)
{
//int sSliderHt =sSlider.getSize().height;
timer.setDelay(sSlider.getValue());
}
}
}
I want to make them go slower and faster using my slider that I put in.
So it looks to me like you already have logic to make each circle move a random distance each time the Timer fires.
So, just use the slider to change the interval at which the timer fires. Then each time the Timer fires you set the new location of the circles based on your random distance.
Other issues:
The paintComponent(...) method is for painting only. It should NOT set properties of the class. For example you should not be generating random values in the method. You can't control when the paintComponent() is invoked and you don't want to randomly change the properties of the objects you are painting.
Don't use an Array to hold random values. First of all you don't know how many object will be added so you don't want to set a size limit. Instead use an ArrayList.
Create a custom object to contain all the properties you want to control. So you would have the size/location/color etc for each circle you want to paint in a custom object.
See: get width and height of JPanel outside of the class for an example using the above suggestions.
stackoverflow community!
I am making a small GUI based game in JAVA.
I am having a few troubles right now.
To give you basic understanding of my program,
I have a window that shows menu(JPanel) first. When I click on "start game" button. It proceeds to another JPanel on which I can play game.
![menu_window][1]
I have a timer working on a small bullet that is moving periodically. Every time the timer works, the Panel repaints the bullet. I can see the repaint method works but it doesn't remove the previous trace.
![enter image description here][2]
This is my main class
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Main {
private final int window_x_size = 500;
private final int window_y_size = 500;
private JFrame frame;
private JPanel Menu;
private Game myGame = new Game();//extends from JPanel
private JButton startButton = new JButton("Game Start");
private JButton exitButton = new JButton("Game Exit");
private JButton showRank = new JButton("Rank");
private JButton OneOnOne = new JButton("One on One");
private ActionListener MyButtonListener = new MyButtonListener();
public class MyButtonListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
if (source == startButton) {
frame.remove(Menu);
frame.setContentPane(myGame);
frame.validate();
frame.repaint(); // prefer to write this always.
} else if (source == exitButton) {
System.exit(1);
} else if (source == showRank) {
} else {// one on one
}
}
}
public Main() {
frame = new JFrame();
frame.setBackground(Color.white);
frame.setSize(window_x_size, window_y_size);
frame.setTitle("My First GUI Game");
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
Menu = new JPanel();
startButton.addActionListener(MyButtonListener);
exitButton.addActionListener(MyButtonListener);
showRank.addActionListener(MyButtonListener);
OneOnOne.addActionListener(MyButtonListener);
Menu.setLayout(new GridLayout(4, 1));
Menu.add(startButton);
Menu.add(OneOnOne);
Menu.add(showRank);
Menu.add(exitButton);
frame.setContentPane(Menu);
frame.setVisible(true);
}
public static void main(String[] args) {
/*
* This is the most important part of your GUI app, never forget
* to schedule a job for your event dispatcher thread :
* by calling the function, method or constructor, responsible
* for creating and displaying your GUI.
*/
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
new Main();
}
});
}
}
This is my Game class
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Game extends JPanel {
private Random rnd = new Random();
private ActionListener MyBulletListener = new BulletListener();
public KeyListener myKeyListen = new MyKeyListener();
Timer bullet_timer;
private boolean IsExplosion = false;
private ImageIcon spacecraftImg = new ImageIcon("spacecraft.png");
private Rectangle spacecraftBox = new Rectangle(5, 10,
spacecraftImg.getIconWidth(), spacecraftImg.getIconHeight());
private ImageIcon explosionImg = new ImageIcon("explosion.png");
private ImageIcon bulletImg = new ImageIcon("bullet.png");
private Rectangle bulletBox = new Rectangle(70, 200,
bulletImg.getIconWidth(), bulletImg.getIconHeight());
private MySound explosion_sound = new MySound();
public Game() {
addKeyListener(myKeyListen);
bullet_timer = new Timer(500, MyBulletListener);
bullet_timer.start();
}
public class MyKeyListener implements KeyListener {
private int x;
private int y;
#Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
int keyCode = e.getKeyCode();
if (keyCode == 37) {
x = -10;
y = 0;
} else if (keyCode == 38) {
x = 0;
y = -10;
} else if (keyCode == 39) {
x = 10;
y = 0;
} else {
x = 0;
y = 10;
}
if (spacecraftBox.x + x < 0
|| spacecraftBox.x + x > 500
|| spacecraftBox.y + y < 0
|| spacecraftBox.y + y > 500) {
} else {
move_plane(x, y);
repaint();
}
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
}
public class BulletListener implements ActionListener {
private int x;
private int y;
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
Object source = e.getSource();
if (source == bullet_timer) {
int dir = rnd.nextInt(4);
if (dir == 0) {// move left
x = -10;
y = 0;
} else if (dir == 1) {// move right
x = 10;
y = 0;
} else if (dir == 2) {// move up
x = 0;
y = -10;
} else {// move down
x = 0;
y = 10;
}
if (bulletBox.x + x < 0
|| bulletBox.x + x > 500
|| bulletBox.y + y < 0
|| bulletBox.y + y > 500) {
} else {
move_bullets(x, y);
repaint();
}
}
}
}
#Override
public void paint(Graphics g) {
g.drawImage(bulletImg.getImage(), (int) bulletBox.getX(),
(int) bulletBox.getY(), null);
g.drawImage(spacecraftImg.getImage(), (int) spacecraftBox.getX(),
(int) spacecraftBox.getY(), null);
if (IsExplosion) {
g.drawImage(explosionImg.getImage(), (int) bulletBox.getX(),
(int) bulletBox.getY(), null);
MySound.play();
IsExplosion = false;
}
}
public void move_plane(int x, int y) {
spacecraftBox.translate(x, y);
if (spacecraftBox.intersects(bulletBox)) {
IsExplosion = true;
}
}
public void move_bullets(int x, int y) {
bulletBox.translate(x, y);
if (spacecraftBox.intersects(bulletBox)) {
IsExplosion = true;
}
}
}
Plus I added KeyListener but it doesn't work. I have no idea how to fix it out.
I tried googling about the issue. I tried the game panel focusable in main class but it didn't work.
I would really appreciate your help.
Best Regards,
Dongseop
This is almost always due to your painting method not calling the super's painting method to clean up dirty pixels. Since you're overriding paint, you would need to call super.paint(g); as the first method call in your paint override.
Other issues:
Next I'm going to suggest that you not override paint but rather paintComponent(Graphics g), as this will give your animation automatic double buffering and make the animation smoother. Again, call super.paintComponent(g); in your method override.
The easiest way to swap views is to use a CardLayout.
Better to use KeyBindings rather than a KeyListener as this will help you get around the KeyListener's focus issue, the issue which is probably preventing your KeyListener from working. It also allows you to use reusable Actions. Please search this site on KeyListeners to see what I mean and to see examples of Key Bindings (some by me). Also Google Java Key Bindings Tutorial to see the official tutorial on how to use these.
I was working on a simple "Bouncing Ball"-Animation in Java. The idea is that it initally spawns a single ball moving in a straight line until hitting the panel border, which causes it to bounce off as you would expect. You can then spawn additional balls at position x,y with mouseclicks. So far so good.
My problem is that each ball starts its own thread, and each thread individually draws into the panel at their own intervals, causing the panel to flicker like crazy. I know that such problems can be solved by implementing double buffering, which I've read about, but never quite used myself.
I was wondering about how one would go about using double buffering here and if having many threads painting at the same time can be an issue (or conversely, even the norm)?
Thanks a lot in advance!
Here's the code:
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
class MyCanvas extends JPanel
{
MyCanvas()
{
setBackground(Color.white);
setForeground(Color.black);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
}
public Dimension getMinimumSize()
{
return new Dimension(300,300);
}
public Dimension getPreferredSize()
{
return getMinimumSize();
}
}
public class BouncingBalls extends JFrame // main class
{
MyCanvas m_gamefield;
public BouncingBalls()
{
setLayout(new BorderLayout());
m_gamefield = new MyCanvas();
add("Center",m_gamefield);
m_gamefield.addMouseListener(new MeinMausAdapter());
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
public void letsgo()
{
Ball first = new Ball(m_gamefield,200,50);
first.start();
}
class MeinMausAdapter extends MouseAdapter
{
public void mousePressed(MouseEvent e)
{
Ball next = new Ball(m_gamefield,e.getX(),e.getY());
next.start();
}
}
public static void main(String[] args)
{
BouncingBalls test = new BouncingBalls();
test.setVisible(true);
test.pack();
test.letsgo();
}
}
class Ball extends Thread
{
JPanel m_display;
int m_xPos,m_yPos;
int m_dx = 2; // Steps into direction x or y
int m_dy = 2;
Ball(JPanel c,int x,int y)
{
m_display = c;
m_xPos = x;
m_yPos = y;
}
public void run()
{
paintBall(); // Paint at starting position
while(isInterrupted() == false)
{
moveBall();
try
{
sleep(20);
}
catch(InterruptedException e)
{
return;
}
}
}
void paintBall()
{
Graphics g = m_display.getGraphics();
g.fillOval(m_xPos, m_yPos, 20, 20);
g.dispose();
}
void moveBall()
{
int xNew, yNew;
Dimension m;
Graphics g;
g = m_display.getGraphics();
m = m_display.getSize();
xNew = m_xPos + m_dx;
yNew = m_yPos + m_dy;
// Collision detection with borders, "bouncing off":
if(xNew < 0)
{
xNew = 0;
m_dx = -m_dx;
}
if(xNew + 20 >= m.width)
{
xNew = m.width - 20;
m_dx = -m_dx;
}
if(yNew < 0)
{
yNew = 0;
m_dy = -m_dy;
}
if(yNew + 20 >= m.height)
{
yNew = m.height - 20;
m_dy = -m_dy;
}
g.setColor(m_display.getBackground()); // Erases last position by
g.fillRect(m_xPos-2, m_yPos-2, m_xPos+22, m_yPos+22); // painting over it in white
m_xPos = xNew;
m_yPos = yNew;
paintBall(); // paint new position of Ball
g.dispose();
}
}
Don't worry about double buffering when painting with Swing JComponents. They're double buffered by default.
You should, instead of creating each Ball on a different Thread, implement a Swing Timer for the animation. See more at How to Use Swing timers. You can see a good example here where Ball objects are added to a List of Balls and presents at different intervals.
Other Notes
Never use getGraphics of your components. All painting should be done within the Graphics context passed to the paintComponent method. I see you have the method in place. Use it. You can have a draw method in your Ball class that take a Graphics argument, and call that method from within the paintComponent method, passing to it the Graphics context. Example can also be seen in the link above.
You can see more examples here and here and here and here and here and here.
Thanks to peeskillet's excellent references, I've changed the code around a bit by using Swing timers. It's a lot shorter now and forfeits the use of multithreading completely. Also, due to calculating all of the ball positions before actually drawing them (in a single sweeping repaint() as opposed to many smaller ones), the flickering has stopped.
I'm still a bit curious why it is considered bad form to use getGraphics(), though. Does it always lead to flickering (which I had imagined could be removed with an additional layer of of double buffering)? And doesn't paintComponent() become rather bloated in more complex animations if it directs every single act of painting? I'm still fairly new to this, if anybody is wondering.
Here's the new code for those interested:
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
public class BouncingBalls extends JFrame // main class
{
MyCanvas m_gamefield;
public ArrayList<Ball> balls;
public Timer timer = null;
public BouncingBalls()
{
setLayout(new BorderLayout());
m_gamefield = new MyCanvas();
add("Center",m_gamefield);
balls = new ArrayList<Ball>();
timer = new Timer(30, new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
for (Ball b : balls)
{
b.move();
}
repaint();
}
});
m_gamefield.addMouseListener(new MeinMausAdapter());
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
class MeinMausAdapter extends MouseAdapter
{
public void mousePressed(MouseEvent e)
{
balls.add(new Ball(m_gamefield,e.getX(),e.getY()));
}
}
class MyCanvas extends JPanel
{
MyCanvas()
{
setBackground(Color.white);
setForeground(Color.black);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
for (Ball b : balls)
{
b.draw(g);
}
}
public Dimension getMinimumSize()
{
return new Dimension(300,300);
}
public Dimension getPreferredSize()
{
return getMinimumSize();
}
}
public void letsgo()
{
balls.add(new Ball(m_gamefield,200,50));
timer.start();
}
public static void main(String[] args)
{
BouncingBalls test = new BouncingBalls();
test.setVisible(true);
test.pack();
test.letsgo();
}
}
class Ball
{
JPanel m_display;
int m_xPos,m_yPos;
int m_dx = 2; // Steps into direction x or y
int m_dy = 2;
Ball(JPanel c,int x,int y)
{
m_display = c;
m_xPos = x;
m_yPos = y;
}
void draw(Graphics g)
{
g.fillOval(m_xPos, m_yPos, 20, 20);
}
void move()
{
int xNeu, yNeu;
Dimension m;
m = m_display.getSize();
xNeu = m_xPos + m_dx;
yNeu = m_yPos + m_dy;
// Collision detection with borders, "bouncing off":
if(xNeu < 0)
{
xNeu = 0;
m_dx = -m_dx;
}
if(xNeu + 20 >= m.width)
{
xNeu = m.width - 20;
m_dx = -m_dx;
}
if(yNeu < 0)
{
yNeu = 0;
m_dy = -m_dy;
}
if(yNeu + 20 >= m.height)
{
yNeu = m.height - 20;
m_dy = -m_dy;
}
m_xPos = xNeu;
m_yPos = yNeu;
}
}
New question was asked after this one, found here.
I'm new to Java, but I am working on a recreation of "Flappy Bird" to learn more about java and the way that graphics are displayed. Any solutions or suggestions to any of my questions is greatly appreciated. Thanks!
Right now, my program makes a random pipe and scrolls it, but I don't need it to keep scrolling when x1-3 = -83 (this is when the pipe will be off of the screen completely and is no longer needed).
Questions
How can I make my Game.class scroll more than one instance of Pipes.class while adding a preset distance between them? I could find out the distance to put between them, but as far as displaying more than one, I'm not sure how to do that. At most, 3 pipes have to be displayed at the same time.
How can I display a panel for the main menu, and then switch to the pipes panel after a start button is pressed?
Classes
Game.java
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Game {
Pipes panel = new Pipes();
public Game() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(panel);
f.setTitle("Pipe Game");
f.setResizable(false);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
Timer timer = new Timer(10, new ActionListener() { //pipe speed
#Override
public void actionPerformed(ActionEvent e) {
panel.move();
}
});
timer.start();
Timer refresh = new Timer(30, new ActionListener() { //refresh rate
#Override
public void actionPerformed(ActionEvent e) {
panel.repaint();
}
});
refresh.start();
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Game();
}
});
}
}
Pipes.java
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JPanel;
public class Pipes extends JPanel {
//Declare and initialiaze variables
int x1 = 754; //xVal start
int x2 = 75; //pipe width
//total width is 83
int y1 = -1; //yVal start
int y2 = setHeightVal(); //pipe height
int gap = 130; //gap height
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.clearRect(0,0,750,500); //Clear screen
g.drawRect(x1,y1,x2,y2); //Draw part 1
g.drawRect(x1-3,y2-1,x2+6,25); //Draw part 2
g.drawRect(x1-3,y2+25+gap,x2+6,25); //Draw part 3
g.drawRect(x1,y2+25+gap+25,x2,500-y2-49-gap); //Draw part 4
}
public void move() {
x1--;
}
public int getMyX() { //To determine where the pipe is horizontally
return x1-3;
}
public int getMyY() { //To determine where the pipe is vertically
return y2+25;
}
public int setHeightVal() { //Get a random number and select a preset height
int num = (int)(9*Math.random() + 1);
int val = 0;
if (num == 9)
{
val = 295;
}
else if (num == 8)
{
val = 246;
}
else if (num == 7)
{
val = 216;
}
else if (num == 6)
{
val = 185;
}
else if (num == 5)
{
val = 156;
}
else if (num == 4)
{
val = 125;
}
else if (num == 3)
{
val = 96;
}
else if (num == 2)
{
val = 66;
}
else
{
val = 25;
}
return val;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(751, 501);
}
}
"How can I make my Game.class scroll more than one instance of Pipes.class while adding a preset distance between them? "
Here's some simple logic. You want to use a data structure to hold you pipes. What this data structure will hold is whatever data is required to paint then, like x, y, coordinates. For this task, I prefer just to create a new class with it's own draw method, that I pass the paintComponent's Graphics context to. For example
public class Pipe {
int x;
int y;
public class Pipe(int x, int y) {
this.x = x;
this.y = y;
}
public void drawPipe(Graphics g) {
g.fillRect(x, y, 50, 100);
}
}
Now this is just an example class. The above only draws a rectangle, but this is just to show you what you should be doing.
So next you want to have the data structure to hold three Pipe objects, like an array. I prefer to use a List. You'll want that List in your Pipes class, and add three Pipe object to it. You can specify the x to be anything you like, to keep them the same distance apart
public class Pipes extends JPanel {
List<Pipe> pipes = new ArrayList<Pipe>();
public Pipes() {
pipes.add(new Pipe(50, 100));
pipes.add(new Pipe(150, 100));
pipes.add(new Pipe(250, 100));
}
}
Now in the paintComponent method, all you need to do is loop through them and use its drawPipe method
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for ( Pipe pipe : pipes ){
pipe.drawPipe(g);
}
}
Now you move them all you need to do is move there x positions in the timer, and call repaint. You may also want to check against the x to make sure it doesn't do off the screen, or if you moving them the right, you could put them the the very left then whey go off the screen, like a conveyor belt. So you could do something like this
private static final int X_INC = 5;
...
Timer timer = new Timer(40, new ActionListener(){
public void actionPerformed(ActionEvent e) {
for (Pipe pipe : pipes ){
if (pipe.x >= screenWidth) {
pipe.x = 0;
} else {
pipe.x += X_INC;
}
}
repaint();
}
});
As you can see, what I do is loop through the List and just change all their x coordinates, then repaint(). So you can create your own Pipe class with whatever values you need to paint, and just move them around in the loop.
For the changing of speed, instead of using a hard coded vakue like 10 for the timer, use a variable delay, that you can change like with the click of a button
int delay = 100;
JButton speedUp = new JButton("Speed UP");
JButton slowDown = new JButton("Slow Down");
Timer timer = null;
public Pipes() {
timer = new Timer(delay, new ActionListener(){
...
});
timer.start();
speedUp.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
if (!((delay - 20) < 0)) {
delay -=20;
timer.setDelay(delay);
}
}
});
// do the same for slowDown, but decrease the delay
}
Test this out
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.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Mario extends JPanel {
private static final int D_W = 800;
private static final int D_H = 300;
private static final int X_INC = 5;
BufferedImage bg;
BufferedImage pipeImg;
List<Pipe> pipes = new ArrayList<>();
int delay = 50;
Timer timer = null;
public Mario() {
try {
bg = ImageIO.read(new URL("http://farm8.staticflickr.com/7341/12338164043_0f68c73fe4_o.png"));
pipeImg = ImageIO.read(new URL("http://farm3.staticflickr.com/2882/12338452484_7c72da0929_o.png"));
} catch (IOException ex) {
Logger.getLogger(Mario.class.getName()).log(Level.SEVERE, null, ex);
}
pipes.add(new Pipe(100, 150, pipeImg));
pipes.add(new Pipe(400, 150, pipeImg));
pipes.add(new Pipe(700, 150, pipeImg));
timer = new Timer(delay, new ActionListener(){
public void actionPerformed(ActionEvent e) {
for (Pipe pipe : pipes) {
if (pipe.x > D_W) {
pipe.x = 0;
} else {
pipe.x += X_INC;
}
}
repaint();
}
});
timer.start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(bg, 0, 0, getWidth(), getHeight(), this);
for (Pipe pipe : pipes) {
pipe.drawPipe(g);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(D_W, D_H);
}
public class Pipe {
int x;
int y;
Image pipe;
public Pipe(int x, int y, Image pipe) {
this.x = x;
this.y = y;
this.pipe = pipe;
}
public void drawPipe(Graphics g) {
g.drawImage(pipe, x, y, 75, 150, Mario.this);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("Mario Pipes");
frame.add(new Mario());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
I have many planes(threads) that move in window, and I want switch the ImageIcon according to the direction of the plane.
For example: if a plane goes to the right, the imageIcon of the plane is right and then plane goes to the left, exchange the imageIcon for the plane is left.
How can I do that in method paintComponent?
Sorry for my bad english.
If you're talking about swapping the ImageIcon displayed by a JLabel, then you should not switch ImageIcons in paintComponent but rather should do this in the non-paintComponent region of code, perhaps in a Swing Timer. Even if you're not talking about a JLabel, the paintComponent method should not be used for changing the state of an object.
Your question however leaves too much unsaid to allow us to be able to answer it completely and well. Consider telling and showing more.
If you are looking for a logic thingy, then a small example is here, though you might have to modify it for your needs.
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.util.Random;
import javax.swing.*;
public class FlyingAeroplane
{
private Animation animation;
private Timer timer;
private ActionListener timerAction = new ActionListener()
{
#Override
public void actionPerformed(ActionEvent ae)
{
animation.setValues();
}
};
private void displayGUI()
{
JFrame frame = new JFrame("Aeroplane Flying");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
animation = new Animation();
frame.setContentPane(animation);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
timer = new Timer(100, timerAction);
timer.start();
}
public static void main(String... args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
new FlyingAeroplane().displayGUI();
}
});
}
}
class Animation extends JPanel
{
private final int HEIGHT = 150;
private final int WIDTH = 200;
private int x;
private int y;
private ImageIcon image;
private boolean flag;
private Random random;
public Animation()
{
x = 0;
y = 0;
image = new ImageIcon(getClass().getResource("/image/aeroplaneright.jpeg"));
flag = true;
random = new Random();
}
public void setValues()
{
x = getXOfImage();
y = random.nextInt(70);
repaint();
}
private int getXOfImage()
{
if (flag)
{
if ((x + image.getIconWidth()) == WIDTH)
{
flag = false;
x--;
return x;
}
x++;
image = new ImageIcon(getClass().getResource("/image/aeroplaneright.jpeg"));
}
else if (!flag)
{
if (x == 0)
{
flag = true;
x++;
return x;
}
x--;
image = new ImageIcon(getClass().getResource("/image/aeroplaneleft.jpeg"));
}
return x;
}
#Override
public Dimension getPreferredSize()
{
return (new Dimension(WIDTH, HEIGHT));
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(image.getImage(), x, y, this);
}
}
IMAGES USED :
On setting the direction you should set the image icon too, or have a getImageIcon(direction).
In the paintComponent no heavy logic should happen; it should be as fast as possible. You have no (total) control when and how often paintComponent is called.