This is an exercise out of Beginning Java SE6 Game Programming, Third Edition by Jonathan S Harbour.
So I'm given an exerice in chapter 6 to take the code given to me (which puts one asteroid sprite on the screen, rotates it, and moves it with a velocity represented by a Point object) and make multiple asteroids moving around on screen. This part was easy. The second part was that I was to make the asteroids collide and bounce off of each other.
My first run at this produced many problems. The first was that the asteroids tended to stick to each other instead of bounce. I found that this was because of rapidly changing velocities due to two asteroids colliding every single frame and the velocity changing so quickly that they didn't get out of each others way. So instead of checking for collision, I tried to predict a collision. For the purposes of the exercise, this works just fine. It also allowed me to bounce the asteroids off of each other. But it's not completely accurate. Here's my code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
| import java.awt.*; import java.awt.image.*; import javax.swing.*; import java.awt.event.*; import java.awt.geom.*; import java.util.*; import java.net.*; import java.lang.Math;
public class MultiSprite extends JFrame implements Runnable, KeyListener {
int screenWidth = 640; int screenHeight = 480; BufferedImage backbuffer; Graphics2D g2d; int MAX = 5; Sprite[] asteroid = new Sprite[MAX]; ImageEntity background; Thread gameloop; Random rand = new Random(); boolean enterKeyPressed = false; public static void main(String[] args) { new MultiSprite(); } public MultiSprite() { super("Sprite Test"); setSize(640, 480); setVisible(true); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); backbuffer = new BufferedImage(screenWidth, screenHeight, BufferedImage.TYPE_INT_RGB); g2d = backbuffer.createGraphics(); background = new ImageEntity(this); background.load("bluespace.png"); Point point; for (int n = 0; n<MAX; n++){ asteroid[n] = new Sprite(this, g2d); asteroid[n].load("asteroid2.png"); point = new Point(rand.nextInt(600)+20, rand.nextInt(440)+20); asteroid[n].setPosition(point); asteroid[n].setRotationRate(-5 + rand.nextInt(11)); asteroid[n].setAlive(true); asteroid[n].setVelocity(new Point(-5 + rand.nextInt(11), -5 + rand.nextInt(11))); } enterKeyPressed = false; addKeyListener(this);
gameloop = new Thread(this); gameloop.start(); } public void run() { Thread t = Thread.currentThread(); while (t == gameloop) { try { Thread.sleep(30); } catch (InterruptedException e) { e.printStackTrace(); } if(!enterKeyPressed){ g2d.drawImage(background.getImage(), 0, 0, screenWidth-1, screenHeight-1, this);
for(int n = 0; n < MAX; n++){ if(asteroid[n].alive()) { if (asteroid[n].position().getX() < -50){ asteroid[n].setPosition(new Point(getSize().width + 50, (int)asteroid[n].position().getY())); } else if (asteroid[n].position().getX() > getSize().width + 50){ asteroid[n].setPosition(new Point(-50, (int)asteroid[n].position().getY())); } if (asteroid[n].position().getY() < -40){ asteroid[n].setPosition(new Point((int)asteroid[n].position().getX(), getSize().height + 40)); } else if (asteroid[n].position().getY() > getSize().height + 40){ asteroid[n].setPosition(new Point((int)asteroid[n].position().getX(), -40)); } CheckCollisions(n); asteroid[n].updatePosition(); asteroid[n].updateRotation(); asteroid[n].transform(); asteroid[n].draw(); } } repaint(); } } } public void CheckCollisions(int n){ for(int x = 0; x < MAX; x++){ if(x!=n && PredictCollision(asteroid[n], asteroid[x])){ Rectangle intersectRect = asteroid[n].getBounds().intersection(asteroid[x].getBounds()); if(intersectRect.getHeight() > intersectRect.getWidth()){ asteroid[n].setVelocity(new Point(-1 * (int)asteroid[n].velocity().getX(), (int)asteroid[n].velocity().getY())); asteroid[x].setVelocity(new Point(-1 * (int)asteroid[x].velocity().getX(), (int)asteroid[x].velocity().getY())); } if(intersectRect.getHeight() == intersectRect.getWidth()){ asteroid[n].setVelocity(new Point(-1 * (int)asteroid[n].velocity().getX(), -1 * (int)asteroid[n].velocity().getY())); asteroid[x].setVelocity(new Point(-1 * (int)asteroid[x].velocity().getX(), -1 * (int)asteroid[x].velocity().getY())); }
if(intersectRect.getHeight() < intersectRect.getWidth()){ asteroid[n].setVelocity(new Point((int)asteroid[n].velocity().getX(), -1 * (int)asteroid[n].velocity().getY())); asteroid[x].setVelocity(new Point((int)asteroid[x].velocity().getX(), -1 * (int)asteroid[x].velocity().getY())); } } } } public boolean PredictCollision(Sprite ast1, Sprite ast2){ if(Math.abs((ast1.position().getX() + ast1.velocity().getX()) - (ast2.position().getX() + ast2.velocity().getX())) < 60 && Math.abs((ast1.position().getY() + ast1.velocity().getY()) - (ast2.position().getY() + ast2.velocity().getY())) < 60){return true;} return false; }
public void paint(Graphics g) { g.drawImage(backbuffer, 0, 0, this); }
public void keyReleased(KeyEvent k) {} public void keyTyped(KeyEvent k) {} public void keyPressed(KeyEvent k) { switch(k.getKeyCode()){ case KeyEvent.VK_ENTER: enterKeyPressed = !enterKeyPressed; break; } } } |
The two main methods to look at here are CheckCollisions and PredictCollision. Notice how I'm just reversing velocities. This seems to work ok except in instances where two asteroids are moving in the same direction, but the one in front is moving slower than the one behind it. When they collide, they both change course to move in the opposite direction whereas the one in front should get a boost and the one in back should slow down.
Does anyone have a better means of implementing this? My best guess is that vector math is probably the route to go here.
Edit: I should note that the Sprite class used here contains the velocity valuesas well as a move angle and a face angle. So I have both a speed and direction.