Mouse Collision with JComponent and AWT MouseEvent Problem - java

So I have a problem with my mouse detection using a object called Ornament that inherits JComponent and I am getting the mouse clicks using java.awt.event.MouseEvent.
The solution me and my group though would be the best is to compare the distance of each ornament to the mouse and if its less than 50 (which is the radius of the ornament) it would do something.
private class ME implements MouseListener {
#Override
public void mouseClicked(MouseEvent e) {
for(int i = 0; i < ORNAMENT_AMOUNT; i++) {
Ornament current = oh.getOrnament(i);
int distance = (int)(Math.sqrt(((e.getX() - current.getX()) * (e.getX() - current.getX())) + ((e.getY() - current.getY()) * (e.getY() - current.getY()))));
if(distance <= 50) {
System.out.println("CIRCLE CLICKED!");
current.reset();
}
}
}
The problem that I am getting is that it just does not work how it should. I click in the same spot many times and eventually it will trigger the event on one of the ornaments. Its random and confusing.
Here is the Ornament class
//ornament class to define the game object ornament
public class Ornament extends JComponent{
private int xPos;
private int yPos;
private int velocity;
private int screenWidth;
private int screenHeight;
private Random rand;
public Ornament(int screenWidth, int screenHeight, int velocity) {....}
public int getY() { return yPos; }
public int getX() { return xPos; }
#Override
public void paintComponent(Graphics graphics) {
float r = rand.nextFloat();
float g = rand.nextFloat();
float b = rand.nextFloat();
super.paintComponent(graphics);
graphics.setColor(new Color(r, g, b));
graphics.fillOval(0, 0, 50, 50);
}
....
public void reset() {
this.yPos = -(int)(Math.random()*500);
this.xPos = (int)(Math.random()*screenWidth);
this.velocity += 1;
update();
}
}
My whole approach to this might be wrong but it seems right in my head so help would be appreciated! :)
*note oh is an objecthandler class I made to run the game loop in a different thread that has an array of ornaments
public class ObjectHandler implements Runnable {
private int size;
private Ornament[] list;
private GUI game;
public ObjectHandler(int size, GUI game) {
....
list = new Ornament[size];
//init object list
for(int i = 0; i < size; i++) {
list[i] = new Ornament(game.getFrameX(), game.getFrameY(), game.getStartingVel());
game.add(list[i]);
}
}
public Ornament getOrnament(int index) { return list[index]; }
public void run() {
while(true) {
try {
Thread.sleep(50);
} catch (InterruptedException e) { e.printStackTrace(); }
game.loadBackground();
for(int i = 0; i < size; i++) {
if(list[i].getY() >= game.getFrameY())
list[i].reset();
else
list[i].update();
}
}
}
}

Thank you all for your help!
I used Gilbert Le Blanc's method to get this to work.
I removed the JComponent off of the Ornament class and instead created a class that inherits JPanel
ublic class GamePanel extends JPanel{
private Ornament[] objectList;
......
public GamePanel(GUI game) {
....
MouseListener ml = new MouseListener();
addMouseListener(ml);
this.setBounds(0, 0, 500, 500);
}
I then added a paint component and rendered each Ornament in the objectList array using a for loop
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
.....
for(int i = 0; i < objectList.length; i++) {
Color c = new Color(objectList[i].r, objectList[i].g, objectList[i].b);
g2.setColor(c);
g2.fillOval(objectList[i].getX(), objectList[i].getY(), 50, 50);
}
}
}
Furthemore I added the mouselistener to the JPanel component. Using the same technique for mouse to object collision and it worked!
class MouseListener extends MouseAdapter
{
#Override
public void mousePressed(MouseEvent e)
{
if(!gameOver) {
for(int i = 0; i < objectList.length; i++) {
int distance = (int)(Math.sqrt(((e.getX() - objectList[i].getX()) * (e.getX() - objectList[i].getX())) + ((e.getY() - objectList[i].getY()) * (e.getY() - objectList[i].getY()))));
if(distance <= 50) {
game.incScore();
objectList[i].reset();
}
}
...
}
}
Shoutout to hfontanez and Gilbert Le Blanc for the help! :)

Related

How do I change the rectangles to an image?

I just started learning Java (OOP) and I am trying to develop a simple game (like space invaders). I want to replace the guards (brown rectangles) to an image to make the game more aesthetically pleasing.
I am not sure how to do this as the guards are dependent on a lot of things. I tried using the loadImage() method and others but it did not work.
Relevant codes:
Guard.java
public class Guard {
private List<Square> squares;
public Guard(int x, int y) {
squares=new ArrayList<>();
for(int i=0; i<3; i++) {
for (int j = 0; j < 6; j++) {
squares.add(new Square(x + SQUARE_SIZE * j, y + SQUARE_SIZE * i));
}
}
}
public void collisionWith(MovingObject obj) {
for(Square square : squares) {
if(square.visible && square.intersects(obj.getBoundary())) {
square.setVisible(false);
obj.die();
}
}
}
public void draw(Graphics g) {
for(Square square : squares)
{
if(square.visible) square.draw(g);
}
}
}
Square.java
class Square extends Rectangle {
boolean visible;
Square(int x, int y)
{
super(x, y, SQUARE_SIZE, SQUARE_SIZE);
setVisible(true);
}
void setVisible(boolean visible) {
this.visible = visible;
}
void draw(Graphics g) {
g.setColor(new Color(228, 155, 30));
g.fillRect(x, y, width, height);
}
}
Screenshot for reference: Instead of brown boxes, I want to change it to other things using images
The following mre demonstrates using an image to represent a rectangle. Note that for easier implementation Gurd extends Componenet:
import java.awt.*;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
public class SwingMain {
SwingMain() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.add(new Guard(0,0));
frame.pack();
frame.setVisible(true);
}
class Guard extends Component{
private static final int GAP = 3, W = 6, H = 3;
private int squareSize, totalX, totalY;
private final List<Square> squares;
public Guard(int x, int y) {
squares=new ArrayList<>();
totalY = 0;
Square square = null;
for(int i=0; i< H; i++) {
totalX = 0;
for (int j = 0; j < W; j++) {
square = new Square(x + totalX , y + totalY );
squares.add(square);
totalX += square.getWidth() +GAP;
}
totalY += square.getHeight() +GAP;
}
}
#Override
public void paint(Graphics g) {
super.paint(g);
for(Square square : squares) {
square.draw(g);
}
}
#Override
public Dimension getPreferredSize(){
return new Dimension(totalX, totalY);
}
}
class Square{
private static final String BOX =
"https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/128x128/Box_Orange.png";
private Image image = null;
private final int x,y;
Square(int x, int y) {
this.x=x; this.y = y;
try {
image = new ImageIcon(new URL(BOX)).getImage();
} catch (IOException ex) {
ex.printStackTrace();
}
}
void draw(Graphics g) {
g.drawImage(image, x,y, null);
}
int getWidth(){
return image.getWidth(null);
}
int getHeight(){
return image.getHeight(null);
}
}
public static void main(String[] args) {
new SwingMain();
}
}
Square is not efficient because it reads and constructs image over again. To make it more efficient you can introduce a static block to initialize image only once:
static class Square{
private static final String BOX =
"https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/128x128/Box_Orange.png";
private static Image image = null;
static{
try {
image = new ImageIcon(new URL(BOX)).getImage();
} catch (IOException ex) {
ex.printStackTrace();
}
}
private final int x,y;
Square(int x, int y) {
this.x=x; this.y = y;
}
void draw(Graphics g) {
g.drawImage(image, x,y, null);
}
int getWidth(){
return image.getWidth(null);
}
int getHeight(){
return image.getHeight(null);
}
}

Java: drawing multiple sprites in different threads at a time

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.

brick collision for breakout in java

I am working on a breakout game. i have almost everything done except for the collision for the bricks. so far, when the ball hits the brick the brick bounces back but the brick does not disappear. If someone could help me that would be great.
Main class:
public class Breakout extends Applet implements Runnable{
Thread thread = new Thread(this);
boolean running = true;
Brick2 b;
Paddle p;
Ball ba;
Image dbImage;
Graphics dbg;
public void init(){
setSize(800,600);
b = new Brick2();
p = new Paddle(this);
ba = new Ball(this);
}
public void start(){
thread.start();
}
public void destroy(){
running = false;
}
public void stop(){
running = false;
}
public void run(){
while(running){
b.update(ba);
p.update(this);
ba.update(this,p);
repaint();
try{
thread.sleep(20);
}
catch (InterruptedException e){
System.out.println("AN ERROR HAS OCCURED");
}
}
}
public void update(Graphics g){
dbImage = createImage(getWidth(),getHeight());
dbg = dbImage.getGraphics();
paint(dbg);
g.drawImage(dbImage,0,0,this);
}
public void paint(Graphics g){
g.fillRect(0,0,800,600);
b.paint(g);
p.paint(g,this);
ba.paint(g,this);
}
}
Brick Class:
public class Brick2{
private int x;
private int y;
public Brick2(){
}
public void update(Ball ba){
collision(ba);
}
public void collision(Ball ba){
int bX = ba.getX();
int bY = ba.getY();
int bHeight = ba.getImageHeight();
int bWidth = ba.getImageWidth();
for(int x=0; x <= 800; x+=55){
for (int y=0; y<= 100; y+=25){
if (bX-bWidth<=x && bX+bWidth>=x && bY-bHeight<=y && bY+bHeight>=y){
ba.setXVel(6);
}
}
}
}
public int getX(){
return x;
}
public int getY(){
return y;
}
public void paint(Graphics g){
for(int x=0; x <= 800; x+=55){
for (int y=0; y<= 100; y+=25){
g.setColor(Color.RED);
g.fillRect(x,y,50,20);
}
}
}
}
Ball Class:
public class Ball {
private int x=355 ;
private int y=200;
private int speed = 6;
private int xVel = -speed;
private int yVel = speed;
private boolean gameOver = false;
private Image ball;
public Ball (Breakout bR){
ball = bR.getImage(bR.getDocumentBase(),"ball.png");
}
public void update(Breakout bR, Paddle p){
x += xVel;
y += yVel;
if (x < 0){
xVel = speed;
}
else if (x > bR.getWidth()){
xVel = -speed;
}
if(y > bR.getHeight()){
gameOver = true;
}
else if (y < 0){
yVel = speed;
}
collision(p);
}
public void collision(Paddle p){
int pX = p.getX();
int pY = p.getY();
int pHeight = p.getImageHeight();
int pWidth = p.getImageWidth();
if (pX<=x && pX+pWidth>=x && pY-pHeight<=y && pY+pHeight>=y){
yVel = -speed;
}
}
public int getX(){
return x;
}
public int getY(){
return y;
}
public int getImageWidth(){
return ball.getWidth(null);
}
public int getImageHeight(){
return ball.getHeight(null);
}
public void setXVel(int xv){
yVel = xv;
}
public void paint (Graphics g, Breakout bR){
g.drawImage(ball,x,y,bR);
if (gameOver){
g.setColor(Color.WHITE);
g.drawString("Game Over", 100,300);
}
}
}
Thanks for your help.
Nothing seems to happen in class Ball's collision function if a collision is detected.
After
ba.setXVel(6);
You need to have some code to make the brick no longer paint. Try giving the brick a visibility attribute in the class definition:
private Boolean visible;
When in the collision function after
ba.setXVel(6);
Insert
setVisible(false);
You'll need to write a setter and getter for the visible attribute. Then in the paint function, set it only to paint if visible is true.
You'll need to have more than one Brick2 instance. Each Brick2 instance will represent one individual brick. Put them in a collection like List and step through them to draw them.
One way to create the list would be:
List<Brick2> bricks = new List<Brick2>;
for (i = 0; i > num_bricks; i++ ) {
bricks.add(new Brick2());
}
But you'll need to have a way to set the position of each brick. Perhaps based on the brick number(i) and use the brick constructor to derive it's position based on the order in which they're created.

bullets creation in a simple game

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.

Display animation with thread

I want to display many animation at the same time with thread.
But It doesn't work, the second animation start when the first end.
I'm using thread but probably the wrong way because i'm beginner
Here is my code :
public class Board extends JPanel{
Mouse mouse;
ArrayList<Explosion> explosions;
public Board() {
mouse = new Mouse(this);
explosions = new ArrayList();
setDoubleBuffered(true);
this.addMouseListener(mouse);
}
public void addExplosion(Explosion e) {
explosions.add(e);
new Thread(explosions.get(explosions.indexOf(e))).start();
}
public void removeExplosion(Explosion e) {
explosions.remove(e);
}
public void paint(Graphics g) {
super.paint(g);
for(int i=0; i<explosions.size(); i++) {
explosions.get(i).paintComponent(g);
}
Graphics2D g2d = (Graphics2D)g;
Toolkit.getDefaultToolkit().sync();
g.dispose();
}
}
public class Explosion extends JPanel implements Runnable{
private BufferedImage img;
final int width = 320;
final int height = 320;
final int rows = 5;
final int cols = 5;
private int x,y;
private int cursor;
BufferedImage[] sprites = new BufferedImage[rows * cols];
Board board;
public Explosion(Board board,int x, int y) {
this.board = board;
this.x = x;
this.y = y;
try {
try {
this.img = ImageIO.read(new File((this.getClass().getResource("files/explosion2.png")).toURI()));
} catch (URISyntaxException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
cursor = 0;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
sprites[(i * cols) + j] = img.getSubimage(
j * (width/rows),
i * (height/cols),
width/rows,
height/cols
);
}
}
}
public void run() {
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
cursor++;
}
};
Timer timer = new Timer(50, taskPerformer);
while(cursor < ((rows*cols)-1)) {
timer.start();
board.repaint();
}
timer.stop();
board.removeExplosion(this);
}
public void paintComponent(Graphics g){
Graphics2D g2d = (Graphics2D)g;
g2d.drawImage(sprites[cursor], x, y, this);
g.dispose();
}
}
Thanks for helping
Any code that updates the GUI should be run on the thread associated with the GUI. To do so from a different thread, like in your case, you will need to use SwingUtilities.InvokeLater:
SwingUtilities.InvokeLater(new Runnable() {
public void run() {
// code to update the GUI goes here
}
});

Categories

Resources