I have Multi-user Chat-room with shared whiteboard application where I used a Jpanel to draw in a specific client and then broadcast over server to other clients(through Java Socket Programming).
My issue is that I wanted to make the function of draw work real-time as in as soon as a drawing is done on one client's JPanel it should be visible to the other clients. I wrote the function on mouseReleased event of JPanel, but it is visible to the other clients only after a mouseReleased event is fired on that client's JPanel.
Can anyone suggest something by which I can make the action better(real-time)?
#Override
public void mouseReleased(MouseEvent e) {
lineObject = new LineMessage();
lineObject.setImageMessage(DrawPanel.linelist);
ChatApplication_Client.Action_Paint(lineObject);
}
ChatApplication_Client.java
public void run(){
System.out.println("Listening for messages from server . . . ");
try{
while(!receivingdone){
object = myInputStream.readObject();
if(object instanceof LineMessage)
{
lineObject = (LineMessage) object;
WhiteBoardMessageReceive(lineObject);
}
}
}
// This method responsible for re-painting and broadcasting at client's end
private void WhiteBoardMessageReceive(LineMessage lineObject)
{
ArrayList<Line> linelist = (ArrayList)
lineObject.getImageMessage();
ChatClient_GUI.TA_ChatWindow.append(lineObject.Name+": "
+lineObject.Text + "\n" + "At ["
+DateUtils.now()+ "] " + "\n");
drawPanel.drawit(linelist);
}
//The Following method is called from the gui on mouseReleased event
public static void Action_Paint(LineMessage lineObject)
{
try
{
myOutputStream.reset();
myOutputStream.writeObject(lineObject);
myOutputStream.flush();
}
catch(Exception e)
{
System.out.println(e.getMessage());
}
}
LineMessage.java
class LineMessage implements Serializable
{
ArrayList<Line> message;
Line line = new Line();
String Name =line.getName() ;
String Text ;
public void setImageMessage(Object message) {
this.message = (ArrayList) message;
}
public Object getImageMessage() {
return message;
}
}
class Line extends ChatMessage implements Serializable {
int startx, starty, endx, endy;
public Line() {
}
public Line(int sx, int sy, int ex, int ey)
{
setStartX(sx);
setStartY(sy);
setEndX(ex);
setEndY(ey);
}
public void setStartX(int sx) {
startx = sx;
}
public void setStartY(int sy) {
starty = sy;
}
public void setEndX(int ex) {
endx = ex;
}
public void setEndY(int ey) {
endy = ey;
}
public int getStartX() {
return startx;
}
public int getStartY() {
return starty;
}
public int getEndX() {
return endx;
}
public int getEndY() {
return endy;
}
}
List<Point> pointList = new ArrayList<>();
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 500);
JPanel panel = new JPanel(){
#Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.BLACK);
Point l = null;
for(Point p : pointList){
if(l != null && p != null)
g.drawLine(l.x, l.y, p.x, p.y);
l = p;
}
}
};
frame.setContentPane(panel);
panel.addMouseMotionListener(new MouseMotionListener() {
boolean isDrawing = false;
#Override
public void mouseDragged(MouseEvent e) {
pointList.add(e.getPoint());
isDrawing = true;
panel.repaint();
}
#Override
public void mouseMoved(MouseEvent e) {
if(isDrawing)
pointList.add(null); //null is a placeholder to mean line is finished
isDrawing = false;
}
});
frame.setVisible(true);
Related
When I use normal JPanel initialized inside main instead of extended class. The button is added to a panel without problem and after launch is displayed in a center of a frame (default layout).
I would like to be able to add buttons inside an extended class. This problem occurs in Screen class aswell, where I need a Play Again button or Next Level button. The Screen is class extended by a JPanel too and JButtons are initialized inside the constructor aswell.
I'm not sure if the wrong part is in way of adding the components or in writing a code for a JPanel.
Here is the code:
Main:
public static void main(String[] args) {
// window - class JFrame
theFrame = new JFrame("Brick Breaker");
// game panel initialization
GamePanel gamePanel = new GamePanel(1);
theFrame.getContentPane().add(gamePanel);
// base settings
theFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
theFrame.setLocationRelativeTo(null);
theFrame.setResizable(false);
theFrame.setSize(WIDTH, HEIGHT);
theFrame.setVisible(true);
}
GamePanel Class:
public class GamePanel extends JPanel {
// Fields
boolean running;
boolean clicked = false;
private int rows = 8, colms = 11, N = rows * colms, level; // numbers of rows and columns
private int colmsC, rowsC;
private BufferedImage image;
private Graphics2D g;
private MyMouseMotionListener theMouseListener;
private MouseListener mouseListener;
private int counter = 0;
// entities
Ball theBall;
Paddle thePaddle;
Bricle[] theBricks;
Screen finalScreen;
public GamePanel(int level) {
// buttons
JButton pause = new JButton(" P ");
add(pause);
this.level = level;
init(level);
}
public void init(int level) {
// level logic
this.rowsC = level + rows;
this.colmsC = level + colms;
int count = rowsC * colmsC;
thePaddle = new Paddle();
theBall = new Ball();
theBall.setY(thePaddle.YPOS - thePaddle.getHeight() + 2);
theBall.setX(thePaddle.getX() + thePaddle.getWidth() / 2 - 5);
theBricks = new Bricle[count];
theMouseListener = new MyMouseMotionListener();
addMouseMotionListener(theMouseListener);
mouseListener = new MyMouseListener();
addMouseListener(mouseListener);
// make a canvas
image = new BufferedImage(BBMain.WIDTH, BBMain.HEIGHT, BufferedImage.TYPE_INT_RGB);
g = (Graphics2D) image.getGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// specific Bricks initialized
int k = 0;
for (int row = 0; row < rowsC; row++) {
for (int col = 0; col < colmsC; col++) {
theBricks[k] = new Bricle(row, col);
k++;
}
}
running = true;
}
public void playGame() {
while (running) {
//update
if (clicked) {
update();
}
// draw
draw();
// display
repaint();
// sleep
try {
Thread.sleep(20);
} catch (Exception e) {
e.printStackTrace();
}
}
}
// update loop ( like playGame )
public void update() {
// ball moving
checkCollisions();
theBall.update();
}
public void draw() {
// background
g.setColor(Color.WHITE);
g.fillRect(0,0, BBMain.WIDTH, BBMain.HEIGHT-20);
// the bricks
int k = 0;
for (int row = 0; row < rowsC; row++) {
for (int col = 0; col < colmsC; col++) {
theBricks[k].draw(g, row, col);
k++;
}
}
// the ball and the paddle
theBall.draw(g);
thePaddle.draw(g);
// counter
String countString = new Integer(this.counter).toString();
g.drawString(countString, 20, 20);
// WIN / LOOSE SCREEN
if (this.counter == this.N * 20) {
win();
}
if (theBall.getRect().getY() + theBall.getRect().getHeight() >= BBMain.HEIGHT) {
loose();
}
}
public void paintComponent(Graphics g) {
// retype
Graphics2D g2 = (Graphics2D) g;
// draw image
g2.drawImage(image, 0, 0, BBMain.WIDTH, BBMain.HEIGHT, null);
// dispose
g2.dispose();
}
public void pause() {
this.running = false;
finalScreen = new Screen(this.level, counter);
finalScreen.draw(g,"GAME PAUSED");
}
public void win() {
this.running = false;
finalScreen = new Screen(this.level, counter);
finalScreen.draw(g,"YOU WIN");
}
public void loose () {
this.running = false;
finalScreen = new Screen(this.level, counter);
finalScreen.draw(g,"YOU LOOSE");
}
public void addScore() {
this.counter += 20;
theBall.setDY(theBall.getDY() - 0.001);
}
// Mouse Listeners
private class MyMouseListener implements MouseListener {
#Override
public void mouseClicked(MouseEvent e) {
clicked = true;
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
}
private class MyMouseMotionListener implements MouseMotionListener {
#Override
public void mouseDragged(MouseEvent e) {
}
#Override
public void mouseMoved(MouseEvent e) {
if (clicked)
thePaddle.mouseMoved(e.getX());
}
}
}
Another question about inheritance in java: I was wondering about two things:
1) how can I set my program to switch between using an inherited class or not?
2) I'm not sure why my extended class glidingObject.java is not responding to my key presses
Here's my Game.java (which runs the game; I should be passing in some parameter that allows the user to choose which class to use right - either flying object or gliding object? I've also included my two classes for flying object and gliding object)
public class Game extends JPanel {
private static final long serialVersionUID = 1L;
public static int WIDTH = 800, HEIGHT = 750;
public static ArrayList<Rectangle> columns;
public static Random rand;
public static double score;
public static boolean gameOver, started; //two modes, started and gameover
public static String username;
public static String currTime;
public static Timer timer;
public static flyingObject obj;
private PropertyChangeSupport mPcs = new PropertyChangeSupport(this);
public Game(flyingObject object){
obj = object;
timer = new Timer(20, new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
tick();
} catch (IOException e1) {
e1.printStackTrace();
}
}
});
columns = new ArrayList<Rectangle>();
rand = new Random();
Background bk = new Background();
addColumn(true);
addColumn(true);
addColumn(true);
addColumn(true);
addMouseListener(new MouseAdapter() {
#Override
public void mouseReleased(MouseEvent e) {
started = true;
}
});
PropertyChangeListener listener = new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
try {
scoreBoard.record();
} catch (IOException e) {
e.printStackTrace();
}
}
};
this.addPropertyChangeListener(listener);
getDate(); //get the date of the game played
mPcs.firePropertyChange("gameOver",false, true); //alert record() method when the game is over
timer.start();
}
//adding the column
public static void addColumn(boolean start) {
int space = 300;
int width = 100;
int height = 50 + rand.nextInt(300);
if (start) {
//add top and bottom
columns.add(new Rectangle(WIDTH + width + columns.size() * 300, HEIGHT - height - 120, width, height + 100));
columns.add(new Rectangle(WIDTH + width + (columns.size() - 1) * 300, 0, width, HEIGHT - height - space));
}
else
{
columns.add(new Rectangle(columns.get(columns.size() - 1).x + 600, HEIGHT - height - 120, width, height + 100));
columns.add(new Rectangle(columns.get(columns.size() - 1).x, 0, width, HEIGHT - height - space));
}
}
//paint the columns
public void paintColumn(Graphics g, Rectangle column) {
g.setColor(Color.white);
g.fillRect(column.x, column.y, column.width, column.height);
}
public static void reset() {
obj = new glidingObject(WIDTH / 2 - 10, HEIGHT / 2 - 10);
columns.clear();
score = 0.0;
addColumn(true);
addColumn(true);
addColumn(true);
addColumn(true);
gameOver = false;
}
public void tick() throws IOException {
if (started) {
int speed = 10;
glidingObject.move();
for (int i = 0; i < columns.size(); i ++) {
Rectangle column = columns.get(i);
column.x -= speed;
if (column.x + column.width < 0) {
columns.remove(column);
if (column.y == 0) {
addColumn(false);
}
}
}
for (Rectangle column: columns) {
if (column.x == glidingObject.X) {
score += 0.5;
}
if (column.intersects(glidingObject.getBounds())) {
gameOver = true;
//when the object crashes, it does not go through the column
if (glidingObject.X <= column.x) {
glidingObject.X = column.x - glidingObject.DIAMETER;
}
else if (glidingObject.Y < column.height) {
glidingObject.Y = column.height;
}
Main.gameOver();
}
}
if (glidingObject.Y > HEIGHT || glidingObject.Y < 0) {
gameOver = true;
//timer.stop();
Main.gameOver();
}
if (glidingObject.Y + glidingObject.YMotion >= HEIGHT) {
gameOver = true;
//timer.stop();
Main.gameOver();
}
}
//update the display
repaint();
}
public void getDate() {
DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
Date date = new Date();
currTime = dateFormat.format(date);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Background.paint(g);
glidingObject.paint(g);
for (Rectangle column: columns) {
paintColumn(g, column);
}
g.setColor(Color.white);
g.setFont(new Font("Manaspace", 1, 60));
if (!started) {
g.drawString("Click to start!", 75, HEIGHT / 2 - 50);
}
if (gameOver) {
g.drawString("Game Over!", 200, HEIGHT / 2 - 50);
}
if (!gameOver && started) {
g.drawString(String.valueOf(score), WIDTH / 2 - 25, 100);
}
}
}
class flyingobject.java
public class flyingObject implements ActionListener, MouseListener, KeyListener {
static int DIAMETER = 25;
static int Y; // Y position of the unicorn
static int X; // X position of the unicorn
static int YMotion; // Y movement of the unicorn
//parameters are the initial positions
public flyingObject(int xpos, int ypos) {
X = xpos;
Y = ypos; // this changes
}
//getters
public int getX() {
return X;
}
public int getY() {
return Y;
}
//setters
public void setX(int newX) {
X = newX;
}
public void setY(int newY) {
Y = newY;
}
//the bounds of the object (rectangle)
public static Rectangle getBounds() {
return new Rectangle(X, Y, DIAMETER, DIAMETER);
}
//the visible component of the object - this can get overriden by subclasses
public static void paint(Graphics g){
g.setColor(Color.white);
g.fillRect(X, Y, DIAMETER, DIAMETER);
}
//the movement component of the object
public static void jump() {
if (Game.gameOver) {
Game.reset();
Game.gameOver = false;
}
if (!Game.started) {
Game.started = true;
}
else if (!Game.gameOver) {
if (YMotion > 0) {
YMotion = 0;
}
YMotion -= 14;
}
}
public static void move() {
if ((Y > 0) && (Y < Game.HEIGHT)) {
YMotion += 1.5; // gravity
Y += YMotion;
}
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE)
{
jump();
}
}
#Override
public void mouseClicked(MouseEvent e) {
jump();
}
#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 actionPerformed(ActionEvent e) {
}
}
Class glidingobject.java (the game.java should allow the user to choose between just using flying object, or the extended class gliding object)
public class glidingObject extends flyingObject{
//velocity of the object
private static int vx;
private static int vy;
private static int lives;
public glidingObject(int xpos, int ypos) {
super(xpos, ypos);
vx = 0;
vy = 0;
lives = 3;
}
//getter methods
public int getVx() {
return vx;
}
public int getVy() {
return vy;
}
//setter methods
public void setVx(int newVx) {
vx = newVx;
}
public void setVy(int newVy) {
vy = newVy;
}
//moves the object
public static void jump() {
X += vx;
Y += vy;
}
public static void paint(Graphics g) {
g.setColor(Color.CYAN);
g.fillOval(X, Y, DIAMETER, DIAMETER);
}
#Override
public void keyTyped(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_RIGHT){
vx = 10;
}
if (e.getKeyCode() == KeyEvent.VK_LEFT){
vx = -10;
}
if (e.getKeyCode() == KeyEvent.VK_UP){
vy = 10;
}
if (e.getKeyCode() == KeyEvent.VK_DOWN){
vy = -10;
}
}
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
}
#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 actionPerformed(ActionEvent e) {
}
}
I should be passing in some parameter that allows the user to choose which class to use right - either flying object or gliding object?
This is where interfaces become so powerful. Both the "flying" and "gliding" objects are going to share some common properties/functionality, these should be described by the interface. You Game should then only accept instances of this interface, it shouldn't care what the implementation, only that they adhere to the agreed interface.
This is principle of "code to interface, not implementation"
How complicated this gets is up to you, for example, you could have abstract implementations which describe "air" based entities and "ground" based entities, from which you could have "powered" and "unpowered" implementations of the abstract "air" class, all of which would be tied back to the "game entity" interface
I'm not sure why my extended class glidingObject.java is not responding to my key presses
This is because you're using KeyListener, this well known for it's focus related issues. See How to Use Key Bindings for the recommended solution
Basically, I'm making a "scary" maze game and want the game over picture to change to a different picture every time you fail without having to recompile. Currently, the 'game over' picture stays the same every time you lose until you recompile.
public class MazeGame extends JComponent implements MouseMotionListener, MouseListener
{
private Random generator = new Random();
BufferedImage intro;
BufferedImage level1;
BufferedImage level2;
BufferedImage level3;
BufferedImage pic1;
BufferedImage pic2;
BufferedImage pic3;
BufferedImage pic4;
BufferedImage pic5;
BufferedImage gameOver;
BufferedImage currentLevel;
AudioClip spooky = JApplet.newAudioClip(getClass().getResource("gr/spooky.aiff"));
public MazeGame() throws IOException
{
intro = ImageIO.read(getClass().getResource("gr/start.png"));
level1 = ImageIO.read(getClass().getResource("gr/level1.png"));
level2 = ImageIO.read(getClass().getResource("gr/level2.png"));
level3 = ImageIO.read(getClass().getResource("gr/level3.png"));
pic1 = ImageIO.read(getClass().getResource("gr/pic1.jpg"));
pic2 = ImageIO.read(getClass().getResource("gr/pic2.jpg"));
pic3 = ImageIO.read(getClass().getResource("gr/pic3.jpg"));
pic4 = ImageIO.read(getClass().getResource("gr/pic4.jpg"));
pic5 = ImageIO.read(getClass().getResource("gr/pic5.jpg"));
int number = 1 + generator.nextInt( 5 );
gameOver = ImageIO.read(getClass().getResource( "gr/pic" + number + ".jpg" ));
currentLevel = intro;
}
public static void main(String args[]) throws IOException
{
JFrame window = new JFrame("Totally Not Scary Maze Game");
MazeGame game = new MazeGame();
window.add(game);
window.pack();
window.setLocationRelativeTo(null);
window.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
window.setVisible(true);
game.addMouseMotionListener(game);
game.addMouseListener(game);
}
public Dimension getPreferredSize() {
return new Dimension(800, 800);
}
protected void paintComponent(Graphics g) {
g.setColor(Color.RED);
g.fillRect(0, 0, 800, 800);
g.drawImage(currentLevel, 0, 0, null);
}
public void mouseMoved(MouseEvent e) {
int x = e.getX();
int y = e.getY();
int color = currentLevel.getRGB(x, y);
System.out.println(color);
int level1WallColor = -13939918;
int level2WallColor = -10340732;
int level3WallColor = -4640206;
int goalColor = -14532251;
if(color == goalColor)
{
if(currentLevel == intro)
{
currentLevel = level1;
}
else if(currentLevel == level1)
{
currentLevel = level2;
}
else if(currentLevel == level2)
{
currentLevel = level3;
}
else if(currentLevel == level3)
{
showGameOver();
}
}
if(color == level1WallColor)
{
showGameOver();
}
if(color == level2WallColor)
{
currentLevel = intro;
}
if(color == level3WallColor)
{
showGameOver();
}
repaint();
}
private void showGameOver()
{
currentLevel = gameOver;
spooky.play();
}
public void mouseClicked(MouseEvent e)
{
if(currentLevel == gameOver)
{
currentLevel = intro;
}
repaint();
}
public void mousePressed(MouseEvent e)
{
}
public void mouseReleased(MouseEvent e)
{
}
public void mouseEntered(MouseEvent e)
{
}
public void mouseExited(MouseEvent e)
{
}
public void mouseDragged(MouseEvent e)
{
}
}
That is because you are always setting cuurentLevel as Intro at the the end Mazegame(). Comment this line and try.
currentLevel = intro;
Also I read your code superficially.. Not sure I see where exactly the switching of the picture will happen. You will call your changePic(i.e your mazegame()) right after where the fail is evaluated. so Everytime the evaluation happens failed user will get a changed pic.
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);
}
}
}
I've been asked to make a game for my CS (high school) class as my end of semester assignment. We haven't been taught properly all of the coding required to make a game so my knowledge in this area is very poor. Anyways, the game I am trying to make is something like "Flappy Fall" (an Apple appstore game) where objects fall from the top of the screen and descend to the bottom of the screen. The objective is to catch these objects before they reach the bottom. I am able to get one object to fall and have also created the "catcher", but I am not sure how to create multiple falling objects, nor do I know how to remove the object once it has been caught by the catcher. So far I have classes "JavaGame", "Catcher", and "Ball". Any help would be greatly appreciated.
int x, y;
int t = 1;
private Image dbImage;
private Graphics dbGraphics;
Image player;
Image bkg;
static Catcher p = new Catcher(150, 450);
public JavaGame() {
//Game Images
ImageIcon b = new ImageIcon("bkg.png");
bkg = b.getImage();
//Game properties
setTitle("Game");
setSize(350, 600);
setResizable(false);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
addKeyListener(new Keys());
addMouseMotionListener(new Mouse());
}
public void paint(Graphics g) {
//
dbImage = createImage(getWidth(), getHeight());
dbGraphics = dbImage.getGraphics();
draw(dbGraphics);
g.drawImage(dbImage, 0, 0, this);
}
public void draw(Graphics g) {
g.drawImage(bkg, 0, 0, this); //Creates background
p.draw(g);
//while (t < 100) {
p.b.draw(g);
//t++;
//}
g.setColor(Color.WHITE);
g.drawString(""+p.score, 175, 50);
repaint();
}
public static void main(String[] args) {
JavaGame jg = new JavaGame();
//Threads
Thread p1 = new Thread(p);
p1.start();
Thread ball = new Thread(p.b);
ball.start();
}
public class Keys extends KeyAdapter {
public void keyPressed(KeyEvent e) {
p.keyPressed(e);
}
public void keyReleased(KeyEvent e) {
p.keyReleased(e);
}
}
public class Mouse implements MouseMotionListener {
public void mouseDragged(MouseEvent e) {
p.mouseDragged(e);
}
public void mouseMoved(MouseEvent e) {
p.mouseMoved(e);
}
}
}
int x, y, ranX, xDirection;
int score;
Rectangle catch1;
Ball b = new Ball(170, 1);
public Catcher (int x, int y) {
score = 0;
this.x = x;
this.y = y;
catch1 = new Rectangle(this.x, this.y, 50, 15);
}
public void run() {
try {
while(true) {
move();
Thread.sleep(5);
}
}
catch(Exception e) {
System.out.println("Error");
}
}
public void collision() {
if (b.ball.intersects(catch1)) {
b.ball(Color.blue);
score++;
System.out.println(score);
}
}
public void move() {
collision();
catch1.x += xDirection;
if (catch1.x <= 0)
catch1.x = 0;
if (catch1.x >= 300)
catch1.x = 300;
}
public void setXDirection(int xDir) {
xDirection = xDir;
}
public void keyPressed(KeyEvent m) {
int keyCode = m.getKeyCode();
if (keyCode == m.VK_LEFT) {
setXDirection(-1);
}
if (keyCode == m.VK_RIGHT) {
setXDirection(+1);
}
m.consume();
}
public void keyReleased(KeyEvent m) {
int keyCode = m.getKeyCode();
if (keyCode == m.VK_LEFT) {
setXDirection(0);
}
if (keyCode == m.VK_RIGHT) {
setXDirection(0);
}
m.consume();
}
public void mouseDragged(MouseEvent e) {
catch1.x = e.getX()-25;
e.consume();
}
public void mouseMoved(MouseEvent e) {
catch1.x = e.getX()-25;
e.consume();
}
public void draw(Graphics g) {
g.fillRect(catch1.x, catch1.y, catch1.width, catch1.height);
}
}
int x, y, yDirection;
Rectangle ball;
public Ball (int x, int y) {
this.x = x;
this.y = y;
ball = new Rectangle(this.x, this.y, 10, 10);
}
public void run() {
try{
while(true) {
move();
Thread.sleep(5);
}
}
catch(Exception e) {
System.out.println("Error");
}
}
public void move() {
if (ball.y >= 600) {
ball.y = 600;
}
if (ball.y > 0) {
ball.y++;
}
}
public void setYDirection(int yDir) {
yDirection = yDir;
}
public void draw(Graphics g) {
g.setColor(Color.WHITE);
g.fillRect(ball.x, ball.y, ball.width, ball.height);
System.out.println(ball.x+ " "+ ball.y+ " " + ball.width + " " + ball.height);
}
}
I'd re-organize the code a little. In the main game, you can have a collection of 'Ball' types. I'll leave the collection option up to you. But you'll want to add 'new' Balls to the collection and then remove them once they are caught.
Since I do not want to do your assignment, I am just giving a short answer:
By calling new Ball() multiple times.
Of course you might want to add them to a collection, like
List list = new ArrayList();
list.add(ball);
And remove them from that collection once they are finished.