I'm using the Java AWT class to make a wall made up of rectangles on both sides. The wall class extends rectangles, and uses the same variables to draw to the screen.
Walls Class:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
public class Walls extends Rectangle {
public Walls(int startX, int startY, int width, int height ){
//super(startX, startY, width, height);
//
//Checking with this way for the collision detection
this.x = startX;
this.y = startY;
this.width = width;
this.height = height;
//this.wallStartX = startX;
//this.wallStartY = startY;
//this.wallWidth = width;
//this.wallHeight = height;
}
public void draw(Graphics g){
g.setColor(Color.GRAY);
//g.fillRect( this.wallStartX, this.wallStartY, this.wallWidth, this.wallHeight);
//Trying with the rectangle methods
g.fillRect( this.x, this.y, this.width, this.height);
}
I used the rectangle.intersects method to check for collision prediction. It works with the left wall, but it seems to collide with the right wall all the time, even when the player square is not touching. I've used the same code for the left and right wall, and I made sure that the arguments for the wall are the same for the rectangle in the class above.
Based on Worms game from Killer Code Programming in Java
RacerPanel:
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.text.DecimalFormat;
import java.util.Random;
import java.awt.event.KeyEvent; //Should be for Key Usage
import java.awt.event.KeyListener;
public class RacingPanel extends GamePanel implements KeyListener {
///KeyListener from https://www.youtube.com/watch?v=5UaEUrbpDPE
//Q: Serial ID? http://stackoverflow.com/questions/285793/what-is-a- serialversionuid-and-why-should-i-use-it
private static final long serialVersionUID = 1825086766957972644L;
private DecimalFormat df = new DecimalFormat("0.##"); // 2 dp
private Main_Racer mrTop;
private Racer bob;
private Walls josephine;
private Walls jeremy;
private static Random randomGenerator = new Random();
boolean wallBuilder;
int leftCreateorDestroy = 100;
int rightCreateorDestroy = 100;
//private ArrayList()[] Walls kinderland;
//Q
private Font font;
private FontMetrics metrics;
//Q: definitely find out what this method is doing
public RacingPanel( Main_Racer rc, long period) {
super(period);
mrTop = rc;
this.addKeyListener(this);
}
//In case we want to integrate mice
//Q: Why void? Why protected?
protected void mousePress( int x, int y){
}
//Q: in the worm game, it calls
//fred.move(), presumably to move the worm
//wcTop.setTimeSpent(timeSpentInGame);
//Probably a counter
protected void simpleUpdate(){
}
//Q: Assuming that this should be things the
//game starts off with, like the block,
//the course. Also, font
protected void simpleInitialize(){
//T: Maybe I need to initialize the racer
bob = new Racer(PWIDTH, PHEIGHT);
josephine = new Walls(0, 35, 100, 1);
jeremy = new Walls(PWIDTH-100, 35, 100,1);
//That did it!
// so the arguments to the racer update change
//where the square was rendered
//TJ: I need to loop in such a way that will draw random variables
// set up message font
font = new Font("SansSerif", Font.BOLD, 24);
metrics = this.getFontMetrics(font);
}
protected void simpleRender(Graphics dbg) {
if(!gameOver){
//These two seem pretty straight forward
dbg.setColor(Color.blue);
dbg.setFont(font);
// report frame count & average FPS and UPS at top left
// dbg.drawString("Frame Count " + frameCount, 10, 25);
dbg.drawString("Average FPS/UPS: " + df.format(averageFPS) + ", "
+ df.format(averageUPS), 20, 25); // was (10,55)
dbg.setColor(Color.black);
// draw game elements: the obstacles and the worm
//This'll probably be the block and the
//course instead
//obs.draw(dbg);
//fred.draw(dbg);
bob.draw(dbg);
//jeremy.draw(dbg);
//See if we can draw this thing over and over
for(int i = 35; i < PHEIGHT; i++){
wallBuilder = randomGenerator.nextBoolean();
if(wallBuilder == false){
if ( leftCreateorDestroy < PWIDTH/2 - 50)
leftCreateorDestroy++;
}
else{
if(leftCreateorDestroy > 0){
leftCreateorDestroy--;
}
}
josephine = new Walls(0, i, leftCreateorDestroy, 1);
josephine.draw(dbg);
wallBuilder = randomGenerator.nextBoolean();
//This wall keeps colliding with the other wall
//Also, it isn't set to the other side of the screen due to conventions
if(wallBuilder == false){
//if ( (PWIDTH - rightCreateorDestroy) > (PWIDTH/2-150))
if ( rightCreateorDestroy > 0)
rightCreateorDestroy--;
}
else{
if((PWIDTH/2 + 50) < PWIDTH - rightCreateorDestroy){
rightCreateorDestroy++;
}
}
//PWIDTH - rightCreateorDestroy is the length of the
jeremy = new Walls((PWIDTH - rightCreateorDestroy), i, rightCreateorDestroy, 1);
//jeremy = new Walls((PWIDTH - rightCreateorDestroy), i, 100, 1);
//Let's see if it will draw negative; will not;
jeremy.draw(dbg);
//Now to put up collision detection
if(bob.intersects(josephine) ){
System.out.println("Ouch!");
gameOver = true;
}
//System.out.println(bob.);
else if( bob.intersects(jeremy) ){
Rectangle Sektor = bob.intersection(jeremy);
System.out.println(Sektor);
System.out.println(jeremy);
}
//System.out.println("Yow!");
//Problems seem to be with the right side; let's take out the right and check.
//For some reason, intersecting all of the time.
}
}
//1msecond is too long. Starts clipping;
/* try{
Thread.sleep(1000);
}catch ( InterruptedException exc ){
System.out.println("No work");
} */
}
//So this does draw over and over again
//FPS/UPS Still good
//Pretty straight forward as well. Just need
//to see when it is called
protected void gameOverMessage(Graphics g)
// center the game-over message in the panel
{
//Keeps updating after death...
String msg = "Game Over. Your Score: " + timeSpentInGame;
int x = (PWIDTH - metrics.stringWidth(msg)) / 2;
int y = (PHEIGHT - metrics.getHeight()) / 2;
g.setColor(Color.red);
g.setFont(font);
g.drawString(msg, x, y);
} // end of gameOverMessage()
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_RIGHT){
//System.out.println("Right");
//Should set x position to current position plus 1
bob.setRacerPositionX(bob.getRacerPositionX() + 1);
}
if (e.getKeyCode() == KeyEvent.VK_LEFT){
// System.out.println("Left");
bob.setRacerPositionX(bob.getRacerPositionX() - 1);
}
if (e.getKeyCode() == KeyEvent.VK_UP){
//System.out.println("Up");
bob.setRacerPositionY(bob.getRacerPositionY() - 1);
}
if (e.getKeyCode() == KeyEvent.VK_DOWN){
//System.out.println("Down");
bob.setRacerPositionY(bob.getRacerPositionY() + 1);
}
}
Game Panel Class:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.text.DecimalFormat;
import javax.swing.JPanel;
public abstract class GamePanel extends JPanel implements Runnable {
private static final long serialVersionUID = 1863596360846514344L;
private static long MAX_STATS_INTERVAL = 1000000000L;
// private static long MAX_STATS_INTERVAL = 1000L;
// record stats every 1 second (roughly)
private static int NUM_FPS = 10;
// number of FPS values stored to get an average
protected static final int PWIDTH = 500; // size of panel
protected static final int PHEIGHT = 400;
protected static final int NO_DELAYS_PER_YIELD = 16;
/*
* Number of frames with a delay of 0 ms before the animation thread yields
* to other running threads.
*/
protected static int MAX_FRAME_SKIPS = 5; // was 2;
// no. of frames that can be skipped in any one animation loop
// i.e the games state is updated but not rendered
private Thread animator; // the thread that performs the animation
protected boolean running = false; // used to stop the animation thread
protected boolean isPaused = false;
protected long period; // period between drawing in _nanosecs_
// used for gathering statistics
private long statsInterval = 0L; // in ns
private long prevStatsTime;
private long totalElapsedTime = 0L;
private long gameStartTime;
protected int timeSpentInGame = 0; // in seconds
private long frameCount = 0;
private double fpsStore[];
private long statsCount = 0;
protected double averageFPS = 0.0;
private long framesSkipped = 0L;
private long totalFramesSkipped = 0L;
private double upsStore[];
protected double averageUPS = 0.0;
private DecimalFormat df = new DecimalFormat("0.##"); // 2 dp
// used at game termination
protected boolean gameOver = false;
// off screen rendering
private Graphics dbg;
private Image dbImage = null;
public GamePanel(long period) {
this.period = period;
setBackground(Color.white);
setPreferredSize(new Dimension(PWIDTH, PHEIGHT));
setFocusable(true);
requestFocus(); // the JPanel now has focus, so receives key events
readyForTermination();
simpleInitialize();
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
mousePress(e.getX(), e.getY());
}
});
// initialise timing elements
fpsStore = new double[NUM_FPS];
upsStore = new double[NUM_FPS];
for (int i = 0; i < NUM_FPS; i++) {
fpsStore[i] = 0.0;
upsStore[i] = 0.0;
}
} // end of GamePanel()
/**
* Sets up a listener to kill the game loop if the user attempts to quit
*/
private void readyForTermination() {
addKeyListener(new KeyAdapter() {
// listen for esc, q, end, ctrl-c on the canvas to
// allow a convenient exit from the full screen configuration
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if ((keyCode == KeyEvent.VK_ESCAPE)
|| (keyCode == KeyEvent.VK_Q)
|| (keyCode == KeyEvent.VK_END)
|| ((keyCode == KeyEvent.VK_C) && e.isControlDown())) {
running = false;
}
}
});
} // end of readyForTermination()
/**
* Triggers the animation loop to start
*/
public void addNotify()
// wait for the JPanel to be added to the JFrame before starting
{
super.addNotify(); // creates the peer
startGame(); // start the thread
}
/**
* Starts the animation loop
*/
private void startGame()
// initialise and start the thread
{
if (animator == null || !running) {
animator = new Thread(this);
animator.start();
}
} // end of startGame()
// ------------- game life cycle methods ------------
// called by the JFrame's window listener methods
public void resumeGame()
// called when the JFrame is activated / deiconified
{
isPaused = false;
}
public void pauseGame()
// called when the JFrame is deactivated / iconified
{
isPaused = true;
}
public void stopGame()
// called when the JFrame is closing
{
running = false;
}
// ----------------------------------------------
public void run()
/* The frames of the animation are drawn inside the while loop. */
{
long beforeTime, afterTime, timeDiff, sleepTime;
long overSleepTime = 0L;
int noDelays = 0;
long excess = 0L;
gameStartTime = System.nanoTime();
prevStatsTime = gameStartTime;
beforeTime = gameStartTime;
running = true;
while (running) {
gameUpdate();
//T Entering Game Render
System.out.println("Entering Game Render");
gameRender();
paintScreen();
afterTime = System.nanoTime();
timeDiff = afterTime - beforeTime;
sleepTime = (period - timeDiff) - overSleepTime;
if (sleepTime > 0) { // some time left in this cycle
try {
Thread.sleep(sleepTime / 1000000L); // nano -> ms
} catch (InterruptedException ex) {
}
overSleepTime = (System.nanoTime() - afterTime) - sleepTime;
} else { // sleepTime <= 0; the frame took longer than the period
excess -= sleepTime; // store excess time value
overSleepTime = 0L;
if (++noDelays >= NO_DELAYS_PER_YIELD) {
Thread.yield(); // give another thread a chance to run
noDelays = 0;
}
}
beforeTime = System.nanoTime();
/*
* If frame animation is taking too long, update the game state
* without rendering it, to get the updates/sec nearer to the
* required FPS.
*/
int skips = 0;
while ((excess > period) && (skips < MAX_FRAME_SKIPS)) {
excess -= period;
gameUpdate(); // update state but don't render
skips++;
}
framesSkipped += skips;
storeStats();
}
printStats();
System.exit(0); // so window disappears
} // end of run()
/**
* Renders to the backbuffer
*/
private void gameRender() {
if (dbImage == null) {
dbImage = createImage(PWIDTH, PHEIGHT);
if (dbImage == null) {
System.out.println("dbImage is null");
return;
} else
dbg = dbImage.getGraphics();
//T
System.out.println("dbg set");
}
// clear the background
dbg.setColor(Color.white);
dbg.fillRect(0, 0, PWIDTH, PHEIGHT);
//T
simpleRender(dbg);
if (gameOver)
gameOverMessage(dbg);
} // end of gameRender()
/**
* Paints the back buffer
*/
private void paintScreen()
// use active rendering to put the buffered image on-screen
{
Graphics g;
try {
g = this.getGraphics();
if ((g != null) && (dbImage != null))
g.drawImage(dbImage, 0, 0, null);
g.dispose();
} catch (Exception e) {
System.out.println("Graphics context error: " + e);
}
} // end of paintScreen()
/**
* Should be update the game state
*/
private void gameUpdate() {
if (!isPaused && !gameOver)
simpleUpdate();
} // end of gameUpdate()
private void storeStats() {
frameCount++;
statsInterval += period;
if (statsInterval >= MAX_STATS_INTERVAL) { // record stats every
// MAX_STATS_INTERVAL
long timeNow = System.nanoTime();
timeSpentInGame = (int) ((timeNow - gameStartTime) / 1000000000L);
long realElapsedTime = timeNow - prevStatsTime; // time since last
// stats collection
totalElapsedTime += realElapsedTime;
totalFramesSkipped += framesSkipped;
double actualFPS = 0; // calculate the latest FPS and UPS
double actualUPS = 0;
if (totalElapsedTime > 0) {
actualFPS = (((double) frameCount / totalElapsedTime) * 1000000000L);
actualUPS = (((double) (frameCount + totalFramesSkipped) / totalElapsedTime) * 1000000000L);
}
// store the latest FPS and UPS
fpsStore[(int) statsCount % NUM_FPS] = actualFPS;
upsStore[(int) statsCount % NUM_FPS] = actualUPS;
statsCount = statsCount + 1;
double totalFPS = 0.0; // total the stored FPSs and UPSs
double totalUPS = 0.0;
for (int i = 0; i < NUM_FPS; i++) {
totalFPS += fpsStore[i];
totalUPS += upsStore[i];
}
if (statsCount < NUM_FPS) { // obtain the average FPS and UPS
averageFPS = totalFPS / statsCount;
averageUPS = totalUPS / statsCount;
} else {
averageFPS = totalFPS / NUM_FPS;
averageUPS = totalUPS / NUM_FPS;
}
framesSkipped = 0;
prevStatsTime = timeNow;
statsInterval = 0L; // reset
}
} // end of storeStats()
private void printStats() {
System.out.println("Frame Count/Loss: " + frameCount + " / "
+ totalFramesSkipped);
System.out.println("Average FPS: " + df.format(averageFPS));
System.out.println("Average UPS: " + df.format(averageUPS));
System.out.println("Time Spent: " + timeSpentInGame + " secs");
} // end of printStats()
/**
* Should implement game specific rendering
*
* @param g
*/
protected abstract void simpleRender(Graphics g);
/**
* Should display a game specific game over message
*
* @param g
*/
protected abstract void gameOverMessage(Graphics g);
protected abstract void simpleUpdate();
/**
* This just gets called when a click occurs, no default behavior
*/
protected abstract void mousePress(int x, int y);
/**
* Should be overridden to initialize the game specific components
*/
protected abstract void simpleInitialize();
} // end of GamePanel class
Racer Class:
import java.awt.*;
import java.awt.geom.*;
public class Racer extends Rectangle {
//Only method that will likely be common between
//the two areas. This presumably draws to screen
//Size of Car
private static final int carSize = 20;
//Changing racer position from array to x and y coordinates
private int pWidth, pHeight; // panel dimensions
private long startTime; // in ms
public Racer (int pW, int pH){
this.width = pW;
this.height = pH;
//Meant to start racer in the center of the screen
//But it does not
this.x = pW/2;
this.y = pW/2;
}
public void draw(Graphics g){
g.setColor(Color.blue);
g.fillRect( x, y, carSize, carSize);
}
public int getRacerPositionX() {
return x;
}
public void setRacerPositionX(int x) {
this.x = x;
}
public int getRacerPositionY() {
return y;
}
public void setRacerPositionY(int y) {
this.y = y;
}
}
Main Racer:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Main_Racer extends JFrame implements WindowListener
{
private static int DEFAULT_FPS = 80;
private RacingPanel rp;
//Let's leave now and make the panel
//Q: Just a value; why JTextField instead of int?
private JTextField jtfBox;
//Q: Just a value; why JTextField instead of int?
private JTextField jtfTime;
//Main_Racer Constructor
public Main_Racer(long period)
{
//Q: Gotta find this constructor
super("The Main Racder");
//Q:What does the argument do?
makeGUI(period);
addWindowListener( this);
pack();
setResizable(false);
setVisible(true);
}
//Q: Warrants an in depth look
private void makeGUI(long period)
{
Container c = getContentPane(); // default BorderLayout used
rp = new RacingPanel(this, period);
c.add(rp, "Center");
JPanel ctrls = new JPanel(); // a row of textfields
ctrls.setLayout( new BoxLayout(ctrls, BoxLayout.X_AXIS));
//Keeps track of time. Might still be useful
jtfTime = new JTextField("Time Spent: 0 secs");
jtfTime.setEditable(false);
ctrls.add(jtfTime);
c.add(ctrls, "South");
} // end of makeGUI()
//Pretty straing forward
public void setTimeSpent(long t)
{ jtfTime.setText("Time Spent: " + t + " secs"); }
// QQQ -----------------Going to copy the window listening methods, as probably n
//need em all
public void windowActivated(WindowEvent e)
{ rp.resumeGame(); }
public void windowDeactivated(WindowEvent e)
{ rp.pauseGame(); }
public void windowDeiconified(WindowEvent e)
{ rp.resumeGame(); }
public void windowIconified(WindowEvent e)
{ rp.pauseGame(); }
public void windowClosing(WindowEvent e)
{ rp.stopGame(); }
public void windowClosed(WindowEvent e) {}
public void windowOpened(WindowEvent e) {}
//=================================================================================
public static void main(String args[])
{
int fps = DEFAULT_FPS;
if (args.length != 0)
fps = Integer.parseInt(args[0]);
long period = (long) 1000.0/fps;
System.out.println("fps: " + fps + "; period: " + period + " ms");
new Main_Racer(period*1000000L); // ms --> nanosecs
}
} // end of WormChase class
Any reason why collision detection only works on one side?