I switched from programming in Processing to Java and right now, I am trying to translate some techniques to pure Java programming language.
It seems that my code is lagging sometimes and it is also slower than in my Processing sketches. Did I get something wrong? How could I improve my code? Would this kind of calculation also work gpu computation, maybe in lwjgl?
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.awt.Canvas;
import java.awt.Graphics;
import java.util.Random;
import javax.swing.JFrame;
public class Main extends Canvas implements Runnable {
public static final int WIDTH = 1280;
public static final int HEIGHT = 800;
private int xstart;
private int ystart;
private int xend;
private int yend;
private int xPos;
private int yPos;
private Thread thread;
private boolean isRunning = false;
private BufferedImage img;
private int[] pixels;
private Random r;
public Main() {
img = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
pixels = ((DataBufferInt) img.getRaster().getDataBuffer()).getData();
r = new Random();
xPos = WIDTH / 2;
yPos = HEIGHT / 2;
xstart = xPos - 200;
ystart = yPos - 200;
xend = xPos + 200;
yend = yPos + 200;
}
#Override
public void run() {
while (isRunning) {
render();
}
}
private void start() {
if (isRunning)
return;
isRunning = true;
thread = new Thread(this);
thread.start();
}
private void stop() {
if (!isRunning)
return;
isRunning = false;
try {
thread.join();
} catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
}
private void render() {
addMouseMotionListener(new MouseMotionListener() {
public void mouseDragged(MouseEvent e) {
xPos = e.getX();
yPos = e.getY();
}
public void mouseMoved(MouseEvent e) {
}
});
addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent e) {
xPos = e.getX();
yPos = e.getY();
}
public void mousePressed(MouseEvent e) {
xPos = e.getX();
yPos = e.getY();
}
public void mouseReleased(MouseEvent e) {
xPos = e.getX();
yPos = e.getY();
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
});
xstart = xPos - 50;
ystart = yPos - 50;
xend = xPos + 50;
yend = yPos + 50;
if (xstart < 0)
xstart = 0;
if (xstart > WIDTH)
xstart = WIDTH;
if (ystart < 0)
ystart = 0;
if (ystart > HEIGHT)
ystart = HEIGHT;
if (xend < 0)
xend = 0;
if (xend > WIDTH)
xend = WIDTH;
if (yend < 0)
yend = 0;
if (yend > HEIGHT)
yend = HEIGHT;
BufferStrategy bs = this.getBufferStrategy();
if (bs == null) {
createBufferStrategy(3);
return;
}
for (int i = 0; i < WIDTH * HEIGHT; i++) {
pixels[i] *= 0.99;
if (pixels[i] <= 0)
pixels[i] = 0;
}
for (int y = ystart; y < yend; y++) {
for (int x = xstart; x < xend; x++) {
int i = x + y * WIDTH;
pixels[i] = r.nextInt(0xffffff);
}
}
Graphics g = bs.getDrawGraphics();
g.drawImage(img, 0, 0, WIDTH, HEIGHT, null);
g.dispose();
bs.show();
}
public static void main(String[] args) {
Main main = new Main();
JFrame frame = new JFrame();
frame.add(main);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(WIDTH, HEIGHT);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setVisible(true);
main.start();
}
}
You're adding new MouseListeners every time render is called, this means, each time a MouseEvent occurs, they have to notify and ever increasing list of listeners, all which seem to do the same thing...
Instead, setup your listeners in the constructor
I would also suggest getting rid of the "render thread" and simply calling your render method only when you want the screen updated (use passive painting instead of active rendering)
Related
I'm recreating Asteroids, the spaceship can move forward and backwards and change it's rotation. When I for example move forward, the velocity keeps increasing until I release the forward key. But it also stops increasing the velocity when I for example hit the rotate left key. I don't understand why it does this, and I want to be able to increase the rotation degrees while increasing or decreasing my velocity (i.e. pressing UP or DOWN arrow). It also stops moving forward or rotating when I shoot (i.e. space bar).
Also, is my code readable and the aproach OO?
Asteroids:
import javax.swing.*;
public class Asteroids {
public static void createAndShowGui() {
GamePanel gamePanel = new GamePanel();
JFrame frame = new JFrame("Asteroids");
frame.getContentPane().add(gamePanel);
frame.pack();
frame.setVisible(true);
frame.setResizable(false);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setLocation(2000, 50);
gamePanel.requestFocusInWindow();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
GamePanel.java
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
public class GamePanel extends JPanel implements ActionListener {
private final int WIDTH = 1600;
private final int HEIGHT = 900;
private ArrayList<Rock> rocks;
private ArrayList<Bullet> bullets;
private SpaceShip spaceShip;
private boolean keyHeld;
private int keyCode;
public GamePanel() {
setPreferredSize(new Dimension(WIDTH, HEIGHT));
setUp();
}
public void setUp() {
Timer animationTimer = new Timer(15, this);
spaceShip = new SpaceShip(WIDTH, HEIGHT);
bullets = new ArrayList<>();
rocks = new ArrayList<>();
addKeyListener(new KeyListener());
for (int i = 0; i < 12; i++) {
rocks.add(new Rock(WIDTH, HEIGHT));
}
animationTimer.start();
}
class KeyListener extends KeyAdapter {
#Override
public void keyPressed(KeyEvent e) {
keyCode = e.getKeyCode();
keyHeld = true;
}
#Override
public void keyReleased(KeyEvent e) {
keyHeld = false;
}
}
#Override
public void actionPerformed(ActionEvent e) {
checkRockCollisions();
for (int i = 0; i < bullets.size(); i++)
checkBulletOffScreen(bullets.get(i));
spaceShip.checkForCollisionWithFrame(WIDTH, HEIGHT);
moveObjects();
checkForPressedKeys();
repaint();
}
public void moveObjects() {
for (Rock rock : rocks)
rock.moveForward();
for (Bullet bullet : bullets)
bullet.moveForward();
spaceShip.moveSpaceShip();
}
public void checkRockCollisions() {
for (int i = 0; i < rocks.size(); i++) {
Rock rock = rocks.get(i);
rocks.stream().filter(rockToCheck -> !rock.equals(rockToCheck)).forEach(rock::checkRockCollision);
for (int j = 0; j < bullets.size(); j++) {
Bullet bullet = bullets.get(j);
if (rock.checkBulletCollision(bullet)) {
rocks.remove(rock);
bullets.remove(bullet);
}
}
if (rock.checkSpaceShipCollision(spaceShip))
resetSpaceShip();
rock.checkFrameCollision(WIDTH, HEIGHT);
}
}
public void checkBulletOffScreen(Bullet bullet) {
if (bullet.getxPos() + bullet.getBulletWidth() < 0 || bullet.getxPos() > WIDTH || bullet.getyPos() + bullet.getBulletHeight() < 0 || bullet.getyPos() > HEIGHT)
bullets.remove(bullet);
}
public void resetSpaceShip() {
spaceShip = new SpaceShip(WIDTH, HEIGHT);
}
public void checkForPressedKeys() {
if (keyHeld) {
switch (keyCode) {
case KeyEvent.VK_RIGHT:
spaceShip.increaseRotationDegree();
break;
case KeyEvent.VK_LEFT:
spaceShip.decreaseRotationDegree();
break;
case KeyEvent.VK_UP:
spaceShip.increaseForwardVelocity();
break;
case KeyEvent.VK_DOWN:
spaceShip.decreaseForwardVelocity();
break;
case KeyEvent.VK_SPACE:
bullets.add(new Bullet(spaceShip.getxPos() + spaceShip.getSpaceShipRadius(), spaceShip.getyPos() + spaceShip.getSpaceShipRadius(), spaceShip.getRotationDegree()));
keyHeld = false;
break;
}
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
RenderingHints mainRenderingHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHints(mainRenderingHints);
for (Rock rock : rocks)
rock.display(g2d);
for (Bullet bullet : bullets)
bullet.display(g2d);
spaceShip.display(g2d);
g2d.dispose();
}
}
SpaceShip.java
import java.awt.*;
public class SpaceShip {
private double xPos;
private double yPos;
private double xVelocity;
private double yVelocity;
private int spaceShipDiameter;
private int rotationDegree;
private int spaceShipRadius;
public SpaceShip(final int WIDTH, final int HEIGHT) {
spaceShipDiameter = 30;
spaceShipRadius = spaceShipDiameter / 2;
xPos = WIDTH / 2 - spaceShipRadius;
yPos = HEIGHT / 2 - spaceShipRadius;
rotationDegree = 0;
}
public int getSpaceShipRadius() {
return spaceShipRadius;
}
public double getxPos() {
return xPos;
}
public double getyPos() {
return yPos;
}
public int getRotationDegree() {
return rotationDegree;
}
public void increaseForwardVelocity() {
xVelocity += 0.04 * Math.sin(Math.toRadians(this.rotationDegree));
yVelocity += 0.04 * -Math.cos(Math.toRadians(this.rotationDegree));
}
public void decreaseForwardVelocity() {
xVelocity -= 0.04 * Math.sin(Math.toRadians(this.rotationDegree));
yVelocity -= 0.04 * -Math.cos(Math.toRadians(this.rotationDegree));
}
public void moveSpaceShip() {
this.xPos += xVelocity;
this.yPos += yVelocity;
}
public void increaseRotationDegree() {
if (rotationDegree >= 350)
rotationDegree = 0;
else
rotationDegree+=10;
}
public void decreaseRotationDegree() {
if (rotationDegree <= 0)
rotationDegree = 350;
else
rotationDegree-=10;
}
public void checkForCollisionWithFrame(final int WIDTH, final int HEIGHT) {
if (xPos + spaceShipDiameter < 0)
xPos = WIDTH;
else if (xPos > WIDTH)
xPos = 0 - spaceShipDiameter;
else if (yPos + spaceShipDiameter < 0)
yPos = HEIGHT;
else if (yPos > HEIGHT)
yPos = 0 - spaceShipDiameter;
}
public void display(Graphics2D g2d) {
g2d.setColor(Color.BLACK);
g2d.rotate(Math.toRadians(rotationDegree), xPos + spaceShipRadius, yPos + spaceShipRadius);
g2d.fillOval((int) xPos, (int) yPos, spaceShipDiameter, spaceShipDiameter);
g2d.setColor(Color.YELLOW);
g2d.drawLine((int) xPos + spaceShipRadius, (int) yPos , (int) xPos + spaceShipRadius, (int) yPos + spaceShipRadius);
}
}
Rock.java
import java.awt.*;
import java.util.Random;
public class Rock {
private int xPos;
private int yPos;
private int rockDiameter;
private int xVelocity;
private int yVelocity;
private int rockRadius;
private Color rockColor;
public Rock(int WIDTH, int HEIGHT) {
Random r = new Random();
rockDiameter = r.nextInt(40) + 30;
rockRadius = rockDiameter / 2;
xPos = r.nextInt(WIDTH - rockDiameter);
yPos = r.nextInt(HEIGHT - rockDiameter);
xVelocity = r.nextInt(6) - 3;
yVelocity = r.nextInt(6) - 3;
rockColor = new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255));
}
public void moveForward() {
xPos += xVelocity;
yPos += yVelocity;
}
public void checkRockCollision(Rock rock) {
if (calculateDistances(rock.xPos, rock.yPos, rock.rockRadius))
switchVelocities(rock);
}
public void switchVelocities(Rock rock) {
int tempXVelocity = this.xVelocity;
int tempYVelocity = this.yVelocity;
this.xVelocity = rock.xVelocity;
this.yVelocity = rock.yVelocity;
rock.xVelocity = tempXVelocity;
rock.yVelocity = tempYVelocity;
moveForward();
rock.moveForward();
}
public boolean checkBulletCollision(Bullet bullet) {
return calculateDistances(bullet.getxPos(), bullet.getyPos(), bullet.getBulletRadius());
}
public boolean checkSpaceShipCollision(SpaceShip spaceShip) {
return calculateDistances(spaceShip.getxPos(), spaceShip.getyPos(), spaceShip.getSpaceShipRadius());
}
public boolean calculateDistances(double objectxPos, double objectyPos, int objectRadius) {
int radiusOfBoth = objectRadius + rockRadius;
double horDistance = Math.abs((objectxPos + objectRadius) - (xPos + rockRadius));
double verDistance = Math.abs((objectyPos + objectRadius) - (yPos + rockRadius));
double diagDistance = Math.sqrt(Math.pow(horDistance, 2) + Math.pow(verDistance, 2));
return diagDistance <= radiusOfBoth;
}
public void checkFrameCollision(final int WIDTH, final int HEIGHT) {
if (xPos < 0) {
xVelocity *= -1;
xPos = 0;
} else if (xPos + rockDiameter > WIDTH) {
xVelocity *= -1;
xPos = WIDTH - rockDiameter;
} else if (yPos < 0) {
yVelocity *= -1;
yPos = 0;
} else if (yPos + rockDiameter > HEIGHT) {
yVelocity *= -1;
yPos = HEIGHT - rockDiameter;
}
}
public void display(Graphics2D g2d) {
g2d.setColor(rockColor);
g2d.fillOval(xPos, yPos, rockDiameter, rockDiameter);
}
}
Bullet.java
import java.awt.*;
public class Bullet {
private double xPos;
private double yPos;
private int bulletWidth;
private int bulletHeight;
private double xVelocity;
private double yVelocity;
private int bulletRadius;
public Bullet(double startingXPos, double startingYPos, int rotationDegree) {
bulletHeight = 10;
bulletWidth = 10;
bulletRadius = bulletWidth / 2;
xPos = startingXPos - bulletRadius;
yPos = startingYPos - bulletRadius;
xVelocity = 5 * Math.sin(Math.toRadians(rotationDegree));
yVelocity = 5 * -Math.cos(Math.toRadians(rotationDegree));
}
public void moveForward() {
this.xPos += xVelocity;
this.yPos += yVelocity;
}
public double getxPos() {
return xPos;
}
public double getyPos() {
return yPos;
}
public int getBulletWidth() {
return bulletWidth;
}
public int getBulletHeight() {
return bulletHeight;
}
public int getBulletRadius() {
return bulletRadius;
}
public void display(Graphics2D g2d) {
g2d.setColor(Color.GREEN);
g2d.fillOval((int) xPos, (int) yPos, bulletWidth, bulletHeight);
}
}
You're only keeping track of the most recent key pressed. Instead, you need to keep track of every key being pressed. One way to do that is by using a boolean variable for each key. In your keyPressed() function, set the appropriate boolean to true, and in the keyReleased() function, set it to false. Then in your game loop, just check which booleans are true and take the appropriate action.
I tried to recreate some physics by creating a ball that bounces from the sides and that slows down. The ball stops moving in the x direction, but it keeps vibrating like only 1 pixel up and down in the y direction. Also it does this a little bit above the bottom border.
Also, is my code readable/good practise?
Bouncy.java
package Bouncy;
import javax.swing.*;
public class Bouncy {
private static void createAndShowGui() {
JFrame frame = new JFrame("Bouncy Balls");
Board board = new Board();
frame.getContentPane().add(board);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setLocation(2000, 50);
board.requestFocusInWindow();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
Board.java
package Bouncy;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Board extends JPanel implements ActionListener {
public static final int BOARDWIDTH = 800;
public static final int BOARDHEIGHT = 800;
private Ball ball;
public Board() {
Dimension preferedDimension = new Dimension(BOARDWIDTH, BOARDHEIGHT);
setPreferredSize(preferedDimension);
ball = new Ball(15, 0);
Timer animationTimer = new Timer(17, this);
animationTimer.start();
}
public void actionPerformed(ActionEvent e) {
ball.applyPhysics();
repaint();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHints(rh);
ball.display(g2d);
g2d.dispose();
}
}
Ball.java
package Bouncy;
import java.awt.*;
public class Ball {
private int xPos;
private int yPos;
private double dx;
private double dy;
private int ballWidth;
private int ballHeight;
public Ball() {
this(0, 0);
}
public Ball(int dx, int dy) {
ballWidth = 50;
ballHeight = ballWidth;
xPos = 0;
yPos = 0;
this.dx = dx;
this.dy = dy;
}
public void applyPhysics() {
bounceX();
applyXFriction();
bounceY();
}
public void bounceX() {
if (xPos > Board.BOARDWIDTH - ballWidth) {
xPos = Board.BOARDWIDTH - ballWidth;
dx = -dx;
} else if (xPos < 0) {
xPos = 0;
dx = -dx;
} else {
xPos += dx;
}
}
public void applyXFriction() {
final double xFriction = .95;
if (yPos == Board.BOARDHEIGHT - ballHeight) {
dx *= xFriction;
if (Math.abs(dx) < .5) {
dx = 0;
}
}
}
public void bounceY() {
final int gravity = 12;
final double energyLoss = .75;
final double dt = .2;
if (yPos > Board.BOARDHEIGHT - ballHeight){
yPos = Board.BOARDHEIGHT - ballHeight;
dy = -dy * energyLoss;
} else if (yPos < 0) {
yPos = 0;
dy *= -energyLoss;
} else {
dy += gravity * dt;
yPos += dy * dt + .5 * gravity * dt * dt;
}
}
public void display(Graphics2D g2d) {
g2d.fillOval(xPos, yPos, ballWidth, ballHeight);
}
}
Inside your apply friction method, where you set dx to 0, also set dy to 0. This will stop the ball moving along both the X and Y axis
if (Math.abs(dx) < .5)
{
dx = 0;
dy = 0;
}
This will stop the ball vibrating:
if (Math.abs(dy * dt + .5 * gravity * dt * dt) < 1.5 && yPos == Board.BOARDHEIGHT - ballHeight)
{
dy = 0;
yPos = Board.BOARDHEIGHT - ballHeight;
}
I'm trying to fire a projectile in the mouse direction but i am having trouble.
The angles are wrong, and by that I mean it will only go up left or in the top-left corner.
This is my Gun class for which I fire the Bullet.
package assets;
import Reaper.Game;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
public class Gun
{
public ArrayList<Bullet> bullets;
protected double angle;
Mouse mouse = new Mouse();
private BufferedImage image;
private int centerX = Game.WIDTH / 2;
private int centerY = Game.HEIGHT;
public Gun()
{
bullets = new ArrayList<Bullet>();
image = null;
try
{
image = ImageIO.read(new File("assets/gun.png"));
}
catch (IOException e)
{
e.printStackTrace();
}
}
public BufferedImage loadImage()
{
return image;
}
public int getImageWidth()
{
return image.getWidth(null);
}
public int getImageHeight()
{
return image.getHeight(null);
}
public void rotate(Graphics g)
{
angle = Math.atan2(centerY - mouse.getMouseY(), centerX - mouse.getMouseX()) - Math.PI / 2;
((Graphics2D) g).rotate(angle, centerX, centerY);
g.drawImage(image, Game.WIDTH / 2 - image.getWidth() / 2,
900 - image.getHeight(), image.getWidth(), image.getHeight(),
null);
}
public Image getImage()
{
return image;
}
public void update()
{
shoot();
for (int i = 0; i < bullets.size(); i++)
{
Bullet b = bullets.get(i);
b.update();
}
}
public void shoot()
{
if (mouse.mouseB == 1)
{
double dx = mouse.getMouseX() - Game.WIDTH / 2;
double dy = mouse.getMouseY() - Game.HEIGHT / 2;
double dir = Math.atan2(dy, dx);
bullets.add(new Bullet(Game.WIDTH / 2, Game.HEIGHT / 2, dir));
mouse.mouseB = -1;
}
}
public void render(Graphics g)
{
for (int i = 0; i < bullets.size(); i++)
{
bullets.get(i).render(g);
}
rotate(g);
}
}
This is my Bullet class:
package assets;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
public class Bullet
{
private double dx, dy;
private int x, y;
private double dir;
private BufferedImage image;
public Bullet(int x, int y, double angle)
{
this.x = x;
this.y = y;
this.dir = angle;
image = null;
try
{
image = ImageIO.read(new File("assets/bolt.png"));
}
catch (IOException e)
{
e.printStackTrace();
}
dx = Math.cos(dir);
dy = Math.sin(dir);
}
public void update()
{
x += dx;
y += dy;
System.out.println("dx : " + dx + " " + dy);
}
public void render(Graphics g)
{
g.drawImage(image, x, y, image.getWidth(), image.getHeight(), null);
}
public BufferedImage getImage()
{
return image;
}
}
And this is the main Game class:
package Reaper;
import Reaper.graphics.Screen;
import assets.Gun;
import assets.Mouse;
import gameState.MenuState;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferStrategy;
public class Game extends Canvas implements Runnable
{
private static final long serialVersionUID = 1L;
public static int WIDTH = 900;
public static int HEIGHT = 900;
public static int scale = 3;
public int frames = 0;
public int updates = 0;
private boolean running = false;
private Thread thread;
private JFrame frame;
#SuppressWarnings("unused")
private Screen screen;
private Gun gun;
private MenuState mns;
private Mouse mouse;
public Game()
{
Dimension size = new Dimension(WIDTH, HEIGHT);
setPreferredSize(size);
screen = new Screen(WIDTH, HEIGHT);
frame = new JFrame();
gun = new Gun();
mns = new MenuState();
mouse = new Mouse();
addMouseListener(mouse);
addMouseMotionListener(mouse);
}
public static void main(String[] args)
{
Game game = new Game();
game.frame.setResizable(false);
game.frame.add(game);
game.frame.pack();
game.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
game.frame.setVisible(true);
game.frame.setLocationRelativeTo(null);
game.strat();
}
public void strat()
{
running = true;
thread = new Thread(this, "Reaper");
thread.start();
}
public void stop()
{
running = false;
try
{
thread.join();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
public void run()
{
long timer = System.currentTimeMillis();
long lastTime = System.nanoTime();
double ns = 1000000000.0 / 60;
double delta = 0;
while (running)
{
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
while (delta >= 1)
{
update();
updates++;
delta--;
}
render();
frames++;
if (System.currentTimeMillis() - timer > 1000)
{
timer = System.currentTimeMillis();
frame.setTitle("Reaper! " + " | " + updates + " ups , " + frames + " fps");
updates = 0;
frames = 0;
}
}
stop();
}
public void update()
{
if (mns.play)
{
gun.update();
}
}
public void render()
{
BufferStrategy bs = getBufferStrategy();
if (bs == null)
{
createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
g.fillRect(0, 0, WIDTH, HEIGHT);
if (mns.menu)
{
mns.draw(g);
}
if (mns.play)
{
g.clearRect(0, 0, WIDTH, HEIGHT);
g.drawImage(mns.getImage(), 0, 0, mns.getImageWidth(), mns.getImageHeight(), this);
gun.render(g);
}
if (mns.rules)
{
g.clearRect(0, 0, WIDTH, HEIGHT);
}
bs.show();
}
}
I know it's very badly coded, and I will try to fix it as much as I can, but I am really stuck on this. Searched around the web, tried some of the solutions but it wont work. I guess I'm doing it wrong by putting methods in wrong places and calling them, I guess.
I know it's very badly coded, and I will try to fix it as much as I can, ...
Next time, consider first cleaning up the mess and then asking the question.
(Are the variables storing the values returned by getMouseX() and getMouseY() really static?)
However, the main reason for the actual problem are the x and y values in the Bullet class:
class Bullet
{
private double dx, dy;
private int x, y;
....
public void update()
{
x += dx;
y += dy;
System.out.println("dx : " + dx + " " + dy);
}
...
}
They are declared as int values. Imagine what happens in the update method, for example, when x=0 and dx = 0.75: It will compute 0+0.75 = 0.75, truncate this to be an int value, and the result will be 0.
Thus, the x and y values will never change, unless the dx and dy values are >= 1.0, respectively.
Just changing the type of x and y to double will solve this. You'll have to add casts in the render method accordingly:
g.drawImage(image, (int)x, (int)y, ...);
But you should really clean this up.
After making changes based on a previous post I have taken the following code a few steps further by introducing single/double click recognition. Why are the balls being created in the top left corner and not where the mouse is clicked?
BouncingBalls.java
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class BouncingBalls extends JPanel implements MouseListener {
protected List<RandomBall> randomBalls = new ArrayList<RandomBall>(20);
protected List<VerticalBall> verticalBalls = new ArrayList<VerticalBall>(20);
private Container container;
private DrawCanvas canvas;
private Boolean doubleClick = false;
private final Integer waitTime = (Integer) Toolkit.getDefaultToolkit()
.getDesktopProperty("awt.multiClickInterval");
private static int canvasWidth = 500;
private static int canvasHeight = 500;
public static final int UPDATE_RATE = 30;
int count = 0;
public static int random(int maxRange) {
return (int) Math.round((Math.random() * maxRange));
}
public BouncingBalls(int width, int height) {
canvasWidth = width;
canvasHeight = height;
container = new Container();
canvas = new DrawCanvas();
this.setLayout(new BorderLayout());
this.add(canvas, BorderLayout.CENTER);
this.addMouseListener(this);
start();
}
public void start() {
Thread t = new Thread() {
public void run() {
while (true) {
update();
repaint();
try {
Thread.sleep(1000 / UPDATE_RATE);
} catch (InterruptedException e) {
}
}
}
};
t.start();
}
public void update() {
for (RandomBall ball : randomBalls) {
ball.ballBounce(container);
}
for (VerticalBall ball : verticalBalls) {
ball.verticalBounce(container);
}
}
class DrawCanvas extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
container.draw(g);
for (RandomBall ball : randomBalls) {
ball.draw(g);
}
for (VerticalBall ball : verticalBalls) {
ball.draw(g);
}
}
public Dimension getPreferredSize() {
return (new Dimension(canvasWidth, canvasHeight));
}
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame f = new JFrame("Stack Answer 2");
f.setDefaultCloseOperation(f.EXIT_ON_CLOSE);
f.setContentPane(new BouncingBalls(canvasHeight, canvasWidth));
f.pack();
f.setVisible(true);
}
});
}
#Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent e) {
if (e.getClickCount() >= 2) {
doubleClick = true;
verticalBalls.add(new VerticalBall(getX(), getY(), canvasWidth, canvasHeight));
System.out.println("double click");
} else {
Timer timer = new Timer(waitTime, new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (doubleClick) {
/* we are the first click of a double click */
doubleClick = false;
} else {
count++;
randomBalls.add(new RandomBall(getX(), getY(), canvasWidth, canvasHeight));
/* the second click never happened */
System.out.println("single click");
}
}
});
timer.setRepeats(false);
timer.start();
}
}
#Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
}
RandomBall.java
import java.awt.Color;
import java.awt.Graphics;
public class RandomBall {
public static int random(int maxRange) {
return (int) Math.round((Math.random() * maxRange));
}
private int x;
private int y;
private int canvasWidth = 500;
private int canvasHeight = 500;
private boolean leftRight;
private boolean upDown;
private int deltaX;
private int deltaY;
private int radius = 20;
private int red = random(255);
private int green = random(255);
private int blue = random(255);
RandomBall(int x, int y, int width, int height) {
this(x, y, width, height, false, false);
}
RandomBall(int x, int y, int width, int height, boolean leftRight, boolean upDown) {
this.x = x;
this.y = y;
this.canvasWidth = width;
this.canvasHeight = height;
this.leftRight = leftRight;
this.upDown = upDown;
updateDelta();
}
public void draw(Graphics g) {
g.setColor(new Color(red, green, blue));
g.fillOval((int) (x - radius), (int) (y - radius), (int) (2 * radius),
(int) (2 * radius));
}
private void updateDelta() {
final int minimumMovement = 5;
final int maxExtra = 10;
deltaY = minimumMovement + (int) (Math.random() * maxExtra);
deltaX = minimumMovement + (int) (Math.random() * maxExtra);
}
public void ballBounce(Container container) {
// controls horizontal ball motion
if (leftRight) {
x += deltaX;
if (x >= getWidth()) {
leftRight = false;
updateDelta();
}
} else {
x += -deltaX;
if (x <= 0) {
leftRight = true;
updateDelta();
}
}
// controls vertical ball motion
if (upDown) {
y += deltaY;
if (y >= getHeight()) {
upDown = false;
updateDelta();
}
} else {
y += -deltaY;
if (y <= 0) {
upDown = true;
updateDelta();
}
}
}
public void verticalBounce(Container container) {
// controls vertical ball motion
if (upDown) {
y += deltaY;
if (y >= getHeight()) {
upDown = false;
updateDelta();
}
} else {
y += -deltaY;
if (y <= 0) {
upDown = true;
updateDelta();
}
}
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getWidth() {
return canvasWidth;
}
public int getHeight() {
return canvasHeight;
}
}
VerticalBall.java
import java.awt.Color;
import java.awt.Graphics;
public class VerticalBall {
public static int random(int maxRange) {
return (int) Math.round((Math.random() * maxRange));
}
private int x;
private int y;
private int canvasWidth = 500;
private int canvasHeight = 500;
private boolean upDown;
private int deltaY;
private int radius = 20;
private int red = random(255);
private int green = random(255);
private int blue = random(255);
VerticalBall(int x, int y, int width, int height) {
this(x, y, width, height, false);
}
VerticalBall(int x, int y, int width, int height, boolean upDown) {
this.x = x;
this.y = y;
this.canvasWidth = width;
this.canvasHeight = height;
this.upDown = upDown;
updateDelta();
}
public void draw(Graphics g) {
g.setColor(new Color(red, green, blue));
g.fillOval((int) (x - radius), (int) (y - radius), (int) (2 * radius),
(int) (2 * radius));
}
private void updateDelta() {
final int minimumMovement = 5;
final int maxExtra = 10;
deltaY = minimumMovement + (int) (Math.random() * maxExtra);
}
public void verticalBounce(Container container) {
// controls vertical ball motion
if (upDown) {
y += deltaY;
if (y >= getHeight()) {
upDown = false;
updateDelta();
}
} else {
y += -deltaY;
if (y <= 0) {
upDown = true;
updateDelta();
}
}
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getWidth() {
return canvasWidth;
}
public int getHeight() {
return canvasHeight;
}
}
Container.java
import java.awt.Color;
import java.awt.Graphics;
public class Container {
private static final int HEIGHT = 500;
private static final int WIDTH = 500;
private static final Color COLOR = Color.WHITE;
public void draw(Graphics g) {
g.setColor(COLOR);
g.fillRect(0, 0, WIDTH, HEIGHT);
}
}
Basically, because that's where you're telling them to be created...
verticalBalls.add(new VerticalBall(getX(), getY(), canvasWidth, canvasHeight));
getX() and getY() are getting the component's location (which happens to be close enough to 0x0 for arguments sake)...
Instead, you should be using the MouseEvent information...
public void mousePressed(MouseEvent e) {
if (e.getClickCount() >= 2) {
doubleClick = true;
verticalBalls.add(new VerticalBall(e.getX(), e.getY(), canvasWidth, canvasHeight));
System.out.println("double click");
}
}
This will create an issue for your Timer...
Instead you may need to introduce a couple of variables to hold the position information...
final int x = e.getX();
final int y = e.getX();
if (e.getClickCount() >= 2) {
...
} else {
Timer timer = new Timer(waitTime, new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (doubleClick) {
/* we are the first click of a double click */
doubleClick = false;
} else {
count++;
randomBalls.add(new RandomBall(x, y, canvasWidth, canvasHeight));
/* the second click never happened */
System.out.println("single click");
}
}
});
timer.setRepeats(false);
timer.start();
}
You need to use the getX and getY from the MouseEvent, not from the JPanel.
As a sidenote, your use of a separate Thread to periodically update the gui is bad because you can't use separate threads to call swing methods. use a swing Timer instead.
I am writing a program in java so it can imitate an etch a sketch. I have it to draw lines but I cant seem to get it to draw a continuous line. I am a beginner at this so much help would be appreciated. Thanks !
here is my code so far..
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Sketch
{
public static void main (String [] args){
SketchFrame frame = new SketchFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true); }}
// JFrame: defines app's window
class SketchFrame extends JFrame
{
public static final int WIDTH = 600;
public static final int HEIGHT = 600;
public static final String FRAME_TITLE = "Sketch";
public SketchFrame()
{
setTitle(FRAME_TITLE);
setSize(WIDTH, HEIGHT);
add( new SketchPanel() );
}
}
class SketchPanel extends JPanel
implements KeyListener {
private int xStart = 0;
private int yStart = 0;
private int xEnd = 0;
private int yEnd = 0;
public SketchPanel() {
addKeyListener(this);
setFocusable(true);
}
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_UP) {
yStart = yEnd;
xStart = xEnd;
yEnd -= 50;
}
else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
yStart = yEnd;
xStart = xEnd;
yEnd += 10;
}
else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
xStart = xEnd;
yStart = yEnd;
xEnd -= 10;
}
else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
xStart = xEnd;
yStart = yEnd;
xEnd += 10;
}
repaint();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setColor(Color.RED);
g2.drawLine(drawXStart(), drawYStart(), drawXEnd(), drawYEnd());
}
private int drawXStart() {
return (getWidth() / 2) + xStart;
}
private int drawXEnd() {
return (getWidth() / 2) + xEnd;
}
private int drawYStart() {
return (getHeight() / 2) + yStart;
}
private int drawYEnd() {
return (getHeight() /2) + yEnd;
}
public void keyReleased(KeyEvent e) {}
public void keyTyped(KeyEvent e) {}
}
The problem is that the SketchPanel doesn't remember the lines drawn previously. You should probably use a BufferedImage to draw on (you could do the drawing in keyPressed), and draw that image onto the Graphics object in paintComponent.