1

I am trying to create a game. In this game, the UI shows multiple paths(JPanels) with obstacles (some sort of JComponent) moving down the paths. The user (Player) must move left and right or switch paths to avoid the obstacles. This game is similar to google Pony Express.

https://www.google.com/doodles/155th-anniversary-of-the-pony-express

For now, because I am in the developing stages, I only have one path. I have a Player class that extends JComponent and overrides the paint Component method and draws a blue square. I have a Obstacle Component Class that also extends JComponent and overrides the paint Component method and draws a black circle.

In my Board class, I create a JPanel called theGame and put one path and inside of that path, one obstacle component and one player. This JPanel is put into a JFrame.

The player is free to move using a key listener. I will keep the obstacle component in one place to make debugging easier. This is not the main problem. The problem is that I want to end the game when the player intersects the obstacle. I create skeleton rectangles that resembles the body of the component. So for example the rectangle for the blue square is just the square and for the circle, a rectangle that covers the most area inside of it. So I call the intersects method on the two rectangles and if true, I game over. However, the intersect method does not work. The intersects method returns true even when the player and obstacle are not visibly touching. (No, there is no problem with misproper refreshing or anything like that). How do I intersect those two objects? Code will be provided if the answerer needs it.

Observation: I am not sure but I believe that the Graphics objects used to draw the player and obstacle are different. To test this hypothesis, I drew a green square in each of the paintComponent methods like this:

public void paintComponent(Graphics g)
{
    //code to create the blue square
    g2 = (Graphics2D) g;
    rect = new Rectangle(x,y,w,h);
    g2.setColor(Color.BLUE);
    g2.fill(rect);
    g2.draw(rect);

    //code to see the green rectangle
    Rectangle bill = new Rectangle(100,100,w,h);
    g2.setColor(Color.GREEN);
    g2.fill(bill);
    g2.draw(bill);
}

The reasoning was that if I paint a green rectangle with seemingly same coordinates using the graphics objects of player and obstacle, they should end up in the same place on the JPanel. Instead, the green Rectangle objects showed up in two different places. This lead me to believe that the graphic objects use different coordinate systems. If so, how to use one universal coordinate system for multiple graphic objects?

MCVE: I could not make the classes any smaller because the code was not working if I did that. Player Class:

import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
import java.util.*;
import java.awt.event.*;
public class Player extends JComponent
{
private int x;
private int y;
private int h = 20;
private int w = 20;
private Rectangle rect;
private int dx=5; 
private int dy=5;
private Graphics2D g2;
public Player(int x, int y)
{
    this.x = x;
    this.y = y;
    rect= new Rectangle(x,y,w,h);
}

public void assignGraphics(Graphics g)
{
    g2 = (Graphics2D)g;
}

public Player()
{
    x=0;
    y=0;
}

public void paintComponent(Graphics g)
{

    g2 = (Graphics2D) g;
    rect = new Rectangle(x,y,w,h);
    g2.setColor(Color.BLUE);
    g2.fill(rect);
    g2.draw(rect);


    Rectangle test = new Rectangle(100,100,w,h);
    g2.setColor(Color.GREEN);
    g2.fill(test);
    g2.draw(test);
}

public Rectangle getRectComp()
{
    return rect;
}

public void setMove(int a, int b)
{
    x=x+a; 
    y=y+b;
    repaint();
}

public int getX()
{
    return x;
}

public int getY()
{
    return y;
}

}

Obstacle Component:

import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
import java.util.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.net.URL;
import javax.imageio.ImageIO;
import java.io.IOException;
public class ObstacleComponent extends JComponent
{
private int x;
private int y;
public Ellipse2D.Double elli;
private int dx=5; 
private int dy=5;
private int h;
private int w;
private Graphics2D g2;
public ObstacleComponent(int x, int y, int h, int w)
{
    this.x = x;
    this.y = y;
    this.h = h;
    this.w = w;
}

public void assignGraphics(Graphics g)
{
    g2 = (Graphics2D)g;
}

public ObstacleComponent()
{
    x=0;
    y=0;
}

public void paintComponent(Graphics g) // method declaration- a method which overrides the paintComponent method from the JComponent class. 
// This method takes in an explicit parameter of a Graphics object
{
    g2 = (Graphics2D) g;
    elli = new Ellipse2D.Double(x,y,w,h);
    g2.setColor(Color.BLACK);
    g2.fill(elli);
    g2.draw(elli);
    Rectangle test= new Rectangle(0,0,w,h);
    g2.setColor(Color.GREEN);
    g2.fill(test);
    g2.draw(test);
}

public void setMove(int a, int b)
{
    x=x+a; // sets the dx variable to the value of parameter variable a
    y=y+b;
    repaint();
}

public int getX()
{
    return x;
}

public int getY()
{
    return y;
}

public Rectangle getRectComp()
{
    return new Rectangle(x,y,20,20);
}
}

Path:

import javax.swing.*;
import java.awt.*; 
import java.awt.geom.*; 
import javax.swing.*; 
import java.util.*;
import javax.swing.plaf.TableUI;
import javax.swing.table.TableColumn;
import java.awt.event.*;
import javax.swing.border.EtchedBorder;
public class Path extends JPanel implements ActionListener
{
public ArrayList <ObstacleComponent> badGuys;
private int interval;
private int xDimension = 1000;
private int yDimension = 600;
private Graphics2D pen;
public Path(int rate)
{
    badGuys = new ArrayList<ObstacleComponent>();
    setPreferredSize(new Dimension(xDimension, yDimension));
    setBorder(new EtchedBorder());
    setLayout(new GridLayout(1,1));
}

public Graphics2D getG()
{
    return pen;
}

public void actionPerformed(ActionEvent av)
{
    if(interval%10==0)
    {  
        badGuys.add(new ObstacleComponent(200,10,20,20));
        add(badGuys.get(badGuys.size()-1));
        badGuys.get(badGuys.size()-1).assignGraphics(pen);
    }
    for(int b = 0;b<badGuys.size();b++)
    {

        badGuys.get(b).setMove(-5,0);

    }

    removeObstaclesOffTheScreen();
    repaint();
    interval++;
}

public void removeObstaclesOffTheScreen()
{
    for(int b = 0;b<badGuys.size();b++)
    {
        if(badGuys.get(b).getX()<0)
        {
            remove(badGuys.remove(b));
        }
    }
}

}

Board:

import javax.swing.*;
import java.awt.*; 
import java.awt.geom.*; 
import javax.swing.*; 
import java.util.*;
import javax.swing.plaf.TableUI;
import javax.swing.table.TableColumn;
import java.awt.event.*;
import javax.swing.border.EtchedBorder;
public class Board  implements ActionListener {

    // instance variables - replace the example below with your own
    private ArrayList<Path> paths; 
    public JPanel theGame;
    private  int interval;
    private int currentPlayerPath;
    public Player player;
    private int pathNumber = 1;//4;
    private int levelTime;
    private int levelRate;
    private boolean notGameOver;
    private int removeThisCounter=0;
    private int timeToChangeRate;
    private Random gen;
    private int levelRange = 20;
    public Board()
    {
        theGame = new JPanel();
        setLayout(pathNumber);
        player = new Player();
        levelRate = 0;
        paths =  new ArrayList<Path>();  
        gen = new Random();
        for(int a = 0;a<pathNumber;a++)
        {
            paths.add(new Path(getRandomRate()));
        }
        for(int a = 0;a<pathNumber;a++)
        {
            theGame.add(paths.get(a));
        }
        currentPlayerPath=0;
        setCurrentPlayerPath();
    }

    public JPanel returnTheGamePanel()
    {
        return theGame;
    }


    public void setLayout(int paths)
    {
        theGame.setLayout(new GridLayout(pathNumber, paths));
    }


    public void setCurrentPlayerPath()
    {
        paths.get(currentPlayerPath).add(player);
        player.assignGraphics(paths.get(currentPlayerPath).getG());
    }

    public boolean hit()
    {
        Path currentPath = paths.get(currentPlayerPath);
        for(int a = 0;a<currentPath.badGuys.size();a++)
        {
            Object b = player.getRectComp();
            Object c = currentPath.badGuys.get(a).getRectComp();
            if (((Rectangle)b).intersects((Rectangle)c))
            {
                return true;
            }
            else
            {
                System.out.println("player: " + player.getX() + " " + player.getY());
                System.out.println("badguy: " + currentPath.badGuys.get(a).getX() + " " + currentPath.badGuys.get(a).getY());             
            } 
        }
        return false;
    }

    public void actionPerformed(ActionEvent av)
    {
        for(int a = 0;a<pathNumber;a++)
        {
            paths.get(a).actionPerformed(av);  
        }

        player.repaint();
        theGame.revalidate();
        theGame.repaint();
    }

    public void gameOver()
    {  
        notGameOver=!notGameOver;
    }

    public boolean isGameOver()
    {
        return notGameOver;
    }
}

Engine:

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import javax.sound.sampled.*;
import java.io.*;
import java.awt.geom.*;

public class Engine {

public static void main (String[] args) {        
    JOptionPane.showMessageDialog(null, "Press arrow key to move.");
    final JFrame display = new JFrame(); 
    display.setSize(1000, 600); 
    display.setTitle("Code does not work"); 
    display.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
    final Board b = new Board();
    class TimedAction implements ActionListener
    {
        private int removeThisCounter=0;
        public void actionPerformed(ActionEvent av)
        {
            b.actionPerformed(av);
            if(b.hit())
            {
                System.out.println("HIT!!"+removeThisCounter);
                removeThisCounter++;
            }               

            display.revalidate();
            display.repaint();
        }

    }
    class PlayerAction implements KeyListener
    {
        public void keyPressed (KeyEvent e) { 
            int keyCode = e.getKeyCode(); 
            if(keyCode==KeyEvent.VK_LEFT)
            {
                b.player.setMove(-7, 0);

            }
            if(keyCode==KeyEvent.VK_RIGHT)
            {
                 b.player.setMove(7, 0);

            }
            if(keyCode==KeyEvent.VK_UP)
            {
                b.player.setMove(0,-7);

            }
            if(keyCode==KeyEvent.VK_DOWN)
            {
                 b.player.setMove(0,7);

            }
            else
            {
                b.player.setMove(0,0);

            }               
        }

        public void keyReleased(KeyEvent e) {} //method delcaration for empty methods needed to override the abstract methods of the KeyListener interface
        public void keyTyped(KeyEvent e) {}
    }
    Timer theTime = new Timer(1000, new TimedAction());
    theTime.start();
    JPanel masterPanel = new JPanel();
    display.addKeyListener(new PlayerAction()); 
    masterPanel.add(b.returnTheGamePanel());
    display.add(masterPanel);
    display.setVisible(true); 
}
}
Morchul
  • 1,987
  • 1
  • 7
  • 21
  • The coordinates are relative to the component. So `0, 0` for the Graphics passed in to `paintComponent` is the top-left corner of the component you are painting. *"This lead me to believe that the graphic objects use different coordinate systems."* So yes, I think you have the right idea. How to fix it for your program, I don't know exactly. Consider posting a [minimal, complete example](http://stackoverflow.com/help/mcve) for an answer to this. Also please take a look at [this answer](http://stackoverflow.com/a/30175751) which shows an example of animation without components. – Radiodef May 14 '15 at 02:29
  • Try printing the location of both of the components. You can do `System.out.println("Component1: " + component1.getBounds());` and like wise for your second component. It's most likely that you haven't layered your two components exactly on top of eachother so yes, the Graphic object will be set to a different coordinate root. – Erwin Bolwidt May 14 '15 at 02:31
  • *"Somehow, you've got to get this requirement into 'graphics-coordinate space terms,'"* no matter how, when, or by-whom it manages to get there." Therefore, I suggest that you need to define another(!) set of classes which are *only* concerned with "the *game* state." These classes do not "do" anything. They do not "paint" anything, and so-on. Rather: **they are The Masters Of 'The Game State' At The Present Time."* *They* are "the authority." The classes which "draw things" and which "handle user inputs" are merely *servants* of these master-classes. – Mike Robinson May 14 '15 at 03:08
  • To further clarify the above: "all of the various classes which 'draw things'" ... only 'draw things' when The Masters *(via callbacks, etc.)* tell them to." Likewise, "all of the various classes which handle user inputs" ... merely inform The Masters that such events have occurred. (And, to side-step any sort of timing problems, perhaps they "post messages to a queue.") The Masters, being the (only) true arbiters of the game-state, are thus able to realize that "two objects have intersected," and to issue appropriate commands to their subordinates. – Mike Robinson May 14 '15 at 03:11
  • For better help sooner, post an [MCVE](http://stackoverflow.com/help/mcve) (Minimal Complete Verifiable Example) or [SSCCE](http://www.sscce.org/) (Short, Self Contained, Correct Example). – Andrew Thompson May 14 '15 at 03:50
  • @Radiodef, Mr. Bolwidt, Mr. Robinson, Mr. Thompson, Thank you for the swift comments you made. I will input your comments and add an MCVE – Shaurey Vetsa May 14 '15 at 04:43

0 Answers0