The problem is that this ball after it is dragged and exited click, it is supposed to repaint according to the new y component given. This is calculated from the gravity final and added to the velocity which is added to the existing y component in a loop.
I have debugged many times and I just cant hit it on the head.
Its supposed to..
move to where you drag it >>> when you let go it is supposed to fall until it hits the ground.
Thank you ahead of time.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class DragBallPanel extends JPanel implements MouseListener, MouseMotionListener
{
private static final int BALL_DIAMETER = 40;
private int screen_size_x = 300;
private int screen_size_y = 300;
private int ground_lvl = screen_size_y - 15;
private int _ballX = ground_lvl/2;
private int _ballY = ground_lvl - BALL_DIAMETER;
private final double GRAVITY = -9.8;
private double velocity;
private static final double TERM_VEL = -100;
private int _dragFromX = 0;
private int _dragFromY = 0;
private boolean _canDrag = false;
public DragBallPanel() throws InterruptedException
{
setPreferredSize(new Dimension(screen_size_x, screen_size_y));
setBackground(Color.darkGray);
setForeground(Color.darkGray);
this.addMouseListener(this);
this.addMouseMotionListener(this);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g); // Required for background.
g.setColor (Color.green);
g.fillRect (0, 280, 400, 50 );
g.setColor (Color.black);
g.fillOval(_ballX, _ballY, BALL_DIAMETER, BALL_DIAMETER);
}
public void mousePressed(MouseEvent e)
{
int x = e.getX();
int y = e.getY();
if (x >= _ballX && x <= (_ballX + BALL_DIAMETER)
&& y >= _ballY && y <= (_ballY + BALL_DIAMETER))\
{
_canDrag = true;
_dragFromX = x - _ballX;
_dragFromY = y - _ballY;
} else
{
_canDrag = false;
}
}
//===== mouseDragged ======
/** Set x,y to mouse position and repaint. */
public void mouseDragged(MouseEvent e)
{
if (_canDrag)
{ // True only if button was pressed inside ball.
//--- Ball pos from mouse and original click displacement
_ballX = e.getX() - _dragFromX;
_ballY = e.getY() - _dragFromY;
//--- Don't move the ball off the screen sides
_ballX = Math.max(_ballX, 0);
_ballX = Math.min(_ballX, getWidth() - BALL_DIAMETER);
//--- Don't move the ball off top or bottom
_ballY = Math.max(_ballY, 0);
_ballY = Math.min(_ballY, getHeight() - BALL_DIAMETER);
this.repaint();
}
}
public void mouseExited(MouseEvent e)
{
while(_ballY < ground_lvl)
{
simulateGravity();
}
}
public void simulateGravity()
{
if(_canDrag)
{
try{
velocity = velocity + GRAVITY;
if (velocity < TERM_VEL)
{
velocity = TERM_VEL;
}
if (_ballY >= ground_lvl - BALL_DIAMETER)
{
velocity = velocity/4;
}
_ballY += velocity;
Thread.sleep(400);
this.repaint();//**problem occurs here**
}catch(InterruptedException ie)
{
}
}
}
public void mouseMoved (MouseEvent e){}
public void mouseEntered (MouseEvent e){}
public void mouseClicked (MouseEvent e){}
public void mouseReleased(MouseEvent e){}
}
main() class
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class DragDemo extends JApplet
{
public static void main(String[] args) throws InterruptedException
{
JFrame window = new JFrame();
window.setTitle("Drag Demo");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//window.add(new DragBallPanel());
window.setContentPane(new DragBallPanel());
window.setResizable(false);
window.pack();
window.show();
}
public DragDemo() throws InterruptedException
{
new DragBallPanel();
}
}
This SSCCE begins to show the problems in the code.
Compile the code.
Run it.
Drag the ball upwards.
Release the ball.
Remove the mouse from the drawing area, to see..
The ball fall upwards!
You seem to have gotten the Y values upside down. They start at top of screen, and go downwards. Also, the code was blocking the EDT in an infinite loop. To solve that, run the animation using a Swing Timer.
Please read the document on the SSCCE & ask if there is anything in it you do not understand. I am well placed to explain. :)
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class DragBallPanel extends JPanel implements MouseListener, MouseMotionListener
{
private static final int BALL_DIAMETER = 40; // Diameter of ball
private int screen_size_x = 300;
private int screen_size_y = 300;
private int ground_lvl = screen_size_y - 15;
private int _ballX = ground_lvl/2;
private int _ballY = ground_lvl - BALL_DIAMETER;
private final double GRAVITY = -9.8;
private double velocity;
private static final double TERM_VEL = 100;
private int _dragFromX = 0; // pressed this far inside ball's
private int _dragFromY = 0; // bounding box.
/** true means mouse was pressed in ball and still in panel.*/
private boolean _canDrag = false;
public DragBallPanel()
{
setPreferredSize(new Dimension(screen_size_x, screen_size_y));
setBackground(Color.darkGray);
setForeground(Color.darkGray);
this.addMouseListener(this);
this.addMouseMotionListener(this);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g); // Required for background.
g.setColor (Color.green);
g.fillRect (0, 280, 400, 50 );
g.setColor (Color.black);
g.fillOval(_ballX, _ballY, BALL_DIAMETER, BALL_DIAMETER);
}
public void mousePressed(MouseEvent e)
{
int x = e.getX(); // Save the x coord of the click
int y = e.getY(); // Save the y coord of the click
if (x >= _ballX && x <= (_ballX + BALL_DIAMETER)
&& y >= _ballY && y <= (_ballY + BALL_DIAMETER)) {
_canDrag = true;
_dragFromX = x - _ballX; // how far from left
_dragFromY = y - _ballY; // how far from top
} else {
_canDrag = false;
}
}
//========= mouseDragged =================
/** Set x,y to mouse position and repaint. */
public void mouseDragged(MouseEvent e)
{
if (_canDrag) { // True only if button was pressed inside ball.
//--- Ball pos from mouse and original click displacement
_ballX = e.getX() - _dragFromX;
_ballY = e.getY() - _dragFromY;
//--- Don't move the ball off the screen sides
_ballX = Math.max(_ballX, 0);
_ballX = Math.min(_ballX, getWidth() - BALL_DIAMETER);
//--- Don't move the ball off top or bottom
_ballY = Math.max(_ballY, 0);
_ballY = Math.min(_ballY, getHeight() - BALL_DIAMETER);
this.repaint(); // Repaint because position changed.
}
}
//====================================================== method mouseExited
/** Turn off dragging if mouse exits panel. */
public void mouseExited(MouseEvent e)
{
System.out.println("Exited: " + e);
//_canDrag = false;
runGravity();
/* while(_ballY < ground_lvl)
{
simulateGravity();
}*/
}
Timer timer;
ActionListener animate;
public void runGravity() {
if (animate==null) {
animate = new ActionListener() {
public void actionPerformed(ActionEvent ae) {
System.out.println("Ground: " + (_ballY-ground_lvl));
if (_ballY > ground_lvl) {
timer.stop();
} else {
simulateGravity();
}
}
};
timer = new Timer(100,animate);
}
timer.start();
}
public void simulateGravity()
{
System.out.println("_canDrag: " + _canDrag);
if(_canDrag)
{
velocity = velocity + GRAVITY;
if (velocity > TERM_VEL)
{
velocity = TERM_VEL;
}
if (_ballY >= ground_lvl - BALL_DIAMETER)
{
//We have hit the "ground", so bounce back up. Reverse
//the speed and divide by 4 to make it slower on bouncing.
//Just change 4 to 2 or something to make it faster.
velocity = velocity/4;
}
_ballY += velocity;
//this.revalidate();
this.repaint();
}
}
public void mouseMoved (MouseEvent e){}
public void mouseEntered (MouseEvent e){}
public void mouseClicked (MouseEvent e){}
public void mouseReleased(MouseEvent e){}
public static void main(String[] args) {
SwingUtilities.invokeLater( new Runnable() {
public void run() {
DragBallPanel dbp = new DragBallPanel();
JOptionPane.showMessageDialog(null, dbp);
}
});
}
}
Try updateUI() instead of repaint().
If there is no effect remove the component and add it again.
Related
I'm using Java to make a breakout game. The independent parts are functioning: the paddle, the ball, the bricks.
However, the ball hits a wall and then instead of changing it's vector, the ball just travels up the x-axis in a straight line at the edge of the JFrame window until it hits the top of the window and bounces back down from this corner.
The ball then gets stuck in an infinite back and forth line from the top left corner until it touches the paddle (back and forth) and will never break any of the other bricks.
How can I change my code to fix this problem?
import java.awt.Graphics;
public class Ball extends Sprite {
private int xVelocity = 1, yVelocity = -1;
// Constructor
public Ball() {
setWidth(Settings.BALL_WIDTH);
setHeight(Settings.BALL_HEIGHT);
resetPosition();
public void resetPosition() {
setX(Settings.INITIAL_BALL_X);
setY(Settings.INITIAL_BALL_Y);
}
public void update() {
x += yVelocity;
y += yVelocity;
// Bounce off left side of screen
if(x <= 0) {
x = 0;
setXVelocity(-1);
}
// Bounce off right side of screen
if(x >= Settings.WINDOW_WIDTH - Settings.BALL_WIDTH) {
x =Settings.WINDOW_WIDTH;
setXVelocity(-1);
}
// Bounce off top of screen
if(y <= 0) {
y = 0;
setYVelocity(1);
}
}
public void setXVelocity(int x) {
xVelocity = x;
}
public void setYVelocity(int y) {
yVelocity = y;
}
public int getXVelocity() {
return xVelocity;
}
public int getYVelocity() {
return yVelocity;
}
public void paint(Graphics g) {
g.fillOval(x, y, Settings.BALL_WIDTH, Settings.BALL_HEIGHT);
}
}
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Paint;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JPanel;
import javax.swing.Timer;
public class BreakoutPanel extends JPanel implements ActionListener, KeyListener {
static final long serialVersionUID = 2L;
private boolean gameRunning = true;
private int livesLeft = 3;
private String screenMessage = "";
private Ball ball;
private Paddle paddle;
private Brick bricks[];
public BreakoutPanel(Breakout game) {
addKeyListener(this);
setFocusable(true);
Timer timer = new Timer(5, this);
timer.start();
ball = new Ball();
paddle = new Paddle();
bricks = new Brick[Settings.TOTAL_BRICKS];
createBricks();
}
private void createBricks() {
int counter = 0;
int x_space = 0;
int y_space = 0;
for(int x = 0; x < 4; x++) {
for(int y = 0; y < 5; y++) {
bricks[counter] = new Brick((x * Settings.BRICK_WIDTH) + Settings.BRICK_HORI_PADDING + x_space, (y * Settings.BRICK_HEIGHT) + Settings.BRICK_VERT_PADDING + y_space);
counter++;
y_space++;
}
x_space++;
y_space = 0;
}
}
private void paintBricks(Graphics g) {
for(int x = 0; x < Settings.TOTAL_BRICKS; x++) {
for(int y = 0; y < Settings.TOTAL_BRICKS; y++) {
bricks[y].paint(g);
}
}
}
private void update() {
if(gameRunning) {
// TODO: Update the ball and paddle
ball.update();
paddle.update();
collisions();
repaint();
}
}
private void gameOver() {
stopGame();
screenMessage = "Game Over!";
}
private void gameWon() {
stopGame();
screenMessage = "Congratulations! You have won!";
}
private void stopGame() {
gameRunning = false;
}
private void collisions() {
// Check for loss
if(ball.y > 450) {
// Game over
livesLeft--;
if(livesLeft <= 0) {
gameOver();
return;
} else {
ball.resetPosition();
ball.setYVelocity(-1);
}
}
// Check for win
boolean bricksLeft = false;
for(int i = 0; i < bricks.length; i++) {
// Check if there are any bricks left
if(!bricks[i].isBroken()) {
// Brick was found, close loop
bricksLeft = true;
break;
}
}
if(!bricksLeft) {
gameWon();
return;
}
// Check collisions
if(ball.getRectangle().intersects(paddle.getRectangle())) {
// Simplified touching of paddle
ball.setYVelocity(-1);
}
for(int i = 0; i < bricks.length; i++) {
if (ball.getRectangle().intersects(bricks[i].getRectangle())) {
int ballLeft = (int) ball.getRectangle().getMinX();
int ballHeight = (int) ball.getRectangle().getHeight();
int ballWidth = (int) ball.getRectangle().getWidth();
int ballTop = (int) ball.getRectangle().getMinY();
Point pointRight = new Point(ballLeft + ballWidth + 1, ballTop);
Point pointLeft = new Point(ballLeft - 1, ballTop);
Point pointTop = new Point(ballLeft, ballTop - 1);
Point pointBottom = new Point(ballLeft, ballTop + ballHeight + 1);
if (!bricks[i].isBroken()) {
if (bricks[i].getRectangle().contains(pointRight)) {
ball.setXVelocity(-1);
} else if (bricks[i].getRectangle().contains(pointLeft)) {
ball.setXVelocity(1);
}
if (bricks[i].getRectangle().contains(pointTop)) {
ball.setYVelocity(1);
} else if (bricks[i].getRectangle().contains(pointBottom)) {
ball.setYVelocity(-1);
}
bricks[i].setBroken(true);
}
}
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
ball.paint(g);
paddle.paint(g);
paintBricks(g);
// Draw lives left
// TODO: Draw lives left in the top left hand corner**
if(livesLeft != 0) {
String displayLives = Integer.toString(livesLeft);
g.setFont(new Font("Arial", Font.BOLD, 18));
g.drawString(displayLives, Settings.LIVES_POSITION_X, Settings.LIVES_POSITION_Y);
}
// Draw screen message*
if(screenMessage != null) {
g.setFont(new Font("Arial", Font.BOLD, 18));
int messageWidth = g.getFontMetrics().stringWidth(screenMessage);
g.drawString(screenMessage, (Settings.WINDOW_WIDTH / 2) - (messageWidth / 2),
Settings.MESSAGE_POSITION);
}
}
#Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_LEFT) {
paddle.setXVelocity(-1);
}
if (key == KeyEvent.VK_RIGHT) {
paddle.setXVelocity(1);
}
}
#Override
public void keyReleased(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_LEFT || e.getKeyCode() == KeyEvent.VK_RIGHT) {
paddle.setXVelocity(0);
}
}
#Override
public void keyTyped(KeyEvent arg0) {
}
#Override
public void actionPerformed(ActionEvent arg0) {
update();
}
}
I made a similar game in python years ago. What I did to solve this problem was allow the player to control the direction of the ball by hitting it with different parts of the paddle.
If the player hit the ball with the absolute center of the paddle, then I would just reverse the vector as if it had hit off a wall. But if the absolute edge of the paddle hit the ball, then I would increase the x velocity in the same direction of the edge that it hit. You can make the effect continuous, so if the the ball hits the paddle halfway between the edge and the center, it increases the x velocity half as much as it would have if it had hit the absolute edge.
This way the vector will continuously change and the ball will bounce off of things in a more interesting way, as well as give the player more control so they don't have to just wait until the ball eventually hits every brick in a predefined pattern.
You can use a coefficient, or more complex function if you don't want the effect of where the ball hits the paddle to effect the x velocity in such a linear manner.
You can use a similar strategy for handling collisions with the bricks. For instance, if the ball hits the corner of a brick, you probably don't want it to react as if it had hit the bottom, or the side, but have a reaction that is somewhere in between.
I need to create a JPanel, where upon a mouse click a new Sprite must appear in a new thread. This is what I have:
public class BouncingSprites {
private JFrame frame;
private SpritePanel panel = new SpritePanel();
private Sprite ball;
public BouncingSprites() {
frame = new JFrame("Bouncing Sprite");
frame.setSize(400, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.setVisible(true);
}
public void start(){
panel.animate(); // never returns due to infinite loop in animate method
}
public static void main(String[] args) {
new BouncingSprites().start();
}
}
Class SpritePanel:
public class SpritePanel extends JPanel
{
Sprite sprite;
public SpritePanel()
{
addMouseListener(new Mouse());
}
//method for creating a new ball
private void newSprite (MouseEvent event)
{
sprite = new Sprite(this);
}
public void animate()
{
}
private class Mouse extends MouseAdapter
{
#Override
public void mousePressed( final MouseEvent event )
{
newSprite(event);
}
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
if (sprite != null)
{
sprite.draw(g);
}
}
}
Class Sprite :
public class Sprite implements Runnable
{
public final static Random random = new Random();
final static int SIZE = 10;
final static int MAX_SPEED = 5;
SpritePanel panel;
private int x;
private int y;
private int dx;
private int dy;
private Color color = Color.BLUE;
private Thread animation;
public Sprite (SpritePanel panel)
{
this.panel = panel;
x = random.nextInt(panel.getWidth());
y = random.nextInt(panel.getHeight());
dx = random.nextInt(2*MAX_SPEED) - MAX_SPEED;
dy = random.nextInt(2*MAX_SPEED) - MAX_SPEED;
animation = new Thread(this);
animation.start();
}
public void draw(Graphics g)
{
g.setColor(color);
g.fillOval(x, y, SIZE, SIZE);
}
public void move()
{
// check for bounce and make the ball bounce if necessary
//
if (x < 0 && dx < 0){
//bounce off the left wall
x = 0;
dx = -dx;
}
if (y < 0 && dy < 0){
//bounce off the top wall
y = 0;
dy = -dy;
}
if (x > panel.getWidth() - SIZE && dx > 0){
//bounce off the right wall
x = panel.getWidth() - SIZE;
dx = - dx;
}
if (y > panel.getHeight() - SIZE && dy > 0){
//bounce off the bottom wall
y = panel.getHeight() - SIZE;
dy = -dy;
}
//make the ball move
x += dx;
y += dy;
}
#Override
public void run()
{
while (Thread.currentThread() == animation)
{
move();
panel.repaint();
try
{
Thread.sleep(40);
}
catch ( InterruptedException exception )
{
exception.printStackTrace();
}
}
}
}
This code creates a new moving sprite. However, the previous sprite gets removed from the panel. What I need to do is to add a new sprite with a mouse click, so that the previous sprite is not removed. If I click three times, three sprites should be painted.
How do I change my code to implement that?
Many thanks!
You could replace Sprite sprite; in the SpritePanel class with List<Sprite> sprites = new ArrayList<>();. Then, when you make a new Sprite, add it to the list. Then in your paintComponent method, you could iterate through the list, drawing all of the sprites.
I have made a mini tennis game that moves the racquet left and right using a keylistener.
package mini_tennis;
import java.awt.Rectangle;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
public class Racquet {
private static final int Y = 330;
private static final int WIDTH = 60;
private static final int HEIGHT = 10;
int x = 0;
int xa = 0;
private Game game;
public Racquet(Game game) {
this.game= game;
}
public void move() {
if (x + xa > 0 && x + xa < game.getWidth()-60)
x = x + xa;
}
public void paint(Graphics2D g) {
g.fillRect(x, 330, 60, 10);
}
public void keyReleased(KeyEvent e) {
xa = 0;
}
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_LEFT)
xa = -game.speed;
if (e.getKeyCode() == KeyEvent.VK_RIGHT)
xa = game.speed;
}
public Rectangle getBounds() {
return new Rectangle(x, Y, WIDTH, HEIGHT);
}
public int getTopY() {
return Y;
}
}
I would like to know how to move the racquet using mouse events. For example, when I click on the racquet using the left button it will drag the racquet left or right. Could you also include how I could move the racquet by moving my mouse left and right without pressing any buttons.
you will need an mouse listener
public class Racquet {
...
public void moveM(int mx)
{
x = mx; //invoked from panel to move the racquet
}
...
}
your panel class:
class yourPanelClass implements MouseListener {
...
panel.addMouseListener(this); //add listener to the panel
Racquet raq = new Racquet(); //create the racquet
...
#Override
public void mouseClicked(MouseEvent e) {
raq.moveM(e.getX()+((int)raq.getWidth()/2));
// get pointer position on mouse press and invoke method
// in Racquet passing the current X location of the pointer
// subtracting half of the width of the racquet thus positioning it in
// the middle.
}
#Override
public void mouseEntered(MouseEvent e) {}
#Override
public void mouseExited(MouseEvent e) {}
#Override
public void mousePressed(MouseEvent e) {}
....
}
I am creating a simple game where shapes fall and the player shoots them, but I am having problems creating bullet at every click of the mouse. I have tried various logic with no help, so am just going to put the code up here so you guys can take a look at it and help me out.
The bullet I created is not been created on every click just one is created and it moves on every click which is wrong........I want one bullet to be created per click.
// My main class: mousework2
import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.awt.geom.*;
public class mousework2 extends JFrame
{
public static int Width = 300;
public static int Height = 400;
private JPanel p1;
private Image pixMage,gunMage;
public mousework2()
{
super("shoot-em-up");
this.setSize(Width, Height);
this.setResizable(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Dimension pos = Toolkit.getDefaultToolkit().getScreenSize();
int x = (pos.width - Width) / 2;
int y = (pos.height - Height) / 2;
this.setLocation(x, y);
p1 = new CreateImage();
this.add(p1);
this.getContentPane();
Thread t = new recMove(this);
t.start();
}
class recMove extends Thread
{
JFrame b;
public recMove(JFrame b)
{
this.b = b;
}
public void run()
{
while (true) {
b.repaint();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
}
}
}
class CreateImage extends JPanel implements MouseListener
{
ArrayList<fallShape> rect = new ArrayList<fallShape>();
int x_pos = mousework.Width / 2;
int y_pos = mousework.Height - 50;
int bx_pos = mousework.Width / 2;
int by_pos = mousework.Height;
int y_speed = -10;
boolean clicked;
public CreateImage()
{
for (int i = 0; i < 10; i++) {
rect.add(new fallShape(15, 15, rect));
}
Toolkit picx = Toolkit.getDefaultToolkit();
gunMage = picx.getImage("gunner.jpg");
gunMage = gunMage.getScaledInstance(200, -1, Image.SCALE_SMOOTH);
Toolkit pic = Toolkit.getDefaultToolkit();
pixMage = pic.getImage("ballfall3.jpg");
pixMage = pixMage.getScaledInstance(200, -1, Image.SCALE_SMOOTH);
addMouseListener(this);
addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseMoved(MouseEvent e)
{
x_pos = e.getX() - 5;
}
});
}
public void mousePressed(MouseEvent e)
{
if (e.getButton() == 1) {
clicked = true;
}
}
public void mouseReleased(MouseEvent e)
{
if (e.getButton() == 1) {
clicked = false;
}
}
public void mouseExited(MouseEvent e)
{
}
public void mouseEntered(MouseEvent e)
{
}
public void mouseClicked(MouseEvent e)
{
}
public void paint(Graphics g)
{
super.paint(g);
g.drawImage(pixMage, 0, 0, Width, Height, null);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.drawImage(gunMage,x_pos,y_pos,10,20,null);
if (clicked) {
by_pos += y_speed;
Shape bullet = new Rectangle2D.Float(bx_pos, by_pos, 3, 10);
g2.setColor(Color.BLACK);
g2.fill(bullet);
g2.draw(bullet);
}
g2.setColor(Color.RED);
for (fallShape b : rect) {
b.move();
g2.fill(b);
}
}
}
public static void main(String[] args)
{
java.awt.EventQueue.invokeLater(new Runnable() {
#Override
public void run()
{
new mousework2().setVisible(true);
}
});
}
}
And:
// My falling shapes class: fallShape
import java.awt.geom.*;
import java.util.*;
public class fallShape extends Rectangle2D.Float
{
public int x_speed, y_speed;
public int l, b;
public int height = mousework.Height;
public int width = mousework.Width;
public ArrayList<fallShape> fall;
public fallShape(int breadth, int length, ArrayList<fallShape> fall)
{
super((int) (Math.random() * (mousework.Width - 20) + 1), 0, breadth, length);
this.b = breadth;
this.l = length;
this.x_speed = (int) Math.random() * (10) + 1;
this.y_speed = (int) Math.random() * (10) + 1;
this.fall = fall;
}
public void move()
{
Rectangle2D rec = new Rectangle2D.Float(super.x, super.y, b, l);
for (fallShape f : fall) {
if (f != this && f.intersects(rec)) {
int rxspeed = x_speed;
int ryspeed = y_speed;
x_speed = f.x_speed;
y_speed = f.y_speed;
f.x_speed = rxspeed;
f.y_speed = ryspeed;
}
}
if (super.x < 0) {
super.x =+ super.x;
//super.y =+ super.y;
x_speed = Math.abs(x_speed);
}
if (super.x> mousework.Width - 30) {
super.x =+ super.x;
super.y =+ super.y;
x_speed =- Math.abs(x_speed);
}
if (super.y < 0) {
super.y = 0;
y_speed = Math.abs(y_speed);
}
super.x += x_speed;
super.y += y_speed;
}
}
if(clicked){
by_pos+=y_speed;
This code only draws the bullet when the mouse is down. This is because you are setting clicked to false in your mouseReleased method:
public void mouseReleased(MouseEvent e){
if(e.getButton()==1)
clicked=false;
}
If you were to remove the body of the mouseReleased method, your bullet would move properly.
However, say you wanted to have more than just one bullet. Currently, your paint method only draws one bullet at a time. To draw multiple bullets, you would need to create a list of the coordinates of the bullets, and add a new coordinate pair to the list whenever you click. Then, in the paint method, just update each position in a for loop.
ArrayList<Integer> by_poss = new ArrayList<>();
by_poss is the list of all the y-positions of your bullets.
public void mousePressed(MouseEvent e){
if(e.getButton() == 1)
by_poss.add(mousework.Height);
}
The mousePressed method adds a new "bullet", in the form of a y-position, to the coordinates.
public void mouseReleased(MouseEvent e){
//do nothing
}
Nothing needs to happen in the mouseReleased method.
//update the bullets
public void paint(Graphics g){
...
g2.setColor(Color.BLACK);
Shape bullet;
for(int i = 0; i < by_poss.size(); i++){
by_poss.set(i, by_poss.get(i) + y_speed); //move the bullet
bullet = new Rectangle2D.Float(bx_pos, by_poss.get(i), 3, 10);
g2.fill(bullet);
g2.draw(bullet);
}
...
}
The for loop in your paint method draws all the bullets, one by one, usin g the y-positions from the by_poss list.
UPDATE: semicomplex animation + swing timer = trainwreck. The ultimate source of the problems was the java timer, either the swing or utility version. They are unreliable, especially when performance is compared across operating systems. By implementing a run-of-the-mill thread, the program runs very smoothly on all systems. http://zetcode.com/tutorials/javagamestutorial/animation/. Also, adding Toolkit.getDefaultToolkit().sync() into the paintComponent() method noticeably helps.
I wrote some code that animated smoothly in an awt.Applet (but flickered), then I refactored it to java swing. Now it doesn't flicker but it looks choppy. I've messed with the timer but that doesn't work. Any tips or suggestions for smoothly animating swing components would be greatly appreciated.
import java.util.Random;
import java.util.ArrayList;
import java.awt.event.;
import java.awt.;
import javax.swing.*;
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
public class Ball extends JApplet{
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.setTitle("And so the ball rolls");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
initContainer(frame);
frame.pack();
frame.setVisible(true);
}
});
}
public static void initContainer(Container container){
GraphicsPanel graphicsPanel = new GraphicsPanel();
MainPanel mainPanel = new MainPanel(graphicsPanel);
container.add(mainPanel);
graphicsPanel.startTimer();
}
#Override
public void init(){
initContainer(this);
}
}
///////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
class MainPanel extends JPanel {
JLabel label = new JLabel("Particles");
GraphicsPanel gPanel;
public MainPanel(GraphicsPanel gPanel){
this.gPanel = gPanel;
add(gPanel);
add(label);
}
}
///////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
class GraphicsPanel extends JPanel implements MouseListener {
private ArrayList<Particle> ballArr = new ArrayList<Particle>();
private String state="s"; //"s"=spiral, "p"=particle
private int speed=10; //~20 Hz
private Timer timer;
public GraphicsPanel(){
System.out.println("echo from gpanel");
setPreferredSize(new Dimension(500,500));
timer = new Timer(speed, new TimerListener());
addMouseListener(this);
}
public void startTimer(){
timer.start();
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
for (Particle b: ballArr){
g.setColor(b.getColor());
g.fillOval(b.getXCoor(),b.getYCoor(),
b.getTheSize(),b.getTheSize());
}
}
public void mousePressed(MouseEvent e) {
ballArr.add(new Particle(e.getX(), e.getY(), state));
}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e){}
public void mouseExited(MouseEvent e){}
public void mouseClicked(MouseEvent e) {}
class TimerListener implements ActionListener {
public void actionPerformed(ActionEvent e){
for (Particle b: ballArr)
b.move();
setBackground(Color.WHITE);
repaint();
}
}
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
class Particle
{
private static int instanceCount; {{instanceCount++;}}
private int z = 11, t=1, u=1;
private int[] RGB = new int[3];
private int[] randomizeColor = new int[3];
private double radius, theta;
private int x, y, centerX, centerY, size, spiralDirection=1,
ballSizeLowerBound, ballSizeUpperBound,
radiusLowerBound, radiusUpperBound,
mouseInputX, mouseInputY,
radiusXMultiplier, radiusYMultiplier;
private Color color;
private String state;
private Random random = new Random();
///////////////////////////////////////////////////////////////////////////
public Particle(int x, int y, int centerX, int centerY, int radius,
int theta, int size, Color color){
this.x=x;this.y=y;this.centerX=centerX;this.centerY=centerY;
this.radius=radius;this.theta=theta;this.size=size;this.color=color;
}
public Particle(int mouseInputX, int mouseInputY, String state){
this.mouseInputX=mouseInputX;
this.mouseInputY=mouseInputY;
this.state=state;
//randomize color
RGB[0] = random.nextInt(252);
RGB[1] = random.nextInt(252);
RGB[2] = random.nextInt(252);
randomizeColor[0] = 1+random.nextInt(3);
randomizeColor[0] = 1+random.nextInt(3);
randomizeColor[0] = 1+random.nextInt(3);
centerX=mouseInputX;
centerY=mouseInputY;
if (state.equals("s")){ //setup spiral state
ballSizeLowerBound=5;
ballSizeUpperBound=18;
radiusLowerBound=0;
radiusUpperBound=50;
radiusXMultiplier=1;
radiusYMultiplier=1;
}
if (state.equals("p")){ //setup particle state
ballSizeLowerBound = 15;
ballSizeUpperBound =20 + random.nextInt(15);
radiusLowerBound = 5;
radiusUpperBound = 15+ random.nextInt(34);
radiusXMultiplier=1 + random.nextInt(3);
radiusYMultiplier=1 + random.nextInt(3);
}
size = ballSizeUpperBound-1; //ball size
radius = radiusUpperBound-1;
if (instanceCount %2 == 0) // alternate spiral direction
spiralDirection=-spiralDirection;
}
///////////////////////////////////////////////////////////////////////////
public int getXCoor(){return centerX+x*spiralDirection;}
public int getYCoor(){return centerY+y;}
public int getTheSize(){return size;}
public Color getColor(){return color;}
//////////////////////////////////////////////////////////////////////////
void move(){
//spiral: dr/dt changes at bounds
if (radius > radiusUpperBound || radius < radiusLowerBound)
u = -u;
//spiral shape formula: parametric equation for the
//polar equation radius = theta
x = (int) (radius * radiusXMultiplier * Math.cos(theta));
y = (int) (radius * radiusYMultiplier * Math.sin(theta));
radius += .1*u;
theta += .1;
//ball size formula
if (size == ballSizeUpperBound || size == ballSizeLowerBound)
t = -t;
size += t;
//ball colors change
for (int i = 0; i < RGB.length; i++)
if (RGB[i] >= 250 || RGB[i] <= 4)
randomizeColor[i] = -randomizeColor[i];
RGB[0]+= randomizeColor[0];
RGB[1]+= randomizeColor[1];
RGB[2]+= randomizeColor[2];
color = new Color(RGB[0],RGB[1],RGB[2]);
}
}
Don't set a constant interval timer. Set the timer to go off once -- in the handler
Get the current time (save in frameStartTime)
Do your frame
Set the timer to go off in: interval - (newCurrentTime - frameStartTime)
Should be smoother. If you want to go really pro (and stay in Java), I think you have to consider JavaFX.