Draw on JPanel from other class - java

In my program I try to paint on a JPanel when the mouse is pressed. The mousePressed method is just to test the painting from another class. Later on the spawn method will be called by other class methods. When I press the mouse button spawnPedestrian() is called, but no Pedestrian is painted. Below is a running example with code from my project. If you create a project Roundabout and paste this code in it, you should be able to run it (images are hotlinked).
How to fix the spawnPedestrian() method?
public class Roundabout extends JFrame {
public static Surface surface;
public Roundabout() {
initUI();
}
private void initUI() {
setTitle("Roundabout");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
surface = new Surface();
add(surface);
this.addMouseListener(new MouseAdapter() {// empty implementation of all
// MouseListener`s methods
#Override
public void mousePressed(MouseEvent e) {
//Spawn
Spawn sp = new Spawn();
sp.spawnPedestrian(300, 100);
}
});
setSize(1618, 850);
setLocationRelativeTo(null);
}
public static JPanel getSurface() {
return surface;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Roundabout roundabout = new Roundabout();
roundabout.setVisible(true);
}
});
}
//Track class
class Track {
BufferedImage track;
Point trackPosition;
Point TRACK_POS = new Point(0, 0);
public Track() {
try {
track = ImageIO.read(new URL("http://i.stack.imgur.com/2U3j5.png"));
} catch (Exception ex) {
System.out.println("Problem loading track image: " + ex);
}
trackPosition = new Point(TRACK_POS.x, TRACK_POS.y);
}
public void paint(Graphics g) {
g.drawImage(track, TRACK_POS.x, TRACK_POS.y, null);
}
}
//Surface class
public class Surface extends JPanel {
Track track = new Track();
public List<Vehicle> toDraw = new ArrayList<>();
public Surface() {
Pedestrian p = new Pedestrian(100, 100);
toDraw.add(p);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
//setLayout(null);
track.paint(g);
//Make sure the track is painted first
for (Vehicle v : toDraw) {
v.paint(g);
}
}
}
class Pedestrian extends Vehicle {
BufferedImage pedestrian;
Point pedestrianPosition;
double pedestrianRotation = 0;
int pedestrianW, pedestrianH;
public Pedestrian(int x, int y) {
try {
pedestrian = ImageIO.read(new URL("http://i.stack.imgur.com/wm0I5.png"));
} catch (IOException e) {
System.out.println("Problem loading pedestrian images: " + e);
}
pedestrianPosition = new Point(x, y);
pedestrianW = pedestrian.getWidth();
pedestrianH = pedestrian.getHeight();
}
#Override
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.rotate(Math.toRadians(pedestrianRotation), pedestrianPosition.x, pedestrianPosition.y);
g2d.drawImage(pedestrian, pedestrianPosition.x, pedestrianPosition.y, null);
}
#Override
public void setPath(List<Point> path) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
#Override
public void update(double i) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
}
//Spawn class
class Spawn {
public void spawnPedestrian(int x, int y) {
//Create a new pedestrian.
System.out.println("Spawn a pedestrian.");
Pedestrian p = new Pedestrian(x, y);
Roundabout.surface.toDraw.add(p);
Roundabout.surface.revalidate();
Roundabout.surface.repaint();
}
}
public abstract class Vehicle {
public abstract void setPath(List<Point> path);
public abstract void update(double i);
public abstract void paint(Graphics g);
}
}
EDIT: It works now Pedestrian is spawned on mouse click.

Basically, you want to decouple your code and centralise the responsible to the classes. So the "data" should be maintained by a model of some kind, the rendering should be handle by some kind of view and the updates to the model and view should be handled by some kind of controller.
This makes it easier to swap out any one part with out requiring a whole bunch of new code or other changes. It also means that each class has a defined domain of responsibility and discourages you from trying to, for example, make changes to state from within the view which should be handled by the model (which could put the state into disarray)
Let's start with something that what's to be painted
public interface Sprite {
public void paint(Graphics2D g2d);
}
public interface MoveableSprite extends Sprite {
public void update(Container container);
}
These represent either a static sprite (like a tree for example) or a sprite which is moving (and wants to be updated on a regular bases)
These are contained within a model
public interface GameModel {
public List<Sprite> getSprites();
public void setObserver(Observer<MoveableSprite> observer);
public Observer<MoveableSprite> getObserver();
public void spawnSprite();
}
Which provides some means by which it can notify (in this case, a single) interested party about some kind of state change. For this example, that means a new MoveableSprite has become available
The Observer is pretty basic and just has a single call back...
public interface Observer<T> {
public void stateChanged(T parent);
}
And an "engine" to help drive it...
public class GameEngine {
private GameModel model;
private SurfacePane surface;
private Timer timer;
public GameEngine(GameModel model, SurfacePane surface) {
this.model = model;
this.surface = surface;
model.setObserver(new Observer<MoveableSprite>() {
#Override
public void stateChanged(MoveableSprite sprite) {
sprite.update(getSurface());
}
});
timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (Sprite sprite : getModel().getSprites()) {
if (sprite instanceof MoveableSprite) {
((MoveableSprite) sprite).update(getSurface());
}
}
getSurface().repaint();
}
});
}
public GameModel getModel() {
return model;
}
public SurfacePane getSurface() {
return surface;
}
public void start() {
timer.start();
}
public void stop() {
timer.stop();
}
}
This is a pretty basic example, but basically, it updates the position of MoveableSprite and asks the surface to repaint itself. It's also observing the GameModel for any new sprites and it will update their position immediately, so they don't appear in some "weird" place
Okay, now we actually need to implement some of this to make it work
public class DefaultGameModel implements GameModel {
private Observer<MoveableSprite> observer;
private List<Sprite> sprites;
public DefaultGameModel() {
sprites = new ArrayList<>(25);
for (int index = 0; index < 10; index++) {
spawnSprite();
}
}
#Override
public List<Sprite> getSprites() {
return Collections.unmodifiableList(sprites);
}
public void spawnSprite() {
try {
ZombieSprite sprite = new ZombieSprite();
sprites.add(sprite);
Observer<MoveableSprite> observer = getObserver();
if (observer != null) {
observer.stateChanged(sprite);
}
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void setObserver(Observer<MoveableSprite> observer) {
this.observer = observer;
}
#Override
public Observer<MoveableSprite> getObserver() {
return observer;
}
}
public class ZombieSprite implements MoveableSprite {
private int x;
private int y;
private int xDelta;
private int yDelta;
private BufferedImage img;
private Observer<Sprite> observer;
private boolean initialised = false;
public ZombieSprite() throws IOException {
img = ImageIO.read(getClass().getResource("/LogoZombi.png"));
}
#Override
public void update(Container container) {
if (!initialised) {
x = (int) (Math.random() * container.getWidth());
y = (int) (Math.random() * container.getHeight());
Random rnd = new Random();
xDelta = rnd.nextBoolean() ? 1 : -1;
yDelta = rnd.nextBoolean() ? 1 : -1;
initialised = true;
}
x += xDelta;
y += yDelta;
if (x < 0) {
x = 0;
xDelta *= -1;
} else if (x + img.getWidth() > container.getWidth()) {
x = container.getWidth() - img.getWidth();
xDelta *= -1;
}
if (y < 0) {
y = 0;
yDelta *= -1;
} else if (y + img.getHeight() > container.getHeight()) {
y = container.getHeight() - img.getHeight();
yDelta *= -1;
}
}
#Override
public void paint(Graphics2D g2d) {
g2d.drawImage(img, x, y, null);
}
}
These two classes implement the GameModel and MoveableSprite interfaces. We use interfaces to decouple the code, which makes it easier to change the way in which things work and provides a jumping off point for agreed to contracts and exceptions of the implemenations
And finally, something that actually paints the current state...
public class SurfacePane extends JPanel {
private GameModel model;
public SurfacePane(GameModel model) {
this.model = model;
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
getModel().spawnSprite();
}
});
}
public GameModel getModel() {
return model;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
GameModel model = getModel();
for (Sprite sprite : model.getSprites()) {
sprite.paint(g2d);
}
g2d.dispose();
}
}
You'll not that this class has the MouseListener, this is kind of deliberate, as other components which might be added to this container could prevent the MouseListener from been notified, so don't do that. But the MouseListener just calls the model to spawn another zombie...
And finally, we need to plumb it altogether...
GameModel model = new DefaultGameModel();
SurfacePane surfacePane = new SurfacePane(model);
GameEngine engine = new GameEngine(model, surfacePane);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(surfacePane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
engine.start();
And just because I know that's a lot of disjointed concepts to put together, a complete example...
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeListener;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
GameModel model = new DefaultGameModel();
SurfacePane surfacePane = new SurfacePane(model);
GameEngine engine = new GameEngine(model, surfacePane);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(surfacePane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
engine.start();
}
});
}
public class SurfacePane extends JPanel {
private GameModel model;
public SurfacePane(GameModel model) {
this.model = model;
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
getModel().spawnSprite();
}
});
}
public GameModel getModel() {
return model;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
GameModel model = getModel();
for (Sprite sprite : model.getSprites()) {
sprite.paint(g2d);
}
g2d.dispose();
}
}
public class GameEngine {
private GameModel model;
private SurfacePane surface;
private Timer timer;
public GameEngine(GameModel model, SurfacePane surface) {
this.model = model;
this.surface = surface;
model.setObserver(new Observer<MoveableSprite>() {
#Override
public void stateChanged(MoveableSprite sprite) {
sprite.update(getSurface());
}
});
timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (Sprite sprite : getModel().getSprites()) {
if (sprite instanceof MoveableSprite) {
((MoveableSprite) sprite).update(getSurface());
}
}
getSurface().repaint();
}
});
}
public GameModel getModel() {
return model;
}
public SurfacePane getSurface() {
return surface;
}
public void start() {
timer.start();
}
public void stop() {
timer.stop();
}
}
public interface Observer<T> {
public void stateChanged(T parent);
}
public interface Sprite {
public void paint(Graphics2D g2d);
}
public interface MoveableSprite extends Sprite {
public void update(Container container);
}
public interface GameModel {
public List<Sprite> getSprites();
public void setObserver(Observer<MoveableSprite> observer);
public Observer<MoveableSprite> getObserver();
public void spawnSprite();
}
public class DefaultGameModel implements GameModel {
private Observer<MoveableSprite> observer;
private List<Sprite> sprites;
public DefaultGameModel() {
sprites = new ArrayList<>(25);
for (int index = 0; index < 10; index++) {
spawnSprite();
}
}
#Override
public List<Sprite> getSprites() {
return Collections.unmodifiableList(sprites);
}
public void spawnSprite() {
try {
ZombieSprite sprite = new ZombieSprite();
sprites.add(sprite);
Observer<MoveableSprite> observer = getObserver();
if (observer != null) {
observer.stateChanged(sprite);
}
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void setObserver(Observer<MoveableSprite> observer) {
this.observer = observer;
}
#Override
public Observer<MoveableSprite> getObserver() {
return observer;
}
}
public class ZombieSprite implements MoveableSprite {
private int x;
private int y;
private int xDelta;
private int yDelta;
private BufferedImage img;
private Observer<Sprite> observer;
private boolean initialised = false;
public ZombieSprite() throws IOException {
img = ImageIO.read(getClass().getResource("/LogoZombi.png"));
}
#Override
public void update(Container container) {
if (!initialised) {
x = (int) (Math.random() * container.getWidth());
y = (int) (Math.random() * container.getHeight());
Random rnd = new Random();
xDelta = rnd.nextBoolean() ? 1 : -1;
yDelta = rnd.nextBoolean() ? 1 : -1;
initialised = true;
}
x += xDelta;
y += yDelta;
if (x < 0) {
x = 0;
xDelta *= -1;
} else if (x + img.getWidth() > container.getWidth()) {
x = container.getWidth() - img.getWidth();
xDelta *= -1;
}
if (y < 0) {
y = 0;
yDelta *= -1;
} else if (y + img.getHeight() > container.getHeight()) {
y = container.getHeight() - img.getHeight();
yDelta *= -1;
}
}
#Override
public void paint(Graphics2D g2d) {
g2d.drawImage(img, x, y, null);
}
}
}

Related

Java: Graphics2D - JPopupMenu - Color Changing

I’m writing a simple graphics editor, thus far I can draw some figures, move and enlarge them. I’m trying to allow user to change color of figure. After I right click in a shape, there appears a popup menu with colors to choose. But no matter what I do - the shape’s color doesn’t change. :/ I hope to get help, I spent a of of time on it but no idea how to solve it. :/
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Point2D;
import java.util.ArrayList;
public class PaintPanel extends JPanel implements MouseListener, MouseMotionListener, MouseWheelListener
{
public RadioMenu radio = new RadioMenu();
private ArrayList <Point2D.Double> points = new ArrayList<>();
private ArrayList <Shape> figures = new ArrayList<>();
private Color mainColor = Color.blue;
private Color bgColor = Color.white;
private Color special = Color.red;
private double scrollSpeed = 5;
private int pointsize = 4;
private int near = 15;
private int index;
private ColorMenu colorMenu = new ColorMenu();
public PaintPanel()
{
super();
addMouseListener(this);
addMouseMotionListener(this);
addMouseWheelListener(this);
setLayout(new BorderLayout());
setBackground(bgColor);
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
drawGraphics(g2d);
}
private void drawGraphics(Graphics2D g2d)
{
int i = 0;
for (Shape s : figures)
{
g2d.setColor(mainColor);
if (s instanceof MyEllipse2D)
{
g2d.setColor(((MyEllipse2D) s).color);
System.out.println(g2d.getColor());
}
else if (s instanceof MyRectangle2D)
{
g2d.setColor(((MyRectangle2D) s).color);
System.out.println(g2d.getColor());
}
else if (s instanceof MyPolygon2D)
{
g2d.setColor(((MyPolygon2D) s).color);
System.out.println(g2d.getColor());
}
if (g2d.getColor() != bgColor)
{
g2d.setColor(mainColor);
} else
{
g2d.setColor(mainColor);
g2d.draw(s);
}
++i;
}
i = 0;
for (Point2D.Double p : points)
{
if (i == 0)
{
g2d.setColor(special);
g2d.fillOval((int) p.getX(), (int) p.getY(), pointsize, pointsize);
g2d.setColor(mainColor);
} else
{
g2d.fillOval((int) p.getX(), (int) p.getY(), pointsize, pointsize);
}
++i;
}
}
#Override
public void mousePressed(MouseEvent e)
{
if(e.getButton()==MouseEvent.BUTTON1)
switch (radio.getChoice())
{
case "Okrag":
points.clear();
points.add(new Point2D.Double(e.getX(),e.getY()));
repaint();
break;
case "Prostokat":
points.clear();
points.add(new Point2D.Double(e.getX(),e.getY()));
repaint();
break;
case "Edycja":
index = isSelected(e);
break;
}
else if(e.getButton()==MouseEvent.BUTTON3 && radio.getChoice().equals("Edycja"))
{
index = isSelected(e);
if(index >= 0)
{
colorMenu.doPop(e);
}
}
}
#Override
public void mouseReleased(MouseEvent e)
{
if(e.getButton()==MouseEvent.BUTTON1)
switch (radio.getChoice())
{
case "Okrag":
points.add(new Point2D.Double(e.getX(),e.getY()));
figures.add(new MyEllipse2D(points.get(0),points.get(1),bgColor));
points.clear();
break;
case "Prostokat":
points.add(new Point2D.Double(e.getX(),e.getY()));
figures.add(new MyRectangle2D(points.get(0),points.get(1),bgColor));
points.clear();
break;
case "Wielokat":
if(points.size() != 0 && points.get(0).distance(e.getX(),e.getY())<=near)
{
figures.add(new MyPolygon2D(points,bgColor));
points.clear();
}
else
{
points.add(new Point2D.Double(e.getX(),e.getY()));
}
break;
case "Edycja":
points.clear();
}
repaint();
}
#Override
public void mouseDragged(MouseEvent e)
{
if(index>=0 && radio.getChoice().equals("Edycja"))
{
if (figures.get(index) instanceof MyEllipse2D)
{
((MyEllipse2D) figures.get(index)).move(new Point2D.Double(e.getX(),e.getY()));
}
else if (figures.get(index) instanceof MyRectangle2D)
{
((MyRectangle2D) figures.get(index)).move(new Point2D.Double(e.getX(),e.getY()));
}
else if(figures.get(index) instanceof MyPolygon2D)
{
((MyPolygon2D) figures.get(index)).move(new Point2D.Double(e.getX(),e.getY()));
}
repaint();
}
}
#Override
public void mouseWheelMoved(MouseWheelEvent e)
{
index = isSelected(e);
if(radio.getChoice().equals("Edycja"))
{
if (index>=0)
{
if (figures.get(index) instanceof MyEllipse2D)
{
((MyEllipse2D) figures.get(index)).scale(e.getPreciseWheelRotation(), scrollSpeed);
}
else if (figures.get(index) instanceof MyRectangle2D)
{
((MyRectangle2D) figures.get(index)).scale(e.getPreciseWheelRotation(), scrollSpeed);
}
else if(figures.get(index) instanceof MyPolygon2D)
{
((MyPolygon2D) figures.get(index)).scale(e.getPreciseWheelRotation(), scrollSpeed);
}
repaint();
}
}
}
private int isSelected(MouseEvent e)
{
int i;
for(i=figures.size()-1;i>=0;--i)
{
if(figures.get(i).contains(e.getPoint()))
{
return i;
}
}
return -1;
}
#Override
public void mouseClicked(MouseEvent e)
{
index = isSelected(e);
if(index >= 0 )
{
colorMenu.doPop(e);
if(e.getButton()==MouseEvent.BUTTON3 && radio.getChoice().equals("Edycja"))
{
colorMenu.doPop(e);
if(figures.get(index) instanceof MyEllipse2D)
((MyEllipse2D) figures.get(index)).color = colorMenu.color;
else if(figures.get(index) instanceof MyRectangle2D)
((MyRectangle2D) figures.get(index)).color = colorMenu.color;
else if(figures.get(index) instanceof MyPolygon2D)
((MyPolygon2D) figures.get(index)).color = colorMenu.color;
System.out.println(colorMenu.color);
//colorMenu.color = bgColor;
}
repaint();
}
}
#Override
public void mouseEntered(MouseEvent e)
{
}
#Override
public void mouseExited(MouseEvent e)
{
}
#Override
public void mouseMoved(MouseEvent e)
{
}
}
ColorMenu
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
public class ColorMenu extends JPopupMenu implements ActionListener
{
private ArrayList<JMenuItem> items = new ArrayList<JMenuItem>();
private ArrayList<Color> colors = new ArrayList<Color>();
public Color color;
public ColorMenu()
{
super();
colors.add(Color.black);
colors.add(Color.blue);
colors.add(Color.cyan);
colors.add(Color.gray);
colors.add(Color.green);
colors.add(Color.magenta);
colors.add(Color.orange);
colors.add(Color.red);
colors.add(Color.yellow);
colors.add(Color.white);
for (Color c : colors)
{
items.add(new JMenuItem(c.toString()));
}
for(JMenuItem i: items)
{
i.addActionListener(this);
add(i);
}
}
public void doPop(MouseEvent e)
{
show(e.getComponent(), e.getX(), e.getY());
}
#Override
public void actionPerformed(ActionEvent e)
{
Object source = e.getSource();
int j=0;
for(JMenuItem i: items)
{
if(i == source)
{
break;
}
++j;
}
this.color = colors.get(j);
}
}
MyRectangle2D
import java.awt.*;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
public class MyRectangle2D extends Rectangle2D.Double implements Shape
{
private Point2D.Double center;
private double width,hight;
public Color color;
public MyRectangle2D()
{}
public MyRectangle2D(Point2D.Double p1, Point2D.Double p2, Color color)
{
super();
this.color = color;
double x1 = p1.getX();
double y1 = p1.getY();
double x2 = p2.getX();
double y2 = p2.getY();
if(x1<=x2 && y1>=y2)
{
width=x2-x1;
hight=y1-y2;
setRect(x1,y2,width,hight);
}
else if(x1<=x2 && y1<=y2)
{
width=x2-x1;
hight=y2-y1;
setRect(x1,y1,width,hight);
}
else if (x1>=x2 && y1<=y2)
{
width=x1-x2;
hight=y2-y1;
setRect(x2,y1,width,hight);
}
else if(x1>=x2 && y1>=y2)
{
width=x1-x2;
hight=y1-y2;
setRect(x2,y2,width,hight);
}
center = new Point2D.Double(x1 + (x2-x1)/2,y1+(y2-y1)/2);
}
public void scale(double amount, double scale)
{
double change = -1*amount*scale;
width += change;
hight += change;
setRect(center.getX()-width/2,center.getY()-hight/2,width,hight);
}
public void move (Point2D.Double p)
{
center = p;
setRect();
}
private void setRect()
{
setRect(center.getX()-width/2,center.getY()-hight/2,width,hight);
}
}
MyPolygon2D
import java.awt.*;
import java.awt.geom.Point2D;
import java.util.ArrayList;
public class MyPolygon2D extends Polygon implements Shape
{
private ArrayList<MyVector> vectors = new ArrayList<>();
private Point2D.Double center;
private int size;
public Color color;
public MyPolygon2D()
{}
public MyPolygon2D(ArrayList<Point2D.Double> points, Color color)
{
super();
this.color = color;
size = points.size();
for(int i=0; i<size;++i)
{
addPoint((int)points.get(i).getX(),(int)points.get(i).getY());
}
center();
setVectors();
}
public void scale(double amount, double scale)
{
double change = -1*amount*scale;
for (int i=0;i<size;++i)
{
vectors.get(i).x *= (100.0+change)/100.0;
vectors.get(i).y *= (100.0+change)/100.0;
Point2D.Double curr = new Point2D.Double(center.getX()+vectors.get(i).x,center.getY()+vectors.get(i).y);
xpoints[i] = (int)curr.getX();
ypoints[i] = (int)curr.getY();
}
invalidate();
}
public void move (Point2D.Double p)
{
MyVector change = new MyVector(center,p);
center = p;
for(int i=0;i<size;++i)
{
xpoints[i] += (int)change.x;
ypoints[i] += (int)change.y;
}
invalidate();
}
public void setColor(Color color)
{
this.color = color;
}
public Color getColor()
{
return this.color;
}
#Override
public boolean contains(Point p)
{
int maxx=0, maxy=0, minx=Integer.MAX_VALUE, miny=Integer.MAX_VALUE;
for (int i=0;i<size;++i)
{
if(xpoints[i]>=maxx)
maxx = xpoints[i];
if(xpoints[i]<=minx)
minx = xpoints[i];
if(ypoints[i]>=maxy)
maxy = ypoints[i];
if(ypoints[i]<=miny)
miny = ypoints[i];
}
if(p.getX() <= maxx && p.getX() >= minx && p.getY() <= maxy && p.getY() >=miny)
return true;
else
return false;
}
private void setVectors()
{
for(int i=0; i<size;++i)
{
vectors.add(new MyVector(center,new Point2D.Double(xpoints[i],ypoints[i])));
}
}
private void center()
{
center = new Point2D.Double(getBounds2D().getX()+getBounds2D().getWidth()/2,getBounds2D().getY()+getBounds2D().getHeight()/2);
}
private class MyVector
{
public double x, y;
public MyVector(Point2D.Double p1, Point2D.Double p2)
{
x=p2.getX()-p1.getX();
y=p2.getY() - p1.getY();
}
}
}
MyEllipse2D
import java.awt.*;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
public class MyEllipse2D extends Ellipse2D.Double implements Shape
{
private double radius;
private Point2D.Double center;
public Color color;
public MyEllipse2D(Point2D.Double p1, Point2D.Double p2, Color color)
{
super();
this.color = color;
center = p1;
radius = (p1.distance(p2));
setFrame();
}
public void scale(double amount, double scale)
{
double change = -1*amount*scale;
radius += change;
setFrame();
}
public void move (Point2D.Double p)
{
center = p;
setFrame();
}
public void setColor(Color color)
{
this.color = color;
System.out.println(this.color);
}
public Color getColor()
{
return this.color;
}
private void setFrame()
{
setFrame(center.getX()-radius,center.getY()-radius,2*radius,2*radius);
}
}
RadioMenu
import javax.swing.*;
import java.awt.*;
public class RadioMenu extends JPanel
{
private int amount = 4;
private JRadioButton[] options = new JRadioButton[amount];
private ButtonGroup group = new ButtonGroup();
private String[] names = {"Okrag","Prostokat","Wielokat","Edycja"};
private Font font = new Font("Times New Roman",Font.BOLD,16);
public RadioMenu()
{
super();
setLayout(new GridLayout(1,amount));
for(int i=0;i<amount;++i)
{
if(i!=0)
options[i] = new JRadioButton(names[i],false);
else
options[i] = new JRadioButton(names[i],true);
group.add(options[i]);
add(options[i]);
options[i].setFont(font);
}
}
public String getChoice()
{
for(int i=0; i<amount; ++i)
{
if(options[i].isSelected())
return options[i].getText();
}
return "";
}
}
Frame
import javax.swing.*;
import java.awt.*;
public class Frame extends JFrame
{
private Dimension prefsize = new Dimension(800,600);
private Dimension minSize = new Dimension(400,200);
private Menu menu = new Menu();
private PaintPanel panel = new PaintPanel();
public Frame()
{
super();
setVisible(true);
setPreferredSize(prefsize);
setMinimumSize(minSize);
setLayout(new BorderLayout());
setDefaultCloseOperation(EXIT_ON_CLOSE);
add(panel,BorderLayout.CENTER);
JPanel upper = new JPanel();
upper.setLayout(new GridLayout(2,1));
upper.add(menu);
upper.add(panel.radio);
add(upper,BorderLayout.NORTH);
pack();
}
}
MyAplet
import javax.swing.*;
public class MyAplet extends JApplet
{
public void init()
{
Frame main = new Frame();
}
}
Menu
import javax.swing.*;
public class Menu extends JMenuBar
{
private JMenu info;
//private JMenuItem x;
public Menu()
{
super();
info = new JMenu("info");
//info.add(x);
add(info);
}
}
First, you should be displaying a JPopupMenu in response to only one type of event. You are currently calling colorMenu.doPop(e) in the mousePressed method and twice in the mouseClicked method.
To make sure you only display a JPopupMenu when you’re supposed to, you should use the JPopupMenu.isPopupTrigger method. This way, the menu will be displayed on mousePress or mouseClick, but never both. You do not want to display a JPopupMenu more than once in response to a single mouse action!
private int selectedIndex = -1;
// ...
#Override
public void mousePressed(MouseEvent e)
{
selectedIndex = isSelected(e);
if (colorMenu.isPopupTrigger(e)) {
colorMenu.doPop(e);
} else if (e.getButton() == MouseEvent.BUTTON1) {
// ...
#Override
public void mouseClicked(MouseEvent e)
{
if (selectedIndex >= 0 && colorMenu.isPopupTrigger(e) && radio.getChoice().equals("Edycja"))
{
colorMenu.doPop(e);
The next problem is that you’re trying to read colorMenu.color before the user has made a color selection. When you call colorMenu.doPopup(e), the menu is displayed and the method returns immediately. It does not wait for the user to make a selection and dismiss the menu. Your code is trying to immediately make use of colorMenu.color, but it hasn’t been set yet.
The only way to know when the user has selected a color is with an ActionListener. Your ColorMenu class has an ActionListener, but currently there is no way for the PaintPanel class to know when the ActionListener has been triggered.
You can give your ColorMenu class the capability of notifying listeners in other classes, using the inherited listenerList object:
public void addChangeListener(ChangeListener listener) {
listenerList.add(ChangeListener.class, listener);
}
public void removeChangeListener(ChangeListener listener) {
listenerList.remove(ChangeListener.class, listener);
}
public ChangeListener[] getChangeListeners() {
return listenerList.getListeners(ChangeListener.class);
}
protected void fireChangeListeners() {
ChangeEvent event = new ChangeEvent(this);
for (ChangeListener listener : getChangeListeners()) {
listener.stateChanged(event);
}
}
(ChangeListener and ChangeEvent are in the javax.swing.event package.)
This allows other classes to listen for a user selection. You want to change ColorMenu to actually notify those listeners:
#Override
public void actionPerformed(ActionEvent e)
{
// (other code) ...
this.color = colors.get(j);
fireChangeListeners();
}
Now, you can make PaintPanel listen for a user selection:
public PaintPanel()
{
super();
addMouseListener(this);
addMouseMotionListener(this);
addMouseWheelListener(this);
setLayout(new BorderLayout());
setBackground(bgColor);
colorMenu.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent event) {
if (selectedIndex >= 0)
{
Shape figure = figures.get(selectedIndex);
if (figure instanceof MyEllipse2D)
((MyEllipse2D) figure).color = colorMenu.color;
else if (figure instanceof MyRectangle2D)
((MyRectangle2D) figure).color = colorMenu.color;
else if (figure instanceof MyPolygon2D)
((MyPolygon2D) figure).color = colorMenu.color;
}
}
});
}
You should remove that color changing code from mouseClicked, as it will never be valid to call it there, since the user hasn’t selected a color yet:
#Override
public void mouseClicked(MouseEvent e)
{
if (selectedIndex >= 0 && colorMenu.isPopupTrigger(e) && radio.getChoice().equals("Edycja"))
{
colorMenu.doPop(e);
// Nothing else to do here, since the user has not selected a color yet.
}
}
The basic problem is, you need to know when the color is selected from the menu. The solution to the problem can be achieved in a number of different ways.
You could continue to use a MouseListener to monitor the mousePressed, mouseReleased and mouseClicked events, but you should be making using of the MouseEvent#isPopupTrigger property to determine when the popup should be displayed, as the triggers are different for different platforms.
Another solution would be to use JComponent#setComponentPopupMenu all the component to take care of the action itself. This, however, would require a slight change in design.
This approach places the onus back onto the component, rather then been separated into a different class, like you have the ColorMenu right now.
To start with, we need to know the selected shape, so, to the PaintPanel, we need to add a new property
private Shape selectedShape;
All this does is determines which shape was selected by the user.
Next we need someway to update the shape color. This can be achieved by simply adding a new method to the paintPanel method, which is called when the color is to be changed.
public void setShapeColor(Color color) {
//...
}
Next, we take advantage of the Action API and create a simple ColorAction.
This is self contained unit of work, which contains the information need to display it on a menu (or button) and which determines the actions to take when triggered.
public class ColorAction extends AbstractAction {
private Color color;
public ColorAction(String name, Color color) {
super(name);
this.color = color;
}
public Color getColor() {
return color;
}
#Override
public boolean isEnabled() {
return getSelectedShape() != null;
}
#Override
public void actionPerformed(ActionEvent e) {
setShapeColor(color);
}
}
In this case, this simple inner class to PaintPanel will call setShapeColor when triggered.
"But wait", I hear you call, "The colors can only be selected when a shape is selected!" Ah, we can control the Action through the isEnabled method, which will only return true when a shape is actually selected.
This might seem somewhat counterintuitive, but by not displaying a popup menu when nothing is displayed, it causes the user to think that no popup menu will be displayed ever, at least this way, we can display a popup menu with it's options disabled.
The next trick is trying to determine what is selected. Unfortunately, during my testing, the OS consumed the mousePressed event, which would have been most useful to make this determination, however, not all is lost.
The JComponent will call getPopupLocation just before the popup is made visible, passing in the MouseEvent which triggered it, we can take advantage of this to determine which shape would be selected
#Override
public Point getPopupLocation(MouseEvent event) {
selectedShape = null;
for (int i = figures.size() - 1; i >= 0; --i) {
if (figures.get(i).contains(event.getPoint())) {
selectedShape = figures.get(i);
}
}
return super.getPopupLocation(event);
}
For a more complete example...
public class PaintPanel extends JPanel implements MouseListener, MouseMotionListener, MouseWheelListener {
private ArrayList<Point2D.Double> points = new ArrayList<>();
private ArrayList<Shape> figures = new ArrayList<>();
private Shape selectedShape;
private Color mainColor = Color.blue;
private Color bgColor = Color.white;
private Color special = Color.red;
private double scrollSpeed = 5;
private int pointsize = 4;
private int near = 15;
private int index;
public PaintPanel() {
super();
addMouseListener(this);
addMouseMotionListener(this);
addMouseWheelListener(this);
setLayout(new BorderLayout());
setBackground(bgColor);
setComponentPopupMenu(makePopupMenu());
}
protected JPopupMenu makePopupMenu() {
JPopupMenu menu = new JPopupMenu();
menu.add(new ColorAction("Black", Color.black));
menu.add(new ColorAction("Blue", Color.blue));
menu.add(new ColorAction("Cyan", Color.cyan));
menu.add(new ColorAction("Grey", Color.gray));
menu.add(new ColorAction("Green", Color.green));
menu.add(new ColorAction("Megenta", Color.magenta));
menu.add(new ColorAction("Orange", Color.orange));
menu.add(new ColorAction("Red", Color.red));
menu.add(new ColorAction("Yellow", Color.yellow));
menu.add(new ColorAction("White", Color.white));
return menu;
}
public Shape getSelectedShape() {
return selectedShape;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
public Point getPopupLocation(MouseEvent event) {
selectedShape = null;
for (int i = figures.size() - 1; i >= 0; --i) {
if (figures.get(i).contains(event.getPoint())) {
selectedShape = figures.get(i);
}
}
return super.getPopupLocation(event);
}
#Override
public void mouseClicked(MouseEvent e) {
//...
}
#Override
public void mousePressed(MouseEvent e) {
//...
}
#Override
public void mouseReleased(MouseEvent e) {
//...
}
#Override
public void mouseEntered(MouseEvent e) {
//...
}
#Override
public void mouseExited(MouseEvent e) {
//...
}
#Override
public void mouseDragged(MouseEvent e) {
//...
}
#Override
public void mouseMoved(MouseEvent e) {
//...
}
#Override
public void mouseWheelMoved(MouseWheelEvent e) {
//...
}
public void setShapeColor(Color color) {
//...
}
public class ColorAction extends AbstractAction {
private Color color;
public ColorAction(String name, Color color) {
super(name);
this.color = color;
}
public Color getColor() {
return color;
}
#Override
public boolean isEnabled() {
return getSelectedShape() != null;
}
#Override
public void actionPerformed(ActionEvent e) {
setShapeColor(color);
}
}
}
There are a couple of other ways you might achieve this, but I'd be using these basic principles are the cornerstone of the design.
For example, you could have a "color model" which get's passed to the popup menu class, which would be updated when a color is selected

Program not painting screen properly

I've been constructing a short program that basically draws a spaceship on a JPanel and listens for keys that tell the program to shoot a bullet. The problem is that it's not even painting the spaceship or the bullets on the screen. I also suspect that the KeyBindings may not be working as that was a previous problem (that I may or may not have fixed), but the main issue at hand is still the fact that my screen isn't being painted. Here is my code:
public enum Direction {
LEFT, RIGHT, SPACE
}
import javax.swing.JFrame;
public class Main {
public static void main(String[] args) {
JFrame frame;
Ship s1;
Shoot shoot;
// Set the frame up
frame = new JFrame();
frame.setSize(400, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setVisible(true);
// Get some more necessary objects
s1 = new Ship();
shoot = new Shoot(s1);
frame.getContentPane().add(shoot);
s1.setShoot(shoot);
// Threads
Thread ship = new Thread(s1);
ship.start();
}
}
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
public class Shoot extends JPanel {
Ship s1;
public Shoot(Ship s1) {
this.s1 = s1;
addKeyBinding(KeyEvent.VK_LEFT, "left.pressed", new MoveAction(true, s1, Direction.LEFT), true);
addKeyBinding(KeyEvent.VK_LEFT, "left.released", new MoveAction(false, s1, Direction.LEFT), false);
addKeyBinding(KeyEvent.VK_RIGHT, "right.pressed", new MoveAction(true, s1, Direction.RIGHT), true);
addKeyBinding(KeyEvent.VK_RIGHT, "right.released", new MoveAction(false, s1, Direction.RIGHT), false);
addKeyBinding(KeyEvent.VK_SPACE, "space.pressed", new MoveAction(true, s1, Direction.SPACE), true);
addKeyBinding(KeyEvent.VK_SPACE, "space.released", new MoveAction(false, s1, Direction.SPACE), false);
setDoubleBuffered(true);
}
#Override
public void paintComponent(Graphics g) {
// Draw the ship
super.paintComponent(g);
s1.draw(g);
g.fill3DRect(40, 50, 10, 10, false);
}
protected void addKeyBinding(int keyCode, String name, Action action, boolean keyPressed) {
if (keyPressed) {
addKeyBinding(KeyStroke.getKeyStroke(keyCode, 0, false), name, action);
} else {
addKeyBinding(KeyStroke.getKeyStroke(keyCode, 0, true), name, action);
}
}
protected void addKeyBinding(KeyStroke keyStroke, String name, Action action) {
InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = getActionMap();
inputMap.put(keyStroke, name);
actionMap.put(name, action);
}
}
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
public class Ship implements Runnable {
int x, y, xDirection, bx, by;
boolean readyToFire, shooting = false;
Rectangle bullet;
Shoot shoot;
public Ship() {
x = 175;
y = 275;
bullet = new Rectangle(0, 0, 3, 5);
}
public void draw(Graphics g) {
// System.out.println("draw() called");
g.setColor(Color.BLUE);
g.fillRect(x, y, 40, 10);
g.fillRect(x + 18, y - 7, 4, 7);
if (shooting) {
g.setColor(Color.RED);
g.fillRect(bullet.x, bullet.y, bullet.width, bullet.height);
}
shoot.repaint();
}
public void move() {
x += xDirection;
if (x <= 0)
x = 0;
if (x >= 360)
x = 360;
shoot.repaint();
}
public void shoot() {
if (shooting) {
bullet.y--;
shoot.repaint();
}
}
public void setXDirection(int xdir) {
xDirection = xdir;
}
public void setShoot(Shoot shoot) {
this.shoot = shoot;
}
#Override
public void run() {
try {
while (true) {
shoot();
move();
Thread.sleep(5);
}
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
}
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.util.HashSet;
import javax.swing.AbstractAction;
public class MoveAction extends AbstractAction {
boolean pressed;
Ship s1;
Direction dir;
private Set<Direction> movement;
public MoveAction(boolean pressed, Ship s1, Direction dir) {
System.out.println("moveaction class");
movement = new HashSet<Direction>();
this.pressed = pressed;
this.s1 = s1;
this.dir = dir;
}
#Override
public void actionPerformed(ActionEvent e) {
try {
if (movement.contains(Direction.LEFT)) {
if (pressed) {
s1.setXDirection(-1);
} else {
s1.setXDirection(0);
}
} else if (movement.contains(Direction.RIGHT)) {
if (pressed) {
s1.setXDirection(1);
} else {
s1.setXDirection(0);
}
} else if (movement.contains(Direction.SPACE)) {
if (pressed) {
if (s1.bullet == null)
s1.readyToFire = true;
if (s1.readyToFire) {
s1.bullet.x = s1.x + 18;
s1.bullet.y = s1.y - 7;
s1.shooting = true;
}
} else {
s1.readyToFire = false;
if (s1.bullet.y <= -7) {
s1.bullet = null;
s1.shooting = false;
s1.bullet = null;
s1.bullet = new Rectangle(0, 0, 0, 0);
s1.readyToFire = true;
}
}
}
} catch (NullPointerException ex) {
System.out.println("NullPointerException");
}
}
So, there are a number of issues...
You should call setVisible on the JFrame last, this will ensure that components are laid out
Your keybindings issue seem to related to the fact that you're using the movement Set, but you never actually add anything to it. Instead you should be checking the dir value.
And probably a bunch of other things. Your code is tightly coupled and there isn't any centralised management of the state.
I'd start by having a better understand of the Model-View-Controller paradigm and separate the code into appropriate areas of responsibility.
The "data" for the game should separate from the "rendering" of the game, which should be separate from the decisions about how the game is to be updated.
What I'm about to present is an oversimplification intended to spark ideas, rather than been a concrete solution, as there are a number of ways you could achieve the physical implementation...
So, what we need is, a concept of something in the game, AKA an "entity", entities take many forms, but I'm going to focus on renderable/displayable entities. You need some kind of model that is responsible for modeling the current state of the game and which is responsible for implementing the rules. You need some kind of view, which is responsible for rendering the model and responding to user input. You need some kind of controller which controls how the model and the view bridged.
It's always a good idea to start with a series of interfaces which define the contractual expectations and outlines the expected means by which elements are expected to communicate with each other. Again, I've taken a simple, direct approach, but it's by no means the only...
public static enum Direction {
LEFT,
RIGHT,
SPACE
}
public interface Entity {
public void paint(Graphics2D g2d);
public Point getLocation();
public void setLocation(Point p);
public Dimension getSize();
}
public interface GameModel {
public Entity[] getEntities();
public void update(Rectangle bounds, Set<Direction> keys);
}
public interface GameController {
public Entity[] getEntities();
public void setDirection(Direction direction, boolean pressed);
public void start();
}
public interface GameView {
public void setController(GameController controller);
public GameController getController();
public Rectangle getViewBounds();
public void repaint();
}
Let's take a look at the implementation of the entities. This example has two entities, a Player and a Bullet...
public abstract class AbstractEntity implements Entity {
private final Point location = new Point(0, 0);
#Override
public Point getLocation() {
return new Point(location);
}
#Override
public void setLocation(Point p) {
location.setLocation(p);
}
}
public class Player extends AbstractEntity {
public Player(Rectangle bounds) {
int x = bounds.x + ((bounds.width - getSize().width) / 2);
int y = bounds.y + (bounds.height - getSize().height);
setLocation(new Point(x, y));
}
#Override
public Dimension getSize() {
return new Dimension(40, 17);
}
#Override
public void paint(Graphics2D g2d) {
Point p = getLocation();
Dimension size = getSize();
g2d.setColor(Color.BLUE);
g2d.fillRect(p.x, p.y + 7, size.width, 10);
g2d.fillRect(p.x + 18, p.y, 4, 7);
}
}
public class Bullet extends AbstractEntity {
#Override
public void paint(Graphics2D g2d) {
Rectangle bullet = new Rectangle(getLocation(), getSize());
g2d.setColor(Color.RED);
g2d.fill(bullet);
}
#Override
public Dimension getSize() {
return new Dimension(4, 8);
}
}
Nothing spectacular, but they each define their parameters and can render their states when asked.
Next, we have the model, controller and view. This example uses the controller as the primary game loop, responsible for updating the game (model) state and scheduling repaints. This is done with the use of a Swing Timer as this prevents possible race conditions between the update loop and the painting loop. A more complex implementation would need to take over the painting process through the use of a BufferStrategy and BufferStrategy and BufferCapabilities.
The model simply takes the current view boundaries and the current state of the keys and updates the state of the entities.
The view monitors user input, notifying the controller, and renders the current state of the game.
You'll note that the view and model never communicate directly with each other, this is the domain of the controller.
public class DefaultGameModel implements GameModel {
private final List<Entity> entities;
private Player player;
private Long lastShot;
public DefaultGameModel() {
entities = new ArrayList<>(25);
}
#Override
public Entity[] getEntities() {
return entities.toArray(new Entity[0]);
}
#Override
public void update(Rectangle bounds, Set<Direction> keys) {
if (player == null) {
player = new Player(bounds);
entities.add(player);
}
Point p = player.getLocation();
int xDelta = 0;
if (keys.contains(Direction.LEFT)) {
xDelta = -4;
} else if (keys.contains(Direction.RIGHT)) {
xDelta = 4;
}
p.x += xDelta;
if (p.x <= bounds.x) {
p.x = bounds.x;
} else if (p.x + player.getSize().width >= bounds.x + bounds.width) {
p.x = bounds.width - player.getSize().width;
}
player.setLocation(p);
Iterator<Entity> it = entities.iterator();
while (it.hasNext()) {
Entity entity = it.next();
if (entity instanceof Bullet) {
Point location = entity.getLocation();
Dimension size = entity.getSize();
location.y -= size.height;
if (location.y + size.height < bounds.y) {
it.remove();
} else {
entity.setLocation(location);
}
}
}
if (keys.contains(Direction.SPACE)) {
if (lastShot == null || System.currentTimeMillis() - lastShot > 100) {
lastShot = System.currentTimeMillis();
Bullet bullet = new Bullet();
int x = p.x + ((player.getSize().width - bullet.getSize().width) / 2);
int y = p.y - bullet.getSize().height;
bullet.setLocation(new Point(x, y));
entities.add(bullet);
}
}
}
}
public class DefaultGameController implements GameController {
private GameModel model;
private GameView view;
private Timer timer;
private Set<Direction> keys = new HashSet<>(25);
public DefaultGameController(GameModel gameModel, GameView gameView) {
gameView.setController(this);
view = gameView;
model = gameModel;
}
#Override
public Entity[] getEntities() {
return model.getEntities();
}
#Override
public void setDirection(Direction direction, boolean pressed) {
if (pressed) {
keys.add(direction);
} else {
keys.remove(direction);
}
}
#Override
public void start() {
if (timer != null && timer.isRunning()) {
timer.stop();
}
timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
model.update(view.getViewBounds(), Collections.unmodifiableSet(keys));
view.repaint();
}
});
timer.start();
}
}
public class DefaultGameView extends JPanel implements GameView {
private GameController controller;
public DefaultGameView() {
addKeyBinding("left.pressed", KeyEvent.VK_LEFT, true, new DirectionAction(Direction.LEFT, true));
addKeyBinding("left.released", KeyEvent.VK_LEFT, false, new DirectionAction(Direction.LEFT, false));
addKeyBinding("right.pressed", KeyEvent.VK_RIGHT, true, new DirectionAction(Direction.RIGHT, true));
addKeyBinding("right.released", KeyEvent.VK_RIGHT, false, new DirectionAction(Direction.RIGHT, false));
addKeyBinding("space.pressed", KeyEvent.VK_SPACE, true, new DirectionAction(Direction.SPACE, true));
addKeyBinding("space.released", KeyEvent.VK_SPACE, false, new DirectionAction(Direction.SPACE, false));
}
protected void addKeyBinding(String name, int keyEvent, boolean pressed, DirectionAction action) {
addKeyBinding(name, KeyStroke.getKeyStroke(keyEvent, 0, !pressed), action);
}
protected void addKeyBinding(String name, KeyStroke keyStroke, DirectionAction action) {
InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = getActionMap();
inputMap.put(keyStroke, name);
actionMap.put(name, action);
}
#Override
public void setController(GameController controller) {
this.controller = controller;
}
#Override
public GameController getController() {
return controller;
}
#Override
public Rectangle getViewBounds() {
return new Rectangle(new Point(0, 0), getSize());
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
GameController controller = getController();
for (Entity entity : controller.getEntities()) {
// I don't trust you
Graphics2D g2d = (Graphics2D) g.create();
entity.paint(g2d);
g2d.dispose();
}
}
public class DirectionAction extends AbstractAction {
private Direction direction;
private boolean pressed;
public DirectionAction(Direction direction, boolean pressed) {
this.direction = direction;
this.pressed = pressed;
}
#Override
public void actionPerformed(ActionEvent e) {
getController().setDirection(direction, pressed);
}
}
}
Okay, but that's all fine and good, but how do you use it? Something like this for example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
GameModel model = new DefaultGameModel();
DefaultGameView view = new DefaultGameView();
GameController controller = new DefaultGameController(model, view);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(view);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
controller.start();
}
});
}
public static enum Direction {
LEFT,
RIGHT,
SPACE
}
public interface Entity {
public void paint(Graphics2D g2d);
public Point getLocation();
public void setLocation(Point p);
public Dimension getSize();
}
public interface GameModel {
public Entity[] getEntities();
public void update(Rectangle bounds, Set<Direction> keys);
}
public interface GameController {
public Entity[] getEntities();
public void setDirection(Direction direction, boolean pressed);
public void start();
}
public interface GameView {
public void setController(GameController controller);
public GameController getController();
public Rectangle getViewBounds();
public void repaint();
}
public class DefaultGameModel implements GameModel {
private final List<Entity> entities;
private Player player;
private Long lastShot;
public DefaultGameModel() {
entities = new ArrayList<>(25);
}
#Override
public Entity[] getEntities() {
return entities.toArray(new Entity[0]);
}
#Override
public void update(Rectangle bounds, Set<Direction> keys) {
if (player == null) {
player = new Player(bounds);
entities.add(player);
}
Point p = player.getLocation();
int xDelta = 0;
if (keys.contains(Direction.LEFT)) {
xDelta = -4;
} else if (keys.contains(Direction.RIGHT)) {
xDelta = 4;
}
p.x += xDelta;
if (p.x <= bounds.x) {
p.x = bounds.x;
} else if (p.x + player.getSize().width >= bounds.x + bounds.width) {
p.x = bounds.width - player.getSize().width;
}
player.setLocation(p);
Iterator<Entity> it = entities.iterator();
while (it.hasNext()) {
Entity entity = it.next();
if (entity instanceof Bullet) {
Point location = entity.getLocation();
Dimension size = entity.getSize();
location.y -= size.height;
if (location.y + size.height < bounds.y) {
it.remove();
} else {
entity.setLocation(location);
}
}
}
if (keys.contains(Direction.SPACE)) {
if (lastShot == null || System.currentTimeMillis() - lastShot > 100) {
lastShot = System.currentTimeMillis();
Bullet bullet = new Bullet();
int x = p.x + ((player.getSize().width - bullet.getSize().width) / 2);
int y = p.y - bullet.getSize().height;
bullet.setLocation(new Point(x, y));
entities.add(bullet);
}
}
}
}
public class DefaultGameController implements GameController {
private GameModel model;
private GameView view;
private Timer timer;
private Set<Direction> keys = new HashSet<>(25);
public DefaultGameController(GameModel gameModel, GameView gameView) {
gameView.setController(this);
view = gameView;
model = gameModel;
}
#Override
public Entity[] getEntities() {
return model.getEntities();
}
#Override
public void setDirection(Direction direction, boolean pressed) {
if (pressed) {
keys.add(direction);
} else {
keys.remove(direction);
}
}
#Override
public void start() {
if (timer != null && timer.isRunning()) {
timer.stop();
}
timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
model.update(view.getViewBounds(), Collections.unmodifiableSet(keys));
view.repaint();
}
});
timer.start();
}
}
public abstract class AbstractEntity implements Entity {
private final Point location = new Point(0, 0);
#Override
public Point getLocation() {
return new Point(location);
}
#Override
public void setLocation(Point p) {
location.setLocation(p);
}
}
public class Player extends AbstractEntity {
public Player(Rectangle bounds) {
int x = bounds.x + ((bounds.width - getSize().width) / 2);
int y = bounds.y + (bounds.height - getSize().height);
setLocation(new Point(x, y));
}
#Override
public Dimension getSize() {
return new Dimension(40, 17);
}
#Override
public void paint(Graphics2D g2d) {
Point p = getLocation();
Dimension size = getSize();
g2d.setColor(Color.BLUE);
g2d.fillRect(p.x, p.y + 7, size.width, 10);
g2d.fillRect(p.x + 18, p.y, 4, 7);
}
}
public class Bullet extends AbstractEntity {
#Override
public void paint(Graphics2D g2d) {
Rectangle bullet = new Rectangle(getLocation(), getSize());
g2d.setColor(Color.RED);
g2d.fill(bullet);
}
#Override
public Dimension getSize() {
return new Dimension(4, 8);
}
}
public class DefaultGameView extends JPanel implements GameView {
private GameController controller;
public DefaultGameView() {
addKeyBinding("left.pressed", KeyEvent.VK_LEFT, true, new DirectionAction(Direction.LEFT, true));
addKeyBinding("left.released", KeyEvent.VK_LEFT, false, new DirectionAction(Direction.LEFT, false));
addKeyBinding("right.pressed", KeyEvent.VK_RIGHT, true, new DirectionAction(Direction.RIGHT, true));
addKeyBinding("right.released", KeyEvent.VK_RIGHT, false, new DirectionAction(Direction.RIGHT, false));
addKeyBinding("space.pressed", KeyEvent.VK_SPACE, true, new DirectionAction(Direction.SPACE, true));
addKeyBinding("space.released", KeyEvent.VK_SPACE, false, new DirectionAction(Direction.SPACE, false));
}
protected void addKeyBinding(String name, int keyEvent, boolean pressed, DirectionAction action) {
addKeyBinding(name, KeyStroke.getKeyStroke(keyEvent, 0, !pressed), action);
}
protected void addKeyBinding(String name, KeyStroke keyStroke, DirectionAction action) {
InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = getActionMap();
inputMap.put(keyStroke, name);
actionMap.put(name, action);
}
#Override
public void setController(GameController controller) {
this.controller = controller;
}
#Override
public GameController getController() {
return controller;
}
#Override
public Rectangle getViewBounds() {
return new Rectangle(new Point(0, 0), getSize());
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
GameController controller = getController();
for (Entity entity : controller.getEntities()) {
// I don't trust you
Graphics2D g2d = (Graphics2D) g.create();
entity.paint(g2d);
g2d.dispose();
}
}
public class DirectionAction extends AbstractAction {
private Direction direction;
private boolean pressed;
public DirectionAction(Direction direction, boolean pressed) {
this.direction = direction;
this.pressed = pressed;
}
#Override
public void actionPerformed(ActionEvent e) {
getController().setDirection(direction, pressed);
}
}
}
}
Again, you're going to need to go away and do some more research, but this is the general idea
Your drawing depends on the boolean variable shooting; there is one place where shooting is being set to true; if the key operations dont work the flow of the program may never reach there and that may never happen.
So I sugest you minimize your project to a screen that will draw the graphic without depending on pressing keys.
If you can see the graphics then you can add gradually the keys

Changing image immediately

I am currently trying to learn a little bit about programming games.
I got myself a player class extending a sprite class i wrote by myself.
Now i want to know, how i can change the image of my player object in the middle of the running game. imagine a spaceship, when pressing the right arrow key it should be a different image, inclined to the right.
now i am trying to do this:
when a button is pressed (e.g. space) setImage(the new image)
But now, when i call this method my image just disappears and the new one won't appear?
Any ideas?
My code of the Main class:
public class Game extends JPanel implements Runnable{
private static final long serialVersionUID = 1L;
public boolean isRunning;
public static final int WIDTH = 320;
public static final int HEIGHT = 240;
public static final int SCALE = 2;
private Player player;
public Game() {
addKeyListener(new TAdapter());
setFocusable(true);
requestFocus();
start();
}
public void start() {
isRunning = true;
new Thread(this).start();
}
public void stop() {
isRunning = false;
}
public void run() {
init();
while(isRunning) {
update();
repaint();
try {
Thread.sleep(5);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void init() {
player = new Player("/ship_blue", WIDTH - 32/2, 400);
}
public void update() {
player.update();
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D)g;
drawPlayer(g2d);
}
public void drawPlayer(Graphics2D g2d) {
if(player.isVisible)g2d.drawImage(player.getImage(),(int) player.getX(), (int) player.getY(), null);
}
public static void main(String[] args) {
Game gameComponent = new Game();
Dimension size = new Dimension(WIDTH*SCALE, HEIGHT*SCALE);
JFrame frame = new JFrame("Invaders");
frame.setVisible(true);
frame.setSize(size);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.add(gameComponent);
}
public class TAdapter extends KeyAdapter{
public void keyPressed(KeyEvent e) {
player.keyPressed(e);
}
public void keyReleased(KeyEvent e) {
player.keyReleased(e);
}
}
}
That's the main class, now the player class in which i am trying to change the image in the onPress() method:
public class Player extends Sprite{
private double glideSpeed = .125;
private int fixY;
private int xDirection = 0;
private int yDirection = 1;
public Player(String source, int x, int y) {
this.x = x;
this.y = y;
ImageIcon ii = new ImageIcon(this.getClass().getResource(source));
setImage(ii.getImage());
setTileSize(ii.getIconWidth());
setSpeed(0.2);
fixY = y;
}
public void update() {
x += xDirection * 1.4;
glide(10);
}
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if(key == KeyEvent.VK_RIGHT) {
xDirection = 1;
}
if(key == KeyEvent.VK_LEFT) {
xDirection = -1;
}
if(key == KeyEvent.VK_SPACE) {
ImageIcon ii2 = new ImageIcon("/player_blue_negativ");
setImage(ii2.getImage()); //<-----Here I am trying to change the image
}
}
public void keyReleased(KeyEvent e) {
int key = e.getKeyCode();
if(key == KeyEvent.VK_RIGHT || key == KeyEvent.VK_LEFT) {
xDirection = 0;
}
}
public void glide(int span) {
y += yDirection * glideSpeed;
if(y - fixY > span || fixY - y > span) {
yDirection = -yDirection;
}
}
}
And to complete everything here's my sprite class:
public class Sprite {
protected double x;
protected double y;
protected int tileSize;
protected double speed;
protected Image img;
protected boolean isVisible;
public Sprite() {
isVisible = true;
}
public double getSpeed() {
return speed;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public int getTileSize() {
return tileSize;
}
public Image getImage() {
return img;
}
public boolean getVisible() {
return isVisible;
}
public void setSpeed(double speed) {
this.speed = speed;
}
public void setX(double x) {
this.x = x;
}
public void setY(double y) {
this.y = y;
}
public void setTileSize(int tileSize) {
this.tileSize = tileSize;
}
public void setImage(Image img) {
this.img = img;
}
public void die() {
isVisible = false;
}
}
if(key == KeyEvent.VK_SPACE) {
ImageIcon ii2 = new ImageIcon("/player_blue_negativ");
There is the basic problem. All images should be loaded and cached prior to an event happening. When the event occurs, use the cached image and the GUI should render immediately.
As mentioned by #camickr, the use of an ImageIcon also subtly slips from an URL to a String (presumed to represent a
File path). Stick with the URL obtained from getResource(..).
On ImageObserver.
If an ImageObserver is used to paint the image, the observer will be informed of updates to images that are asynchronously loaded. All components (e.g. JPanel) implement ImageObserver, so..
g2d.drawImage(player.getImage(),(int) player.getX(), (int) player.getY(), null);
Should be:
g2d.drawImage(player.getImage(),(int) player.getX(), (int) player.getY(), this);
Free the EDT!
class Game extends JPanel implements Runnable ..
Thread.sleep(5);
Don't block the EDT (Event Dispatch Thread) - the GUI will 'freeze' when that happens.
Also don't attempt to update the GUI from anything other than the EDT.
Instead of calling Thread.sleep(n) in a Runnable, implement a Swing Timer for repeating tasks or a SwingWorker for long running tasks. See Concurrency in Swing for more details.
Use paintComponent(Graphics) for Swing components!
public void paint(Graphics g) {
Should be:
#Override
public void paintComponent(Graphics g) {
When you load the initial image you use the following without a problem:
ImageIcon ii = new ImageIcon(this.getClass().getResource(source));
When you change the image you use:
ImageIcon ii2 = new ImageIcon("/player_blue_negativ");
I'm guessing you don't find the image, probably because you don't need the "/". But why not use the same method that you know works?

Multiple calls of paintComponent()

I have these two classes:
public class Pencil extends JComponent implements MouseListener, MouseMotionListener{
Plansa plansa;
Graphics g;
public Pencil(Plansa newCanvas){
this.plansa = newCanvas;
this.plansa.setFlagShape(false);
}
#Override
public void mouseDragged(MouseEvent arg0) {
plansa.setMouseDragged(arg0);
this.plansa.setFlagShape(false);
plansa.paintComponent(plansa.getGraphics());
}
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent arg0) {
plansa.setMousePressed(arg0);
}
#Override
public void mouseReleased(MouseEvent arg0) {
// TODO Auto-generated method stub
plansa.setMouseReleased(arg0);
this.plansa.setFlagShape(true);
plansa.paintComponent(plansa.getGraphics());
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
#Override
public void mouseMoved(MouseEvent e) {
}
}
And this one:
public class Plansa extends JPanel{
Image image;
Pencil pencil;
//...
void init(){
this.setShape("freeLine");
this.setColor(Color.BLACK);
this.size=1;
this.type="round";
this.fill=false;
this.setBackground(Color.WHITE);
pencil = new Pencil(this);
addMouseListener(pencil);
addMouseMotionListener(pencil);
flagPaint = true;
flagShape = false;
}
public Plansa(){
this.setSize(800, 600);
init();
}
//...
#Override
public void paintComponent(Graphics g) {
g.setColor(currentColor);
Graphics2D g2d = (Graphics2D) g;
g2d.setStroke(setBrush(size,type));
super.paintComponent(g);
switch(shape){
default: break;
case "freeLine":{
g.drawLine(xDragged, yDragged, xCurrent, yCurrent);
break;
}
case "rectangle":{
if(flagShape == true){
g.drawRect(xPressed, yPressed, Math.max(xCurrent-xPressed,xPressed-xCurrent), Math.max(yCurrent-yPressed,yPressed-yCurrent));
if(fill == true) g.fillRect(xPressed, yPressed, Math.max(xCurrent-xPressed,xPressed-xCurrent), Math.max(yCurrent-yPressed,yPressed-yCurrent));
}
break;
}
case "circle":{
if(flagShape == true){
int radius = (int)Math.sqrt(Math.max(xCurrent-xPressed,xPressed-xCurrent)*Math.max(xCurrent-xPressed,xPressed-xCurrent)+Math.max(yCurrent-yPressed,yPressed-yCurrent)*Math.max(yCurrent-yPressed,yPressed-yCurrent));
g.drawOval(xPressed, yPressed, radius, radius);
if(fill == true) g.fillOval(xPressed, yPressed, radius, radius);
}
break;
}
case "oval":{
if(flagShape == true){
g.drawOval(xPressed, yPressed, Math.max(xCurrent-xPressed,xPressed-xCurrent), Math.max(yCurrent-yPressed,yPressed-yCurrent));
if(fill == true) g.fillOval(xPressed, yPressed, Math.max(xCurrent-xPressed,xPressed-xCurrent), Math.max(yCurrent-yPressed,yPressed-yCurrent));
}
break;
}
case "line":{
if(flagShape == true){
g.drawLine(xPressed, yPressed, xCurrent, yCurrent);
}
break;
}
}
}
//...
}
My problem is that every time I call paintComponent() method, the JPanel clears and the only item that remains is the one I just drawn. Is there any way to avoid this?
After close inspection of the code, it appears that you're trying to use Components as "painters", it's a bit like using a sports car as a go-kart, a lot of extras for little gain.
Instead, you should define yourself a interface of some kine that provides the basic requirements of what you want to draw/paint and then define concrete class implementations that implement the functionality.
You would then maintain a List of some kind of all the drawings that can be painted.
While simple, the example below provides a jumping off point that could be enhanced that allow selection of drawings, re-ordering and removal (should you want it).
This, is, essentially, the same concept proposed by Doorknob (+1)
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestDraw {
public static void main(String[] args) {
new TestDraw();
}
public TestDraw() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new Pencil());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class Pencil extends JPanel implements MouseListener, MouseMotionListener {
private List<Drawable> drawables;
private Drawable activeDrawable;
private Point clickPoint;
public Pencil() {
drawables = new ArrayList<>(5);
addMouseListener(this);
addMouseMotionListener(this);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (Drawable drawable : drawables) {
drawable.paint(g2d);
}
g2d.dispose();
}
#Override
public void mouseDragged(MouseEvent e) {
if (activeDrawable != null) {
Point p = e.getPoint();
Rectangle bounds = activeDrawable.getBounds();
int x = bounds.x;
int y = bounds.y;
int width = p.x - clickPoint.x;
int height = p.y - clickPoint.y;
if (width < 0) {
width *= -1;
x = p.x;
}
if (height < 0) {
height *= -1;
y = p.y;
}
bounds = new Rectangle(x, y, width, height);
System.out.println(bounds);
activeDrawable.setBounds(bounds);
repaint();
}
}
#Override
public void mouseClicked(MouseEvent e) {
}
protected Drawable createActiveShape(MouseEvent e) {
System.out.println("Anchor = " + e.getPoint());
Drawable drawable = new FreeLine(e.getPoint());
drawable.setLocation(e.getPoint());
return drawable;
}
#Override
public void mousePressed(MouseEvent e) {
// You could also check to see if the clicked on a drawable...
clickPoint = e.getPoint();
activeDrawable = createActiveShape(e);
drawables.add(activeDrawable);
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
if (activeDrawable != null) {
Rectangle bounds = activeDrawable.getBounds();
if (bounds.width == 0 || bounds.height == 0) {
drawables.remove(activeDrawable);
}
}
clickPoint = null;
activeDrawable = null;
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
#Override
public void mouseMoved(MouseEvent e) {
}
}
public interface Drawable {
public void setLocation(Point p);
public void setSize(Dimension dim);
public void setBounds(Rectangle bounds);
public Rectangle getBounds();
public void paint(Graphics2D g2d);
}
public abstract class AbstractDrawable implements Drawable {
private Rectangle bounds;
public AbstractDrawable() {
bounds = new Rectangle();
}
#Override
public void setLocation(Point p) {
bounds.setLocation(p);
}
#Override
public void setBounds(Rectangle bounds) {
this.bounds = bounds;
}
#Override
public void setSize(Dimension dim) {
bounds.setSize(dim);
}
#Override
public Rectangle getBounds() {
return bounds;
}
}
public class FreeLine extends AbstractDrawable {
private Point anchor;
public FreeLine(Point anchor) {
this.anchor = anchor;
}
#Override
public void paint(Graphics2D g2d) {
Rectangle bounds = getBounds();
Point p1 = new Point(anchor);
Point p2 = new Point(bounds.getLocation());
if (p1.x > p2.x) {
p2.x = p1.x - bounds.width;
} else {
p2.x = p1.x + bounds.width;
}
if (p1.y > p2.y) {
p2.y = p1.y - bounds.height;
} else {
p2.y = p1.y + bounds.height;
}
g2d.draw(new Line2D.Float(p1, p2));
}
}
Store all the objects in an ArrayList and iterate through it when you are drawing.
You could have them all implement a custom interface, Drawable for example, then store in an ArrayList<Drawable>.
A better idea generally for progressive drawings is to use a BufferedImage as the canvas, as seen here.
Don't use/comment out:
super.paintComponent(g);
That line is the one that is doing the clearing.

Graphics not appearing in JFrame (SSCCE included)

I am making a game (see my previous threads) and have encountered a lot of problems on the way. All I know is that he code compiles, runs, but nothing appears in the window, it's just grey. At Andrew Thompson's suggestion, I am posting the entire compilable version here. Sorry for the length but it is ALL the code in the program. And a lot of things will probably not make sense (unused ActionPerformed to name one), partially because I implemented code in the event that I would need it but mostly because I have never done this before.
Also, so far I have no multithreading, because once again, I am new to this, so ideally I would like to keep it that way, if only for the sake of my sanity.
EDIT: Forgot to mention I have 4 PNGs in there representing the 4 different objects that appear. My code is flexible enough for you to supply your own. Here is the image I am using for ships and here is the one for bullets just make copies, put them the source file and name them "Enemy-ship" "ship2" "Ebullet" and "PBullet"
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
import javax.swing.JFrame;
public class GameController extends JFrame implements ActionListener {
/**
*
*/
private static final long serialVersionUID = -3599196025204169130L;
private static GameView window;
private static Timer time;
public GameController()
{
setTitle("Space Shooter");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(800, 600);
//window = new GameView(800,600);
//window.setVisible(true);
//
}
//TODO spawn
/*public static void main(String args[])
{
//GameController c = new GameController();
window = new GameView(800,600);
window.setVisible(true);
time = new Timer(40, this);
time.schedule( new TimerTask(){
public void run(){GameState.update();
window.paintComponents(null);}
},0, 40);
}*/
public void display() {
add(new GameView(800,600));
pack();
setMinimumSize(getSize());// enforces the minimum size of both frame and component
setVisible(true);
}
public static void main(String[] args) {
GameController main = new GameController();
main.display();
time = new Timer(40, main);
}
#Override
public void actionPerformed(ActionEvent e) {
if(e instanceof EndEvent)//TODO fix this
{
}
else
{
repaint();
}
}
}
package Game;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class GameView extends JComponent implements ActionListener{
/**
*
*/
private static final long serialVersionUID = -2869672245901003704L;
private static final Graphics Graphics = null;
private boolean liveGame;//used so that buttons cannot be clicked after game is complete
private GameState gs;
private Player p;
private int w, h;
public GameView(int width, int height)
{
liveGame = true;
gs = new GameState();
GameState.init(width, height);
p = new Player(width/2,(height*7)/8);
this.setBackground(Color.BLACK);
paintComponents(Graphics);
w = width;
h = height;
}
#Override
public Dimension getMinimumSize() {
return new Dimension(w, h);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(w, h);
}
#Override
public void paintComponent(Graphics g) {
int margin = 10;
Dimension dim = getSize();
super.paintComponent(g);
g.setColor(Color.black);
GameState.update();
for(Bullet j : GameState.getEnBullets()){
g.drawImage(j.getImage(),j.getX(), j.getY(), null);}
for(Enemy j : GameState.getEnemies()){
g.drawImage(j.getImage(),j.getX(), j.getY(), null);}
for(Bullet j : GameState.getPlayBullets()){
g.drawImage(j.getImage(),j.getX(), j.getY(), null);}
g.fillRect(margin, margin, dim.width - margin * 2, dim.height - margin * 2);
}
public void paintComponents (Graphics g)
{
for(Bullet j : GameState.getEnBullets()){
g.drawImage(j.getImage(),j.getX(), j.getY(), null);}
for(Enemy j : GameState.getEnemies()){
g.drawImage(j.getImage(),j.getX(), j.getY(), null);}
for(Bullet j : GameState.getPlayBullets()){
g.drawImage(j.getImage(),j.getX(), j.getY(), null);}
this.paint(g);
}
public void refreshImage()
{
this.removeAll();
paintComponents(Graphics);
}
public void actionPerformed(ActionEvent e) {
}
}
package Game;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import javax.swing.JFrame;
public class GameState {
private static ArrayList<Bullet> playBullets;
public static ArrayList<Bullet> getPlayBullets() {
return playBullets;
}
public static ArrayList<Bullet> getEnBullets() {
return enBullets;
}
public static ArrayList<Enemy> getEnemies() {
return enemies;
}
public static Player getP() {
return p;
}
private static ArrayList<Bullet> enBullets;
private static ArrayList<Enemy> enemies;
private static int X, Y;//for limit of screen so nothing can go outside of screen
private static Player p;
private static int score;
public GameState(){
}
public static void init(int x, int y)
{
playBullets = new ArrayList<Bullet>();
enBullets = new ArrayList<Bullet>();
enemies = new ArrayList<Enemy>();
X=x;
Y=y;
p = null;
score =0;
}
public static int xLimit(){return X;}
public static int yLimit(){return Y;}
public static int getScore(){return score;}
public static void add (Location e)
{
if(e instanceof Bullet)
{
if(((Bullet) e).getOwner() instanceof Enemy){
enBullets.add((Bullet) e);
}
else
playBullets.add((Bullet) e);
}
else if(e instanceof Enemy){enemies.add((Enemy)e);}
else
p=(Player)e;
}
public static void spawn()
{
Enemy e = new Enemy(((int)(Math.random()*(X-56))+28), 0, 1);
}
public static void playerCD()//detects if player has collided with anything, removes whatever collided with it, and causes the player to take damage
{
if(enemies.size()>0){
for(int i =0; i < enemies.size(); i++)
{
if (p.getLocation().intersects(enemies.get(i).getLocation()))
{
p.takeDamage(enemies.get(i).getDamage());
enemies.get(i).takeDamage(p.getDamage());
}
}
if(enBullets.size()>0)
for(int i =0; i < enBullets.size(); i++)
{
if (p.getLocation().intersects(enBullets.get(i).getLocation()))
{
p.takeDamage(enBullets.get(i).getDamage());
enBullets.remove(i);
i--;
}
}
}
}
public static void enemyCD()
{
for(int i =0; i < enemies.size(); i++)
{
for(int n =0; n < playBullets.size(); n++)
{
if (playBullets.get(n).getLocation().intersects(enemies.get(i).getLocation()))
{
enemies.get(i).takeDamage(playBullets.get(i).getDamage());
playBullets.remove(n);
n--;
score+=50;
}
}
}
}
public static void checkForDead()//clears away dead and things gone offscreen
{
for(int i =0; i < enemies.size(); i++)
{
if(enemies.get(i).getY()>Y)
{
enemies.remove(i);
i--;
}
}
for(int i =0; i < enBullets.size(); i++)
{
if(enBullets.get(i).getY()>Y)
{
enBullets.remove(i);
i--;
}
}
for(int i =0; i < enemies.size(); i++)
{
if(enemies.get(i).getHealth()>0)
{
enemies.remove(i);
i--;
score+=200;
}
}
if(p.getHealth()<=0)
{
ActionEvent e = new EndEvent(null, 0, "end");
}
}
public static void update()
{
move();
playerCD();
enemyCD();
checkForDead();
}
public static void move()
{
p.move();
for(int i =0; i < enemies.size(); i++){enemies.get(i).move();}
for(int i =0; i < enBullets.size(); i++){enBullets.get(i).move();}
for(int i =0; i < playBullets.size(); i++){playBullets.get(i).move();}
}
}
package Game;
import java.awt.Rectangle;
import java.awt.event.ActionListener;
public abstract class Fights extends Location implements ActionListener {
public Fights(Rectangle location) {
super(location);
// TODO Auto-generated constructor stub
}
public Fights(){}
protected int health;
protected int maxHealth;//in the event that I want to have healing items
protected int shotCooldown;//in milliseconds
protected int shotDmg;
protected long currentCool; //cooldown tracker, represents time that shot will be cooled down by (System time # last shot + shotCooldown
protected int xVel, yVel;
public abstract boolean shoot();
public abstract int takeDamage(int damage);//returns remaining health
protected boolean shoots;//determines whether thing can shoot. possible implementation in some enemy class
public boolean move;
public int getHealth(){return health;}
public abstract boolean move();
public int getDamage(){return shotDmg;}
public boolean isDead()
{
return health<=0;
}
}
package Game;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
public class Location {
protected Rectangle loc;
protected Image image;
public Location(){};
public Location (Rectangle location)
{
loc = location;
}
public Rectangle getLocation()
{
return loc;
}
public void setLocation(Rectangle l)
{
loc = l;
}
public void updateLocation(int x, int y)
{
loc.setLocation(x, y);
}
public Image getImage()
{
return image;
}
public int getX()
{
return (int)loc.getX();
}
public int getY()
{
return (int)loc.getY();
}
}
package Game;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.Rectangle;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Player extends Fights implements KeyListener{
int speed = 4;
public Player(Rectangle location) {
super(location);
GameState.add(this);
image = null;
try{
image = ImageIO.read(new File("ship2.png"));
}catch(IOException e){}
}
public Player(int x, int y) {
maxHealth = 1;
health = maxHealth;
image = null;
try{
image = ImageIO.read(new File("ship2.png"));
}catch(IOException e){}
this.setLocation(new Rectangle(x, y, image.getWidth(null), image.getHeight(null)));
GameState.add(this);
}
public void resetVelocity()
{
xVel = 0;
yVel = 0;
}
#Override
public boolean shoot() {
if(currentCool - System.currentTimeMillis() >0){return false;}
else
{
new Bullet(this);
currentCool = System.currentTimeMillis() + shotCooldown;
}//spawns bullet in the center and slightly in front of player
return true;
}
#Override
public int takeDamage(int damage) {
return health-=damage;
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
}
#Override
public boolean move() {//moves in a direction only if it won't exceed screen boundary, boolean just in case i need it later
int newX = this.getX(), newY=this.getY();
if((xVel+ this.getX()+this.getLocation().width)<GameState.xLimit()&& this.getX()+xVel>=0)
{
newX +=xVel;
}
if((yVel+ this.getY()+this.getLocation().height)<GameState.yLimit()&& this.getY()+yVel>=0)
{
newY +=yVel;
}
this.updateLocation(newX, newY);
this.resetVelocity();
return true;
}
#Override
public void keyPressed(KeyEvent arg0) {
if (arg0.getKeyCode()== KeyEvent.VK_LEFT)
{
xVel -= speed;
}
if (arg0.getKeyCode()== KeyEvent.VK_RIGHT)
{
xVel += speed;
}
if (arg0.getKeyCode()== KeyEvent.VK_UP)
{
yVel -= speed;
}
if (arg0.getKeyCode()== KeyEvent.VK_DOWN)
{
yVel += speed;
}
if(arg0.getKeyCode()==KeyEvent.VK_SPACE)
{
this.shoot();
}
}
#Override
public void keyReleased(KeyEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void keyTyped(KeyEvent arg0) {
// TODO Auto-generated method stub
}
}
package Game;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Enemy extends Fights {
public Enemy(Rectangle location) {
super(location);
GameState.add(this);
image = null;
try{
image = ImageIO.read(new File("Enemy-Ship.png"));
}catch(IOException e){}
}
public Enemy(int x, int y, int d) {
image = null;
try{
image = ImageIO.read(new File("Enemy-Ship.png"));
}catch(IOException e){}
this.setLocation(new Rectangle(x, y, image.getWidth(null), image.getHeight(null)));
GameState.add(this);
shotCooldown =(int)(Math.random()*2000);
xVel = (int)((Math.pow(-1, (int)(Math.random())))*((int)(Math.random()*6))+2);
yVel = (int)(Math.random()*3+1);
shotDmg =d;
}
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public boolean shoot() {
if(currentCool - System.currentTimeMillis() >0){return false;}
else
{
new Bullet(this);
currentCool = System.currentTimeMillis() + shotCooldown;
}//spawns bullet in the center and slightly in front of player
return true;
}
#Override
public int takeDamage(int damage)//returns remaining health
{
health = health-damage;
return health;
}
#Override
public boolean move() {
int newX = this.getX(), newY=this.getY();
if((xVel+ this.getX()+this.getLocation().width)<GameState.xLimit()&& this.getX()+xVel>=0)
{
xVel=-xVel;
newX +=xVel;
}
if(this.getY()+yVel>=0)
{
newY +=yVel;
}
this.updateLocation(newX, newY);
return true;
}
}
package Game;
import java.awt.Rectangle;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Bullet extends Location{
private Fights bulletOwner;
private int damage;
private int velocity;
public Bullet(Fights owner)//eventually change to singleton pattern for efficiency
{
bulletOwner = owner;
damage = owner.getDamage();
image = null;
if(owner instanceof Enemy)
{
try{
image = ImageIO.read(new File("Ebullet.png"));
}catch(IOException e){}
this.setLocation(new Rectangle(owner.getX(), owner.getY()+((int)(owner.getLocation().getHeight()/2)), image.getWidth(null), image.getHeight(null)));
velocity = 5;
}
else
{
try{
image = ImageIO.read(new File("Pbullet.png"));
}catch(IOException e){}
this.setLocation(new Rectangle(owner.getX(), owner.getY()-((int)(owner.getLocation().getHeight()/2)), image.getWidth(null), image.getHeight(null)));
velocity = -15;
}
GameState.add(this);
}
public Fights getOwner(){return bulletOwner;}
public int getDamage(){return damage;}
public int getVelocity(){return velocity;}
public boolean move()
{
this.updateLocation(this.getX(), this.getY()+velocity);
return true;
}
}
I can't believe your write 700 lines of code without doing any testing along the way. Its time you go back to the beginning and start with something simple. That is the whole point of a SSCCE. Start with painting a couple of compononents. Once you get that working you add some movement. Once that is working you add collision logic.
The only thing I noticed with a quick broswe is that you override paintComponents(). There is no need to do that custom painting is done in the pantComponent() method.
If you can't produce a smaller sized SSCCE, then all I can do is wish you good luck.
Ok, so I think I have figured most of it out.
You have a couple problems.
First, you should only see a grey screen with a black rectangle in the middle since you have nothing in your Bullet and Enemy Arrays. This is what I got when I ran your code (after removing references to endEvent cuz it couldn't find it). So to fix this, just give it something to draw
The second problem is apparent once you give it something to draw. I manually put in a line of code to draw the Player, for which I used one of my own pngs. When you do this it will fail to compile with a null pointer exception. The reason is because in your GameView class, you have your Graphics object called "graphics" set to null, but then you proceed to call paintComponents(graphics). As mentioned before, this only compiled before because you never actually drew anything. To fix this, you can just remove
public void paintComponents (Graphics g)
{
for(Bullet j : GameState.getEnBullets()){
g.drawImage(j.getImage(),j.getX(), j.getY(), null);}
for(Enemy j : GameState.getEnemies()){
g.drawImage(j.getImage(),j.getX(), j.getY(), null);}
for(Bullet j : GameState.getPlayBullets()){
g.drawImage(j.getImage(),j.getX(), j.getY(), null);}
this.paint(g);
}
and let the overridden paintComponent(Graphics g) method above it do all the work. Additionally, instead of the paintComponents(graphics) calls, use repaint(). Also you can get rid of the first call to paintComponents(graphics) in the constructor as it will paint the first time by default. If you really want to use your own method then you have to create a Graphics object and pass that in.
Lastly, in the overridden paintComponents(Graphics g) method, you have the last line being to draw the giant black box. This will then cover up anything you've drawn before. So you should have that as the first line and draw everything else in order such that the thing you want to be on top should be drawn last. I was able to get my test image to show up with the following code for that class. I don't think I changed anything else.
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class GameView extends JComponent implements ActionListener{
/**
*
*/
private static final long serialVersionUID = -2869672245901003704L;
private boolean liveGame;//used so that buttons cannot be clicked after game is complete
private GameState gs;
private Player p;
private int w, h;
public GameView(int width, int height)
{
liveGame = true;
gs = new GameState();
GameState.init(width, height);
p = new Player(width/2,(height*7)/8);
this.setBackground(Color.BLACK);
w = width;
h = height;
}
#Override
public Dimension getMinimumSize() {
return new Dimension(w, h);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(w, h);
}
#Override
public void paintComponent(Graphics g) {
int margin = 10;
Dimension dim = getSize();
super.paintComponent(g);
g.setColor(Color.black);
GameState.update();
g.fillRect(margin, margin, dim.width - margin * 2, dim.height - margin * 2);
for(Bullet j : GameState.getEnBullets()){
g.drawImage(j.getImage(),j.getX(), j.getY(), null);}
for(Enemy j : GameState.getEnemies()){
g.drawImage(j.getImage(),j.getX(), j.getY(), null);}
for(Bullet j : GameState.getPlayBullets()){
g.drawImage(j.getImage(),j.getX(), j.getY(), null);}
g.drawImage(p.getImage(),p.getX(),p.getY(),null);
}
public void refreshImage()
{
this.removeAll();
repaint();
}
public void actionPerformed(ActionEvent e) {
}
}
The other thing is in some of your other classes you have #Override over the actionPerformed method. My IDE doesn't like that, although it does compile. It says "#Override is not allowed when implementing interface methods."
Hopefully this works for you.
try adding repaint(); after you make a change to a content pane. I dont think concurrency is going to be a problem unless you clog up your EDT.

Categories

Resources