I am getting a error that looks like this,
run:
Exception in thread "main" java.lang.ExceptionInInitializerError
at ao.Game.main(Game.java:11)
Caused by: java.lang.RuntimeException: Uncompilable source code -
ao.Panel is not abstract and does not override abstract method
keyReleased(java.awt.event.KeyEvent) in java.awt.event.KeyListener
at ao.Panel.<clinit>(Panel.java:15)
... 1 more
Java Result: 1
BUILD SUCCESSFUL (total time: 3 seconds)
I can't figure out what the problem with the public class is.
There are 2 separate files below.
Game.java
Panel.java
First File:
package ao;
import ao.Panel;
import javax.swing.JFrame;
public class Game {
public static void main(String[] args) {
JFrame frame = new JFrame("2D Shooter Pre-Alpha");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(new Panel());
frame.pack();
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Next File:
package ao;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import javax.swing.JPanel;
/**
*
* #author andyoppenheimer
*/
public class Panel extends JPanel implements Runnable, KeyListener {
// panel dimensions
public static final int WIDTH = 320;
public static final int HEIGHT = 240;
public static final int SCALE = 2;
// main loop
private Thread thread;
private boolean running = false;
private int fps = 60;
private long targetTime = 1000 / fps;
// drawing
private Graphics2D g;
private BufferedImage image;
public Panel() {
setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
setFocusable(true);
requestFocus();
}
public void addNotify() {
super.addNotify();
if (thread == null) {
running = true;
addKeyListener(this);
thread = new Thread(this);
thread.start();
}
}
public void init() {
image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
g = (Graphics2D) image.getGraphics();
}
public void update() {
}
public void draw() {
g.clearRect(0, 0, WIDTH, HEIGHT);
}
public void drawToScreen() {
Graphics g2 = getGraphics();
g2.drawImage(image, 0, 0, WIDTH * SCALE, HEIGHT * SCALE, null);
g2.dispose();
}
public void run() {
init();
long start;
long elapsed;
long wait;
while (running == true) {
start = System.nanoTime();
update();
draw();
drawToScreen();
elapsed = System.nanoTime() - start;
wait = targetTime - elapsed / 1000000;
if (wait < 0) {
wait = 5;
}
try {
Thread.sleep(wait);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void KeyPressed(KeyEvent k) {
}
public void KeyReleased(KeyEvent k) {
}
public void KeyTyped(KeyEvent k) {
}
}
In java methods starts with lower case keyTyped keyReleased and keyPressed so you are not overriding KeyListener methods.
You can annotate a method with #Override
this causes a compile error if it doesn't actually override. Section 9.6.1.4 of the JLS says:
The annotation type Override supports early detection of such problems. If a method declaration is annotated with the annotation #Override, but the method does not in fact override any method declared in a superclass, a compile-time error will occur.
Your class definition will lead to possible potential bugs cause
public class Panel extends JPanel implements Runnable, KeyListener
Calling Panel it's confusing cause already exists java.awt.Panel so call it different. Implementing multiple interface like that brokes Single Responsability Principle . A possible solution is to make inner classes or anonymous classes. For sure if you don't override a method is not necessary to extends JPanel. Take care that if you use KeyListener components must be in focus and be focusable and bind it to all keys, instead you can use KeyBindings. Don't use requestFocus instead use requestFocusInWindow() if you read api it says it's discouraged.
The class implements the KeyListener interface but does not provide an implementation for the keyReleased, keyPressed and keyTyped methods specified on the interface. Instead it provides implementations for: KeyReleased, KeyPressed and KeyTyped which are not properly cased.
Related
I testing to implement graphics into MVC structure but Im a bit stuck. Here is what I got so far. For now I just want to get the red ball to bounce back and forth. And use the button start to start the thread and button stop to stop the thread that runs the GameLoop in the controller.
But I think Im mixing this up a bit. Would very much appreciate some feedback!
Heres what I got so far:
GameModell
suppose to controll the bouncing. If the location of the ball is under 40 px or above 80 px - multiply the locationX with -1 to make the ball change direction
GameView
Here Im putting the labels on a JFrame. I also want to display the buttons start and stop to controll the thread but I guess they are hidden by the JPanel in TheGraphics class
GameController
Starts and stops the thread with ActionListeners. Contains the GameLoop
TheGraphics
Paints the ball and controll the direction
I guess I got a lot of thing that are all wrong but this is the best I can do at the moment. Would very much apreciate some help!
Thanks!
MAIN:
public class MVCgame {
public static void main(String[] args) {
GameModel gm = new GameModel();
GameView gv = new GameView();
GameController gc = new GameController(gm, gv);
}
}
MODEL:
public class GameModel {
private int multi = 1;
public void setMulti(int locX) {
if(locX < 40 || locX > 80) {
multi = multi * -1;
}
}
public int multi() {
return multi;
}
}
VIEW:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GameView extends JFrame {
private JPanel jp = new JPanel();
private JButton start = new JButton("Start");
private JButton stop = new JButton("Stop");
TheGraphics gr = new TheGraphics();
public GameView() {
add(jp);
add(gr);
jp.add(start);
jp.add(stop);
setSize(250, 250);
setVisible(true);
}
public void addListener(ActionListener theListener) {
start.addActionListener(theListener);
stop.addActionListener(theListener);
}
public JButton getStart() {
return start;
}
public JButton getStop() {
return stop;
}
// GUESS I SHOULD PUT THIS IN THE VIEW???
public void paintEllipse(Graphics theG) {
Graphics2D g2d = (Graphics2D) theG;
g2d.setColor(new Color(255, 0, 0));
g2d.fillOval(0, 0, 10, 10);
}
}
CONTROLLER:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class GameController implements Runnable {
GameView gv;
GameModel gm;
private Thread thread;
private boolean running = false;
public GameController(GameModel gm, GameView gv) {
this.gv = gv;
this.gm = gm;
gv.addListener(theListener);
start();
}
ActionListener theListener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (e.getSource() == gv.getStart()) {
start();
System.out.println("PLAY = ");
} else if (e.getSource() == gv.getStop()) {
stop();
System.out.println("STOP = ");
}
}
};
public synchronized void start() {
thread = new Thread(this);
thread.start();
running = true;
}
public synchronized void stop() {
thread.interrupt();
running = false;
}
// GameLoop
public void run() {
long lastTime = System.nanoTime();
double amountOfTicks = 60.0;
double ns = 1000000000 / amountOfTicks;
double delta = 0;
long timer = System.currentTimeMillis();
int frames = 0;
while (running) {
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
while (delta >= 10) {
// tick();
delta--;
// repainting the graphics
gv.gr.drawer();
gm.setMulti(gv.gr.drawer());
System.out.println("gv.gr.drawer() = " + gv.gr.drawer() + " gm.multi() " + gm.multi());
// I want to use this value in the model to change the direction
}
if (running) {
}
frames++;
if (System.currentTimeMillis() - timer > 1000) {
timer += 1000;
System.out.println("FPS: " + frames);
frames = 0;
}
}
}
}
THE GRAPHICS:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;
public class TheGraphics extends JPanel {
private int locX = 40;
public TheGraphics() {
}
public int drawer() {
locX++;
repaint();
return locX;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(new Color(255, 0, 0));
g2d.fillOval(locX, 30, 10, 10);
}
}
GameModell suppose to controll the bouncing. If the location of the ball is under 40 px or above 80 px - multiply the locationX with -1 to make the ball change direction
public void setMulti(int locX) {
if(locX < 40 || locX > 80) {
multi = multi * -1;
}
}
Really bad idea. You should always check position and direction (sign(speed)). Otherwise, your object might get stuck out of bounds always changing direction without moving from place forever.
Apart from this, using the MVC concept is overkill in my eyes and shouldn't be used in such a small project nor in a game. In a game, you should more or less put all three together. Of course you can, but the advantages and disadvantages of the MVC concept don't fit the needs of a game in many ways (except for the GUI, perhaps).
Your main loop might look something like this (you kind of did this already, but why is the tick() commented out in your code?):
while (running) {
update(); // Update all game objects
paint(); // Paint them all
}
Each game object will have its own update() and paint() implementation. You absolutely need to separate the logic of update and paint, even if they are in the same class. So this one:
public int drawer() {
locX++;
repaint();
return locX;
}
is an absolute no-go.
Edit: (Referring your update answer)
You are using the method location() for different purposes. According to the Java name convention, you should rename it getLocation() and setLocation() depending on the use to clarify the code.
(Even if this is not really MVC anymore, I'd let GameFrame implement ActionListener instead of specifying it as variable of GameController.)
One thing you should really change is this one:
private int locX = 0;
public void location(int loc) {
this.locX = (int) loc;
}
Basically, you are duplicating the location value every frame and create unused redundant data. Another problem is, that this might work fine for only one variable, but what if you add more than the position to your model later on? Instead TheGraphics has to render on an instance of the data model, not its values. As long you are using one GameModel
private GameModel model; // set value once at initialisation
and rendering its values in paintComponent will work fine, but if you want to add more than one GameModel (handling GameModel more like a GameObjectModel), you will need to pass it as parameter in the paint method.
public void update() {
repaint();
}
Remove it and try getting around without. A method called from one place forwarding to a different method is a bad idea most of the time, especially if it obfuscates the functionality with a different name.
gv.gr.update();
gv.gr.location(gm.location());
You are first repainting your image and then setting the location? Basically, your game runs one frame behind all the time. Swap that order.
gv.gr.location(gm.location());
gv.gr.repaint();
Will be fine (I already said about location()).
I've been taking AP Computer Science this year as a sophomore in high school and we mainly cover material like loops, classes, methods, general CS logic, and some math stuff. I am missing what I really loved about coding in the first place, making games. Now every game I have made had some sort of way to manage it whether it was using timers in visual basic or a XNA plugin for c# that setup a update method for me. The problem is I have not learned how to do this for java in my course. I've read up a little on threads and implements runnable but i'm not really sure where I'm going with it.
Class 1
import java.awt.FlowLayout;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class GFXScreen extends JFrame
{
/**
* #param screenHeigth
* #param screenHeigth
* #param The file name of the image. Make sure to include the extension type also
* #param The title at the top of the running screen
* #param The height of the screen
* #param The width of the screen
*/
public GFXScreen(String fileName, String screenTitle, int screenHeight, int screenWidth)
{
setLayout(new FlowLayout());
image1 = new ImageIcon(getClass().getResource(fileName));
label1 = new JLabel(image1);
this.add(label1);
//Set up JFrame
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setVisible(true);
this.setTitle(screenTitle);
this.setSize(screenWidth, screenHeight);
}
/**
* #param desired amount to move picture
*/
public void updatePic(int increment)
{
//update pos
label1.setBounds(label1.bounds().x, label1.bounds().y - increment,
label1.bounds().width, label1.bounds().height);
}
private ImageIcon image1;
private JLabel label1;
}
Class 2
public class MainClass implements Runnable {
public static void main(String[] args)
{
(new Thread(new MainClass())).start();
GFXScreen gfx = new GFXScreen("pixel_man.png", "pixel_Man", 1000, 1000);
}
public void run()
{
gfx.updatePic(1);
}
}
In this instance what I want to happen is, I want a picture that starts in the top to slowly move down smoothly to the bottom. How would i do this?
Suggestions:
Again, a Swing Timer works well for simple Swing animations or simple game loops. It may not be the greatest choice for complex or rigorous tame loops as its timing is not precise.
Most game loops will not be absolutely precise with time slices
And so your game model should take this into consideration and should note absolute time slices and use that information in its physics engine or animation.
If you must use background threading, do take care that most all Swing calls are made on the Swing event thread. To do otherwise will invite pernicious infrequent and difficult to debug program-ending exceptions. For more details on this, please read Concurrency in Swing.
I avoid using null layouts, except when animating components, as this will allow my animation engine to place the component absolutely.
When posting code here for us to test, it's best to avoid code that uses local images. Either have the code use an image easily available to all as a URL or create your own image in your code (see below for a simple example).
Your compiler should be complaining to you about your using deprecated methods, such as bounds(...), and more importantly, you should heed those complaints as they're there for a reason and suggest increased risk and danger if you use them. So don't use those methods, but instead check the Java API for better substitutes.
Just my own personal pet peeve -- please indicate that you've at least read our comments. No one likes putting effort and consideration into trying to help, only to be ignored. I almost didn't post this answer because of this.
For example:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
#SuppressWarnings("serial")
public class GfxPanel extends JPanel {
private static final int BI_WIDTH = 26;
private static final int BI_HEIGHT = BI_WIDTH;
private static final int GAP = 6;
private static final Point INITIAL_LOCATION = new Point(0, 0);
private static final int TIMER_DELAY = 40;
public static final int STEP = 1;
private ImageIcon image1;
private JLabel label1;
private Point labelLocation = INITIAL_LOCATION;
private int prefW;
private int prefH;
private Timer timer;
public GfxPanel(int width, int height) {
// the only time I use null layouts is for component animation.
setLayout(null);
this.prefW = width;
this.prefH = height;
// My program creates its image so you can run it without an image file
image1 = new ImageIcon(createMyImage());
label1 = new JLabel(image1);
label1.setSize(label1.getPreferredSize());
label1.setLocation(labelLocation);
this.add(label1);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(prefW, prefH);
}
public void startAnimation() {
if (timer != null && timer.isRunning()) {
timer.stop();
}
labelLocation = INITIAL_LOCATION;
timer = new Timer(TIMER_DELAY, new TimerListener());
timer.start();
}
// My program creates its image so you can run it without an image file
private Image createMyImage() {
BufferedImage bi = new BufferedImage(BI_WIDTH, BI_HEIGHT,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = bi.createGraphics();
g2.setColor(Color.red);
g2.fillRect(0, 0, BI_WIDTH, BI_HEIGHT);
g2.setColor(Color.blue);
int x = GAP;
int y = x;
int width = BI_WIDTH - 2 * GAP;
int height = BI_HEIGHT - 2 * GAP;
g2.fillRect(x, y, width, height);
g2.dispose();
return bi;
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
int x = labelLocation.x + STEP;
int y = labelLocation.y + STEP;
labelLocation = new Point(x, y);
label1.setLocation(labelLocation);
repaint();
if (x + BI_WIDTH > getWidth() || y + BI_HEIGHT > getHeight()) {
System.out.println("Stopping Timer");
((Timer) e.getSource()).stop();
}
}
}
private static void createAndShowGui() {
final GfxPanel gfxPanel = new GfxPanel(900, 750);
JButton button = new JButton(new AbstractAction("Animate") {
#Override
public void actionPerformed(ActionEvent arg0) {
gfxPanel.startAnimation();
}
});
JPanel buttonPanel = new JPanel();
buttonPanel.add(button);
JFrame frame = new JFrame("GFXScreen");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(gfxPanel);
frame.getContentPane().add(buttonPanel, BorderLayout.PAGE_END);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
What I always use is an infinite loop that calls an update method each iteration, in that method, you would do whatever was required to update the state of the game or render a GUI.
Example
public static void main(String[] args){
// Initialise game
while(true){
updateGame();
}
}
public static void updateGame(){
// Update things here.
}
What I also do ,which is a little more complex, is create and interface called IUpdateListener and have certain classes that are specialised for a certain element of the game. I would example have an InputListener, an AIListener, each handling a certain element of game updating.
public interface IUpdateListener{
public void update();
}
public class Main{
public static ArrayList<IUpdateListener> listeners = new ArrayList<IUpdateListener>();
public static void main(String[] args){
listeners.add(new InputListener());
while(true){
for(IUpdateListener listener : listeners){
listener.update();
}
}
}
}
public class InputListener implements IUpdateListener{
public void update(){
// Handle user input here etc
}
}
I was doing a tutorial online because I wanted to make a 2d side scroller, and I got this exact error. I have googled it but came up with nothing. I tried looking for a typo and it looks clean, its not giving me an error anywere else in the code. I do not know where to start. If you could explaing to me what the error is and how i fix it then that would be amazing.
package Main;
import GameState.GameStateManager;
import javax.swing.JPanel;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
public class GamePanel extends JPanel implements Runnable, KeyListener{
public static final int WIDTH = 320;
public static final int HIGHT = 240;
public static final int SCALE = 2;
//game thread
private Thread thread;
private boolean running;
private int FPS = 60;
private long targetTime = 1000/FPS;
//image
private BufferedImage image;
private Graphics2D g;
//game state manager
private GameStateManager gsm;
public GamePanel(){
super();
setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
setFocusable(true);
requestFocus();
}
public void addNotify(){
super.addNotify();
if (thread == null) {
thread = new Thread(this);
addKeyListener(this);
thread.start();
}
}
private void init() {
image = new BufferedImage(WIDTH, HIGHT, BufferedImage.TYPE_INT_RGB);
g = (Graphics2D) image.getGraphics();
running = true;
gsm = new GameStateManager();
}
public void run(){
init();
long start, elapsed, wait;
//game loop
while(running) {
start = System.nanoTime();
update();
draw();
drawToScreen();
elapsed = System.nanoTime() - start;
wait = targetTime - elapsed / 1000000;
try
{
Thread.sleep(wait);
}
catch(Exception e)
{
e.printStackTrace();
}//end of try catch
}
}
private void update()
{
gsm.update();
}
private void draw()
{
gsm.draw(g);
}
private void drawToScreen()
{
Graphics g2 = getGraphics();
g2.drawImage(image, 0, 0, null);
g2.dispose();
}
public void KeyPressed(KeyEvent key)
{
gsm.keyPressed(key.getKeyCode());
}
public void KeyReleased(KeyEvent key)
{
gsm.keyReleased(key.getKeyCode());
}
}
The compiler error message tells you exactly what's wrong: your class implements the KeyListener interface but does not implement all the necessary methods of the interface. Solution: be sure to implement all the necessary methods as per the KeyListener API. Also be sure to use the #Override annotation to make sure that your overrides are correct.
Having said that, I'm going to recommend that you not use KeyListeners for most key board input with Swing applications, that it is a low-level listener and should be avoided in favor of higher level constructs such as key bindings. Also, Swing GUI's should avoid use of update(...) method overrides as that is more of an AWT construct.
Your KeyReleased(KeyEvent key) method must start with small letter 'k' like keyReleased(KeyEvent key). Java is case sensitive.
You may also required to override other methods of KeyListener interface.
Also add #Override annotation (as suggested by #Hovercraft Full Of Eels) to the method when you want to override a super method. That way IDE's will give you hint's while coding.
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.
I have a question about when paint and update method is called??
i have game applet where i want to use double buffering.But i cant use it.the problem is
In my game there is a ball which is moving inside run() method.I want to know how to use double buffering to swap the offscreen image and current image.Someone plz help.
And when there is both update() and paint() method.which are called first,when and why ???
A method you can use is to add a Canvas to the applet and then create a buffer strategy for that canvas. Abstracts the code, and you may get hardware acceleration.
The code is here: http://www.gamedev.net/community/forums/topic.asp?topic_id=405663 -- extend AppletGameCore and define your own subclass that implements the required methods.
import java.awt.Canvas;
import java.awt.Graphics2D;
import java.awt.Dimension;
import java.awt.image.BufferStrategy;
import java.applet.Applet;
/**
*AppletGameCore.java
*#author David Graham
*/
public abstract class AppletGameCore extends Applet implements Runnable
{
private BufferStrategy bufferStrategy;
private Canvas drawArea;/*Drawing Canvas*/
private boolean stopped = false;/*True if the applet has been destroyed*/
private int x = 0;
public void init()
{
Thread t = new Thread(this);
drawArea = new Canvas();
setIgnoreRepaint(true);
t.start();
}
public void destroy()
{
stopped = true;
/*Allow Applet to destroy any resources used by this applet*/
super.destroy();
}
public void update()
{
if(!bufferStrategy.contentsLost())
{
//Show bufferStrategy
bufferStrategy.show();
}
}
//Return drawArea's BufferStrategy
public BufferStrategy getBufferStrategy()
{
return bufferStrategy;
}
//Create drawArea's BufferStrategies
public void createBufferStrategy(int numBuffers)
{
drawArea.createBufferStrategy(numBuffers);
}
//Subclasses should override this method to do any drawing
public abstract void draw(Graphics2D g);
public void update(Graphics2D g)
{
g.setColor(g.getBackground());
g.fillRect(0,0,getWidth(),getHeight());
}
//Update any sprites, images, or primitives
public abstract void update(long time);
public Graphics2D getGraphics()
{
return (Graphics2D)bufferStrategy.getDrawGraphics();
}
//Do not override this method
public void run()
{
drawArea.setSize(new Dimension(getWidth(),getHeight()));
add(drawArea);
createBufferStrategy(2);
bufferStrategy = drawArea.getBufferStrategy();
long startTime = System.currentTimeMillis();
long currTime = startTime;
//animation loop
while(!stopped)
{
//Get time past
long elapsedTime = System.currentTimeMillis()-currTime;
currTime += elapsedTime;
//Flip or show the back buffer
update();
//Update any sprites or other graphical objects
update(elapsedTime);
//Handle Drawing
Graphics2D g = getGraphics();
update(g);
draw(g);
//Dispose of graphics context
g.dispose();
}
}
}