I am not sure what is incorrect - java

I am creating a small Java Jpanel game in which I am supposed to have a rocket that moves up and down via arrows and fires via space.
The firing method should work like this: Space bar pressed, thing fires and moves across screen , and then when it hits a certain x, it disappears. Also, you can only fire once until the other bullet disappears.
I do not know what I am doing wrong. For one, as soon as my code starts you can see a bullet flying across the screen.
2nd, the bullet is not disappearing.
3rd, even though the other bullet is still visible, it allows me to fire again.
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.File;
import java.io.IOException;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.swing.*;
#SuppressWarnings("serial")
public class SpaceGame extends JPanel implements ActionListener{
Timer t = new Timer(2, this);
private ImageIcon rocket,asteroid,bullet;
private JLabel rocketlabel,ast1,ast2,ast3,bulletLabel;
public static int y=90,dy=0,bulletX=110,bulletY,i=0,canFire;
//public sound sound;
static boolean bulletFired=false;;
static JFrame f = new JFrame();
SpaceGame(){
this.setBackground(Color.black);
rocket = new ImageIcon(getClass().getResource("rocketFinal.png"));
rocketlabel= new JLabel(rocket);
this.add(rocketlabel);
asteroid = new ImageIcon(getClass().getResource("asteroid.png"));
ast1=new JLabel(asteroid);
ast2=new JLabel(asteroid);
ast3=new JLabel(asteroid);
bullet = new ImageIcon(getClass().getResource("bulletReal.png"));
bulletLabel = new JLabel(bullet);
canFire=1;
bulletLabel.setVisible(false);
this.add(ast1);this.add(ast2);this.add(ast3);this.add(bulletLabel);
f.addKeyListener(new controller());
this.setLayout(null);
this.setVisible(true);
}
public class controller implements KeyListener{
#Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if(keyCode== KeyEvent.VK_UP) {
dy=-1;
}
if(keyCode== KeyEvent.VK_DOWN) {
dy=1;
}
if(keyCode== KeyEvent.VK_SPACE) {
if(canFire==0) {
System.out.println(String.valueOf(canFire));
bulletFired = true;
bulletY = y;
bulletX=110;
}canFire=1;
}
}
#Override
public void keyReleased(KeyEvent e) {
int key = e.getKeyCode();
switch(key) {
case KeyEvent.VK_UP: dy=0; break;
case KeyEvent.VK_DOWN: dy=0; break;
}
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
rocketlabel.setBounds(45,y,rocket.getIconWidth(),80);
fireBullet();
paintStars(g);
t.start();
}
public void paintStars(Graphics g) {
g.setColor(Color.yellow);
for(int i=0; i<5;i++) {
Random rand = new Random();
int o = rand.nextInt(500);
int p = rand.nextInt(300);
g.fillOval(o, p, 3, 3);
}
}
public void actionPerformed(ActionEvent e) {
if(y==-20) y=249;
if(y==250)y=-20;
y+=dy;
if(bulletFired=true) {
bulletX++;
if(bulletX==455)bulletFired=false;bulletLabel.setVisible(false);System.out.println(String.valueOf(bulletX)); canFire=0;
}
repaint();
}
public void fireBullet(){
if(bulletFired=true) {
bulletLabel.setVisible(true);
bulletLabel.setBounds(bulletX,bulletY+25,bullet.getIconHeight(),bullet.getIconWidth());
}
}
public static void main(String[] args) {
String filepath = "SpaceGameMusic.wav";
musicStuff musicPlayer = new musicStuff();
musicPlayer.playMusic(filepath);
SpaceGame t = new SpaceGame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(t);
f.setSize(500,335);
f.setVisible(true);
f.setResizable(false);
}
}

For one, as soon as my code starts you can see a bullet flying across the screen.
The paintComponent() method is for painting only. You can't control when Swing will determine a component needs to be repainted.
So, for example:
t.start();
should NOT be in the painting method. As soon as the frame is made visible the panel will be painted and the Timer will be started.
You application code should control when the Timer is started.
Other issues:
you should not be using static variables. The variable should simply be instances of your class.
the paintStars() method should not generate random locations. Again. a painting method should only paint the current state of the class. So if you want to change the location of the stars you should have a method like randomizeStars(). In this method you would update an ArrayList of Point objects. Each Point instance would represent the location of a star. Then the paintStars() method would simply iterate through the ArrayList and paint each star.
you should not be using a KeyListener. A KeyListener only works if a component has focus. You can't guarantee that your component will lose focus. Instead you should be using Key Bindings. Key bindings allow you to handle a KeyEvent even if the component doesn't have focus. See Motion Using the Keyboard for more information and a working example.
you can only fire once until the other bullet disappears
Your canFire variable should be a boolean variable so it only has true/false values. Again you have a method that sets the state. Your game logic will then check the state before firing the bullet again.
if(y==-20) y=249;
if(y==250)y=-20;
Don't hardcode values. The number should be based on the size of your panel. So you use methods like getWidth() and getHeight() to determine the current size of the panel.

The problem was quite simply that I had forgotten to use == in my if(boolean) statements.

Related

KeyListener is not interacting correctly with rest of program

Whenever I press space or any other key I try to configure it does not change the "pixelsizemodifier" variable. I can't even get it to print to the console. I do not know where I am going wrong. Runs perfect besides that.
Sorry in advance for my crappy code. I was experimenting from memory after not writing for a while.
What I want it to do is to take a keyevent to change variables such as the pixel sizing or later things like pixel density or color.
package square;
import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class main extends JPanel implements KeyListener {
Random rand;
int randomInt, boxQuantity, lineCount, ovalCount;
static JFrame frame;
static JPanel p;
boolean painting = false;
int re,gr,bl;
Color color;
int pixelsize , pixelsizemodifier = 10, pixeldistance;
public static void main(String[] args) {
frame = new JFrame("Welcome!");
frame.getContentPane().add(new main());
frame.setSize(800,600);
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.setResizable(true);
frame.setVisible(true);
p = new JPanel();
p.setVisible(true);
frame.add(p);
new main().go();
}
public void go() {
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
}
public void paint(Graphics g) {
super.paint(g);
rand = new Random();
boxQuantity = 0;
ovalCount = 0;
for (int x = 0; x < frame.getWidth()/10; x++) {
for (int y = 0; y < frame.getHeight()/10; y++) {
re = rand.nextInt(255);
gr = rand.nextInt(255);
bl = rand.nextInt(255);
color = new Color(re , gr , bl);
g.setColor(color);
randomInt = rand.nextInt(4);
pixelsize = rand.nextInt(pixelsizemodifier);
pixeldistance = rand.nextInt(11);
switch (randomInt) {
case 0:
g.fillRect(x*10, y*pixeldistance, pixelsize, pixelsize);
break;
case 1:
g.fillRect(x*10, y*pixeldistance, pixelsize, pixelsize);
break;
case 2:
g.fillRect(x*10, y*pixeldistance, pixelsize, pixelsize);
break;
case 3:
g.fillRect(x*10, y*pixeldistance, pixelsize, pixelsize);
break;
}
boxQuantity++;
}
}
System.out.println("width: " + frame.getWidth()/10 + " / bq: " + boxQuantity + " / psm: " + pixelsizemodifier);
repaint();
}
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
pixelsizemodifier++;
}
}
#Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
pixelsizemodifier++;
}
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
}
Many issues:
class names should start with an upper case character
class names should be descriptive. "main" does not follow either of these practices.
Don't use static variables. All the variables in your class should be instance variables. Instance variables can be accesses by any method in your class
There is no need to define a "frame" variable. You class does not need to know the frame it is contained in.
Custom painting is done by overriding paintComponent(...), not paint().
Custom painting should only paint the current state of the component. It should NOT change the state. Therefore you should NOT be generating "random" values in the painting method. Instead you have a method that generates a random value that is added to a 2D array. Then the painting method iterates through the array to paint the representation of the value
Never invoke repaint() in a painting method. That is NOT how animation is done. Instead you use a Swing Timer to schedule the animation. When the Timer fires you invoke the method from above that generates your random values. That method will then invoke repaint.
You should not be using a KeyListener to listen for a KeyEvent. Swing was designed to be used with Key Bindings
The Swing Tutorial has sections on:
Custom Painting
How to Use Swing Timers
How to Use Key Bindings.
Read the tutorial for examples on each of the above topics.
Whenever I press space or any other key I try to configure it does not change the "pixelsizemodifier" variable
Finally, the cause of your problem:
frame.getContentPane().add(new main());
...
new main().go();
You are creating two instances of your custom class. Don't.
You only need one instance. That instance needs to be added to the frame and you need to invoke go() on that instance:
You should invoke the go() method in the constructor of your class.

How to move pong paddle with java.Swing?

I'm trying to make Pong with Java and Swing. However, I have two problems - one, the rectangle on-screen doesn't move at all, and two, a NullPointerException is happening even though the code still runs. Here are my two files:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Pong extends JFrame implements KeyListener {
private static final long serialVersionUID = -5782301423436L;
JPanel panel;
Paddle paddle1;
public Pong() {
super("Pong");
panel = new JPanel();
this.add(panel);
super.setPreferredSize(new Dimension(800, 600));
super.setVisible(true);
super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
super.pack();
addKeyListener(this);
paddle1 = new Paddle(100, 300);
}
public static void main(String[] args) {
#SuppressWarnings("unused")
Pong game = new Pong();
}
#Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.WHITE);
g.fillRect(0, 0, 800, 600);
g.setColor(Color.BLACK);
this.paddle1.draw(g);
}
public void keyTyped(KeyEvent e) {}
public void keyReleased(KeyEvent e) {}
// I've tested this part and the w and s key presses
// are still detected, but nothing happens. Why not?
public void keyPressed(KeyEvent keyEvent) {
int key = keyEvent.getKeyCode();
if (key == KeyEvent.VK_ESCAPE) {
this.dispatchEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING));
} else if (key == KeyEvent.VK_W) {
this.paddle1.up();
} else if (key == KeyEvent.VK_S) {
this.paddle1.down();
}
}
}
import java.awt.Graphics;
public class Paddle {
// Position of the paddle's center
int x;
int y;
public Paddle(int x, int y) {
this.x = x;
this.y = y;
}
public void draw(Graphics g) {
g.fillRect(this.x - 10, this.y - 40, 20, 80);
}
// Up is negative because Java coordinates
public void up() {
this.y -= 30;
}
public void down() {
this.y += 30;
}
}
I've tested the keyPressed method in the Pong file, and when the w and s keys are pressed, it does, in fact, detect them (it will print a line out to the console). I'm very new to graphics in Java, and this is the first thing I've tried creating that's not copy-pasted from a tutorial on YouTube. I've been looking at my code for about an hour and can't find where the error could possibly be. Any and all help would be greatly appreciated.
I've checked the code and can help you solve the errors.
First, here you've shown the Paddle using paint() method of the JFrame Pong. You're detecting the 'W' and 'S' keys and according to the key you've written the code to update the y co-ordinate in the Paddle class.
But, the problem here is, you're just updating the y co-ordinate. You're not drawing the paddle with new co-ordinates again. So, after setting the new y value, you should draw the paddle again.
So, in the keyPressed() method, after
this.paddle1.up();
and
this.paddle1.down();
you should again call the draw() method of the Paddle class that will draw the Paddle at the modified location.
In this draw() method you're passing a Graphics object which is the graphics configuration of the current JFrame Pong. So, this can be get using getGraphics() method of the JFrame.
So, the line to add after up() or down() will be
this.paddle1.draw(getGraphics());
Since we are dealing with graphics, it is also the proper thing to repaint the JFrame once you change something in its graphics. If you have added only the above line after up() and down() method in the keyPressed() method, you will see the Paddle is not moving. Its previous drawing will be as it is and as per the key pressed, new Paddle will be drawn upwards or downwards. So, instead of a moving paddle you will see a vertical bar. This is happening because we are modifying the graphics but not painting the JFrame again. So, it will just add the new rectangle in the JFrame keeping previous as it is.
So, to get rid of this, you should use repaint() method of the JFrame so that it draws only the latest screen.
So, the complete keyPressed() method would be like :
#Override
public void keyPressed(KeyEvent keyEvent) {
int key = keyEvent.getKeyCode();
if (key == KeyEvent.VK_ESCAPE) {
this.dispatchEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING));
} else if (key == KeyEvent.VK_W) {
this.paddle1.up();
this.paddle1.draw(getGraphics());
repaint();
} else if (key == KeyEvent.VK_S) {
this.paddle1.down();
this.paddle1.draw(getGraphics());
repaint();
}
}
So, now after detecting correct keys, it is drawing the paddle again and
after that discards the previous ones as well so that while you will run this code, you will be able to see the paddle is moving as per the key pressed.
Now, about the NullPointerException. I tried to run this code but I could not reproduce such exception. So, after trying the suggested code, if you encounter NPE, you can update the stack trace to get it solved.
I have read the comments and they are very useful. Do check the link that explains better way for listening to the key events.
Thank you.

Java: Repaint in Swing Not Working

I am learning java swing and am having trouble with the following program. It creates a small frame with a quit button at top. The objective is to display coordinates wherever the mouse is clicked. When I click the mouse 2 unwanted things are happening:
the quit button is overridden by the mouse clicks and it no longer responds (instead of responding to event and quitting, it displays coordinates on top of the quit button).
when I click at a new location, the coordinates from the old location persist.
I used removeAll() and revalidate() before doing repaint() based on this discussion but that has not helped. This code is taken from here and the code to says to research online documentation for why this is happening.
Any pointers?
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.Color;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.BorderLayout;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JLabel;
public class QuitCoordinateTest {
public static void main(String[] args){
GUI gui = new GUI();
}
}
class MyFrame extends JFrame implements ActionListener{
int clickX;
int clickY;
public void paint(Graphics g){
g.drawString("" + clickX + ", " + clickY, clickX, clickY);
}
public void actionPerformed(ActionEvent e){
System.exit(0);
}
}
//=======================================================//
class GUI extends MyFrame {
JButton quitButton = new JButton("Quit");
public GUI(){
MyFrame displayWindow = new MyFrame();
displayWindow.setTitle("Title");
/*
JPanel buttonPanel = new JPanel();
buttonPanel.add(quitButton);
displayWindow.getContentPane().add(buttonPanel,BorderLayout.NORTH);
JPanel textPanel = new JPanel();
*/
displayWindow.getContentPane().add(quitButton,BorderLayout.NORTH);
quitButton.addActionListener(displayWindow);
displayWindow.setSize(201,201);
displayWindow.setVisible(true);
// displayWindow.pack();
displayWindow.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
displayWindow.addMouseListener(new MouseProc(displayWindow));
}//end constructor
}//end class GUI definition
//=======================================================//
//This listener class monitors for mouse presses and
// displays the coordinates of the mouse pointer when the
// mouse is pressed on the source object.
class MouseProc extends MouseAdapter{
MyFrame refToWin;
MouseProc(MyFrame inWin){
refToWin = inWin;
}
//Override the mousePressed method to determine and
// display the coordinates when the mouse is pressed.
public void mousePressed(MouseEvent e){
refToWin.removeAll();
refToWin.clickX = e.getX();
refToWin.clickY = e.getY();
//Force the JFrame object to be repainted in order to
// display the coordinate information.
refToWin.removeAll();
refToWin.validate();
refToWin.repaint();
}
}
repaint() is working fine.
Avoid drawing directly on the JFrame.
Instead draw in the protected void paintComponent(Graphics g) method override of a JPanel that is then displayed in your JFrame.
Be sure to call the super's paintComponent(g) method inside of your paintComponent override -- this will erase the old images and is the reason for one of your problems.
Use reasonable comments in your code. Too many comments and too much text distracts and makes understanding your code harder, not easier.
Calling removeAll() on your JFrame will do just that -- remove all components including your button. Why are you calling this? Are you sure that you want to call this method?
A minor nitpick -- you'll want to avoid directly setting the fields of another class, such as your clickX and clickY fields. Instead, make them private, and only allow outside classes to modify them through public methods. While it may not matter much for this small program, it will matter greatly when you start scaling up your programming and create large programs with complex interactions. The key to success here will be to limit and control all communication between classes to avoid hard to detect side effects.
For example, something like...
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
String str = String.format("[%d, %d]", clickX, clickY);
g.drawString(str, clickX, clickY);
}
public int getClickX() {
return clickX;
}
public void setClickX(int clickX) {
this.clickX = clickX;
}
public int getClickY() {
return clickY;
}
public void setClickY(int clickY) {
this.clickY = clickY;
}
For example
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class DetectClicks extends JPanel {
private static final int PREF_W = 800;
private static final int PREF_H = 650;
private int clickX;
private int clickY;
public DetectClicks() {
MyMouseListener mouseAdapter = new MyMouseListener(this);
addMouseListener(mouseAdapter);
addMouseMotionListener(mouseAdapter); // to allow dragging!
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
String str = String.format("[%d, %d]", clickX, clickY);
g.drawString(str, clickX, clickY);
}
public int getClickX() {
return clickX;
}
public void setClickX(int clickX) {
this.clickX = clickX;
}
public int getClickY() {
return clickY;
}
public void setClickY(int clickY) {
this.clickY = clickY;
}
private static void createAndShowGui() {
DetectClicks mainPanel = new DetectClicks();
JFrame frame = new JFrame("DetectClicks");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class MyMouseListener extends MouseAdapter {
private DetectClicks detectClicks;
public MyMouseListener(DetectClicks detectClicks) {
this.detectClicks = detectClicks;
}
#Override
public void mousePressed(MouseEvent evt) {
showPoint(evt);
}
#Override
public void mouseDragged(MouseEvent evt) {
showPoint(evt);
}
private void showPoint(MouseEvent evt) {
detectClicks.setClickX(evt.getX());
detectClicks.setClickY(evt.getY());
detectClicks.repaint();
}
}
Your event is getting consumed by the handler that prints the coordinates, you need to redispatch the event so that the button can see it. You can do it like this, inside the coordinate display event handler:
Component c = e.getComponent();
c.getParent().dispatchEvent( e );
Also, I'd be tempted to use the glass pane of the frame, and put a JLabel on it with the co-ordinates rather than messing with the paint method.
you don't have to use any of repaint(),invalidate() etc.
i highly recommend to use
SwingUtilities.invokeLater(new Runnable() {
public void run() {
//TODO udpdate UI compontents, layouts etc.
}
});
this guarantees that UI components update on real time. Because we don't know when the system update UI hierarchy so we can't force it. This allow system to determine by it's self.

How to delete an JPanel Object?

Im on to create a little "game", something like an 2d AirForce Shooter.
So, i have a problem with deleting unused enemys.
An Enemy is an simple JPanel, which is saved in the main logic as an array List.
public static ArrayList<Enemy> enemys = new ArrayList<Enemy>();
The Enemy run logic does the following:
while(!destroyed){
if(Game.running){
x--;
if(getBounds().intersects(Field.player.getBounding())){
Player.death = true;
}
if(x < 0){
Field.deleteEnemy(this);
}
setBounds((int) x, (int) y, 100, 50);
try{Thread.sleep(10);}catch(InterruptedException e){}
}
}
So you can seem there i already tried to call the method deleteEnemy, and just give it the unused Enemy.
But it isnt possible - when i just do this:
public static void deleteEnemy(Enemy e){
System.out.println("test");
enemys.remove(e);
}
It will be just removed from the list, but coninues existing on the Main JPanel.
And i cannot say
remove(e);
Because then i try to call a non static function in a static.
So, how could i delete an Enemy? Someone knows?
Thanks for help!
The hole code: (Game.java)
And, Enemy.java:
package Game;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class Field extends JPanel implements Runnable{
public static Player player = new Player();
public static ArrayList<Enemy> enemys = new ArrayList<Enemy>();
private Thread moveBackground = new Thread(this);
private boolean bgMoving = false;
public static boolean addMob = false;
private int x = 0;
private int bgSpeed = -1;
public Field(){
setBounds(0, 0, 800, 600);
setFocusable(true);
setLayout(null);
addKeyListener(new Handler());
add(player);
}
public void paintComponent(Graphics g){
Field.super.paintComponent(g);
g.drawImage(Images.images[0], x, 0, this);
}
public static void deleteEnemy(Enemy e){
System.out.println("test");
enemys.remove(e);
}
public void run(){
while(!Player.death){
if(bgMoving){
bgMoving = true;
x += bgSpeed;
if(x < -(Images.images[0].getWidth(this) - this.getWidth() - 20)){
bgMoving = false;
}
repaint();
try { Thread.sleep(20); } catch (InterruptedException e) {}
}
if(addMob){
enemys.add(new Enemy());
add(enemys.get(enemys.size() - 1));
addMob = false;
}
}
JOptionPane.showMessageDialog(null, "DIED!");
}
public class Handler extends KeyAdapter {
public void keyPressed(KeyEvent e) {
player.KeyPressed(e);
if(!bgMoving){
if(Game.running){
bgMoving = true;
if(moveBackground.getState().toString() == "NEW"){
moveBackground.start();
}
}
}
}
public void keyReleased(KeyEvent e) {
player.KeyReleased(e);
}
}
}
And, Enemy.java:
package Game;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class Enemy extends JPanel implements Runnable{
Thread t = new Thread(this);
private double x = Game.width();
private double y = Math.random() * Game.height();
private double xF = 0, yF = 0;
private boolean destroyed = false;
public Enemy(){
setBounds((int) x, (int) y, 100, 50);
setOpaque(false);
t.start();
}
public void paintComponent(Graphics g){
Enemy.super.paintComponent(g);
g.setColor(Color.GREEN);
g.drawImage(Images.images[2], 0, 0, this);
}
public void run() {
while(!destroyed){
if(Game.running){
x--;
if(getBounds().intersects(Field.player.getBounding())){
Player.death = true;
}
if(x < 0){
Field.deleteEnemy(this);
}
setBounds((int) x, (int) y, 100, 50);
try{Thread.sleep(10);}catch(InterruptedException e){}
}
}
}
}
After removing you will need to call revalidate() and repaint()
[Too long for a comment]
I think the problem is in your logic on removing an Enemy/JPanel:
You are removing it from the ArrayList only, what about the containing JPanel/JFrame you added it to?
You must remove the JPanel from its container (maybe another JPanel or the JFrame) not just the ArrayList via Component#remove(Component c).
If you drew the Enemy images directly in paintComponent(...) of your container via iterating the ArrayList; removing it from the ArrayList would be sufficient, as it will no longer be in the Array and thus no longer drawn on the next repaint().
+1 to #Optional, you may need to call revalidate() and repaint() on the container for the affects of the removed JPanel/Enemy to be shown.
Also as #darijan mentioned, the use of static variables along with instance is not really a great design (though for certain designs this may be fine).
In your case if you need access to an instance method of another class, within another class, simply pass the instance of the class whos method you would like to access to the object which will access it.
Here is some psuedo code expressing much of the above mentioned problems / solutions:
public class Field extends JPanel {
private ArrayList<Enemy> enemies;
public Field() {
...
enemies.add(new Enemy(this));//create a new enemy and pas it the JPanel instance so it may access instance methods of this class
}
//ONLY USED IF JPanel for Enemy is ommited and Enemy class created which represents Enemy object and not Enemy object and aJPanel
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
ArrayList<Enemy> enemiesClone = new ArrayList<>(enemies);//copy array into another so we don't get a ConcurrentModificaton exception if removeEnemy is called while iterating the list
if(!enemiesClone.isEmpty())
for(Enemy e:enemiesClone) {//iterate through array of images
draw(e.getImage(),e.getX(),e.getY(),this);
}
}
public void removeEnemy(Enemy e) {
enemies.remove(e);//remove from the array
//ONLY USED IF JPanels are used as Enemy
remove(e);//remove from the JPanel
//so the changes of removed panel can be visible seen
revalidate();
repaint();
}
}
class Enemy extends JPanel //extends JPanel should be ommited for paintComponent method of drawing an enemy onscreen
{
private int x,y;
private BufferedImage image;
private Field f;
public Enemy(Field f) {//constructor accepts Field instance to access instance method for the class
this.f=f;
}
public void update() {
if(offscreen||dead) {
f.removeEnemy(this);//call removeEnemy which is an instance method of Field
}
}
//BELOW METHODS ONLY USED WHEN Enemy represents object and not a JPanel which can draw its image itself (and update position by simply changing co-ordinates)
public BufferedImage getImage() {
return image;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
For a more detailed look check Game Development Loop, Logic and Collision detection Java Swing 2D I made which will give you the basics needed for most 2D games. However I do not use JPanels rather draw directly to a container.
Where do you add an Enemy to JPanel?
Basically, you should call remove on Field JPanel:
public void deleteEnemy(Enemy e){
System.out.println("test");
enemys.remove(e);
this.remove(e);
}
The method should not be static.

Manual Positioning of Image in JFrame

I am trying to create a JFrame that displays an image from a file path onto a particular position on the JFrame. At a later time (when a button is clicked), I want the image to move positions, say, 50 pixles to the left. If a layout manager is necessary, I want to use the null layout, as this is a project for myself and I am not quite ready to learn how to write my own layout manager.
So far, I have managed to display a BufferedImage in a frame, but I do not know how to specify its position.
Is using a BufferedImage even the correct approach? What is the best way to go about doing this?
Update: I tried to follow your suggestion of using mouselistener and it resulted in this:
class ImgComponent extends JComponent implements ChangeListener, MouseListener {
MovableImage mi;
public ImgComponent(MovableImage mi) {
this.mi = mi;
mi.addListener(this);
mi.addListener1(this);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(mi.i, mi.getX(), mi.getY(), null);
}
#Override
public void stateChanged(ChangeEvent e) {
repaint();
}
#Override
public void mouseClicked(MouseEvent e) {
mi.setPos(100, 100);
System.out.println("yay");
}
}
But unfortinely, the mouseClicked event never triggers. I just want that damn image to move, lol.
Here's a complete example that uses the model/view/controller pattern. (Just dump all snippets after each other in a single .java file.)
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.*;
import java.util.List;
import javax.swing.*;
import javax.swing.event.*;
// A class encapsulating an image and a x-coordinate (a "model")
class MovableImage {
Image i = new ImageIcon("duke.png").getImage();
private int x = 0;
// Observers that are interested in movements.
List<ChangeListener> listeners = new ArrayList<ChangeListener>();
public void addListener(ChangeListener cl) {
listeners.add(cl);
}
public int getX() {
return x;
}
public void incrementX() {
x += 10;
// Notify those interested.
for (ChangeListener cl : listeners)
cl.stateChanged(null);
}
}
// A graphical component displaying the model.
// Object of this class are interested in movement because when the image moves,
// this component needs to be repainted.
class ImgComponent extends JComponent implements ChangeListener {
// The movable image to present.
MovableImage mi;
public ImgComponent(MovableImage mi) {
this.mi = mi;
mi.addListener(this);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(mi.i, mi.getX(), 10, null);
}
// This method is called from MovableImage when the position changes.
#Override
public void stateChanged(ChangeEvent e) {
repaint();
}
}
// Main class.
public class FrameTestBase extends JFrame {
public static void main(String args[]) {
// Create the "model".
final MovableImage mi = new MovableImage();
FrameTestBase t = new FrameTestBase();
t.setLayout(new BorderLayout());
// Add a component presenting the model.
t.add(new ImgComponent(mi), BorderLayout.CENTER);
// Create a button which increments x when clicked on.
t.add(new JButton(new AbstractAction("Move right") {
#Override
public void actionPerformed(ActionEvent e) {
mi.incrementX();
}
}), BorderLayout.SOUTH);
// Show it.
t.setDefaultCloseOperation(EXIT_ON_CLOSE);
t.setSize(400, 400);
t.setVisible(true);
}
}
Regarding your edit:
You need to add the mouse listener as well. In the constructor:
public ImgComponent(MovableImage mi) {
this.mi = mi;
mi.addListener(this);
mi.addListener1(this);
}
add the following line at the bottom:
addMouseListener(this);

Categories