import java.awt.*; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.image.BufferedImage; import java.awt.image.BufferStrategy; import java.awt.image.MemoryImageSource; import java.util.Random; import javax.swing.JFrame; /** An adaption of Asteroids for the Java 4k contest * The source code is based on Tube Blazer. * * by Matthias Kalisch */ class A extends JFrame implements KeyListener { private int a = 5; public static void main(String[] args) { new A(); } A() { final int[] clip = new int[240]; final Image[] image = new Image[120]; final double[] asteroid = new double[163840]; // asteroid[0] is the ship, asteroid[x] = x, asteroid[x+1] = y, asteroid[x+2] = dx, asteroid[x+3] = dy, asteroid[x+4] = direction, at asteroid[x+5] starts the next asteroid final int[] asteroid_type = new int[131072]; final int[][] ship_border = new int[120][10]; int last_asteroid = 5; int last_shot = 0; double turn_speed = 0, turn_state = 0; int level = 25, score = 0, at = 0; final int[] highscore = new int[15]; final int w = getToolkit().getScreenSize().width, h = getToolkit().getScreenSize().height; setBackground(Color.black); setDefaultCloseOperation(HIDE_ON_CLOSE); setUndecorated(true); addKeyListener(this); setVisible(true); if(getGraphicsConfiguration().getDevice().isFullScreenSupported()) getGraphicsConfiguration().getDevice().setFullScreenWindow(this); else setBounds(0, 0, w, h); createBufferStrategy(3); Image background = createImage(w,h); Graphics g; // this block encapsules all variables which are only needed for game initialization { // hide the mouse cursor by assigning an invisible image to it int[] pixels = new int[1]; setCursor(getToolkit().createCustomCursor(createImage(new MemoryImageSource(1, 1, pixels, 0, 1)), new Point(), null)); // create the background image g = background.getGraphics(); for(int i = w*h/500, x, y; --i >= 0; ) { g.setColor(new Color(0x010101*(int)(Math.random()*150))); g.drawLine(x=(int)(Math.random()*w), y=(int)(Math.random()*h), x, y); } // generate and store the asteroid images - there's 3 different asteroid sizes and 16 different asteroids of each size for(int i = 48; --i >= 0; ) { final int size = (i/16+1)*16; clip[i] = w-size*2; clip[i+120] = h-size*2; pixels = new int[size*size*4]; for(int y = size*2; --y >= 0; ) for(int x = (int)(1.1+size-Math.sqrt(size*size-(size-y)*(size-y))); x <= (-1.1+size+Math.sqrt(size*size-(size-y)*(size-y))); x++) pixels[y*size*2+x] = 0xff000000+0x030201*(int)((85-((size*.9-x)*(size*.9-x)+(size*.9-y)*(size*.9-y))/((11<<(i/16))-7))*(0.7+Math.random()*0.3)); image[i] = createImage(new MemoryImageSource(size*2, size*2, pixels, 0, size*2)); } // generate and store the ship images - there's 72 different angles the ship can turn to final BufferedImage ship = getGraphicsConfiguration().createCompatibleImage(61, 61, Transparency.BITMASK); for(int y = 50; --y >= 0; ) for(int x = 30+y/2; --x > 30-y/2; ) if(y > 22 && y < 33 && x <= (int)(26+y/2.0) && x > 33-y/2.0) ship.setRGB(x, y, 0xff000000+0x000001*(int)(255.5*0.7-Math.pow((Math.abs(30.0-x)+3)/(y+3), 1.5)*(0.85+Math.random()*0.15)*450*0.7)); else ship.setRGB(x, y, 0xff000000+0x010101*(int)(255.5-Math.pow((Math.abs(30.0-x)+3)/(y+3), 1.5)*(0.85+Math.random()*0.15)*450)); final double[] border = {0, -1.176, -2.2455, 2.2455, 1.176, 29, 13, 31, 31, 13}; for(int i = 72; --i >= 0; ) { clip[i+48] = w-61; clip[i+168] = h-61; for(int j = 5; --j >= 0; ) { ship_border[i+48][j] = (int)(border[j+5]*Math.sin(Math.PI*i/36+border[j]))+30; ship_border[i+48][j+5] = (int)(-border[j+5]*Math.cos(Math.PI*i/36+border[j]))+30; } image[i+48] = getGraphicsConfiguration().createCompatibleImage(61, 61, Transparency.BITMASK); g = image[i+48].getGraphics(); ((Graphics2D)g).rotate(Math.PI*10*i/360, 31, 31); g.drawImage(ship, 0, 0, this); } // show every asteroid image to get rid of the stupid flickering for(int i = 48; --i >= 0; ) getBufferStrategy().getDrawGraphics().drawImage(image[i], -100, -100, this); } // initialization finished. run the garbage collector System.gc(); // initialization is over. here starts the game loop ******************************************************************** // turn this thread into the game thread double framecount = 0.0; long counttime = System.currentTimeMillis()+250; double speedfactor = 1.0; while(isVisible()) { if((a&8) == 8) { // start a new game level = 0; score = 0; last_asteroid = 5; } if(last_asteroid == 5) { // go to the next level last_shot = 131072; level++; if(a != 5) a &= 0xfffffff2; // reset the ship at = asteroid_type[0] = 48; asteroid[0] = (w-61)/2; asteroid[1] = (h-61)/2; asteroid[2] = 0; asteroid[3] = 0; turn_speed = 0; turn_state = 0; // add the asteroids last_asteroid = 20+5*level; for(int i = last_asteroid; (i-=5) >= 5; ) { asteroid_type[i] = (int)(Math.random()*16)+32; do{ asteroid[i] = (int)(Math.random()*w); asteroid[i+1] = (int)(Math.random()*h); } while(asteroid[i] >= w/8 && asteroid[i] < w*7/8 && asteroid[i+1] >= h/8 && asteroid[i+1] < h*7/8); asteroid[i+4] = (float)(Math.random()*Math.PI*2); final double speed = Math.random()*2 + .5; asteroid[i+2] = (float)(Math.sin(asteroid[i+4])*speed); asteroid[i+3] = (float)(Math.cos(asteroid[i+4])*speed); } } // draw the next frame Thread.currentThread().setPriority(Thread.MAX_PRIORITY); g = getBufferStrategy().getDrawGraphics(); g.drawImage(background, 0, 0, this); // draw the asteroids and the spaceship for(int i = last_asteroid; (i-=5) >= (a&5); ) { g.drawImage(image[asteroid_type[i]], (int)asteroid[i], (int)asteroid[i+1], this); if(asteroid[i] >= clip[asteroid_type[i]]) { g.drawImage(image[asteroid_type[i]], (int)asteroid[i]-w, (int)asteroid[i+1], this); if(asteroid[i+1] >= clip[asteroid_type[i]+120]) { g.drawImage(image[asteroid_type[i]], (int)asteroid[i], (int)asteroid[i+1]-h, this); g.drawImage(image[asteroid_type[i]], (int)asteroid[i]-w, (int)asteroid[i+1]-h, this); } } else if(asteroid[i+1] >= clip[asteroid_type[i]+120]) g.drawImage(image[asteroid_type[i]], (int)asteroid[i], (int)asteroid[i+1]-h, this); } // draw the shots g.setColor(Color.yellow); for(int i = last_shot; (i-=5) >= 131072; ) g.drawRect((int)asteroid[i], (int)asteroid[i+1], 1, 1); // display game information g.setColor(Color.yellow); g.drawString("Level "+level, 10, 15); g.drawString("Score "+score, 10, 35); if((a&5) == 5) { g.drawString("Press a key to start", w/2-64, h/2); for(int i = 1; i < 11 && highscore[i] > 0; i++) { g.drawString("Today's Highscores", w/2-57, h/2+40); g.drawString(i+"", w/2-25, h/2+63+i*20); g.drawString(highscore[i]+"", w/2+10, h/2+63+i*20); } } getBufferStrategy().show(); Thread.currentThread().setPriority(Thread.NORM_PRIORITY); // adjust the animation speed to the frame rate framecount++; if(System.currentTimeMillis() >= counttime) { speedfactor = framecount/10; framecount = 0; counttime = System.currentTimeMillis()+250; } // move the shots for(int i = last_shot; (i-=5) >= 131072; ) if(--asteroid[i+4] == 0) { // shot dies last_shot -= 5; System.arraycopy(asteroid, last_shot, asteroid, i, 5); } else { asteroid[i] = (asteroid[i]+asteroid[i+2]*speedfactor+w)%w; asteroid[i+1] = (asteroid[i+1]+asteroid[i+3]*speedfactor+h)%h; } // turn the ship if(((a&16)<<1) == (a&32)) turn_speed = 0; else if((a&32) == 32 && turn_speed < 1) turn_speed += (0.2/speedfactor); else if((a&16) == 16 && turn_speed > -1) turn_speed -= (0.2/speedfactor); turn_state += turn_speed*speedfactor; while(turn_state < 0) turn_state += 72; while(turn_state >= 72) turn_state -= 72; at = asteroid_type[0] = (int)turn_state+48; // adjust the ship velocity if((a&64) == 64) { asteroid[2] += 0.1*Math.sin(turn_state*Math.PI/36)*speedfactor; asteroid[3] -= 0.1*Math.cos(turn_state*Math.PI/36)*speedfactor; } // move the asteroids and the ship for(int i = last_asteroid; (i-=5) >= 0; ) { asteroid[i] = (asteroid[i]+asteroid[i+2]*speedfactor+w)%w; asteroid[i+1] = (asteroid[i+1]+asteroid[i+3]*speedfactor+h)%h; } if((a&5) == 0) { // shoot if((a&130) == 2) { a+=128; asteroid[last_shot] = asteroid[0]+ship_border[at][0]; asteroid[last_shot+1] = asteroid[1]+ship_border[at][5]; asteroid[last_shot+2] = 3*Math.sin(turn_state*Math.PI/36); asteroid[last_shot+3] = -3*Math.cos(turn_state*Math.PI/36); asteroid[last_shot+4] = h/4; last_shot += 5; } // detect collisions between ths shots and the asteroids for(int i = last_shot; (i-=5) >= 131072; ) for(int j = last_asteroid; (j-=5) >= 5; ) { int size = (asteroid_type[j]&0xfffffff0)+16, x = (int)(asteroid[i] - size - asteroid[j]), y = (int)(asteroid[i+1] - size - asteroid[j+1]); if(Math.min(x*x, (x+w)*(x+w)) + Math.min(y*y, (y+h)*(y+h)) <= size*size) { // remove the asteroid last_shot -= 5; System.arraycopy(asteroid, last_shot, asteroid, i, 5); score += 4-size/16; if(size == 16) { // destroy the asteroid last_asteroid -= 5; System.arraycopy(asteroid, last_asteroid, asteroid, j, 5); asteroid_type[j] = asteroid_type[last_asteroid]; } else { // split the asteroid double speed = Math.random()*(5-size/16)+.1; System.arraycopy(asteroid, j, asteroid, last_asteroid, 5); asteroid_type[j] = (int)(Math.random()*16)+size-32; asteroid[j+4] += (Math.random()-0.5)*Math.PI; asteroid[j+2] = (float)(Math.sin(asteroid[j+4])*speed); asteroid[j+3] = (float)(Math.cos(asteroid[j+4])*speed); speed = Math.random()+Math.random()+Math.random()+0.1+((size==32)?Math.random():0); asteroid[last_asteroid+4] += (Math.random()-0.5)*Math.PI; asteroid[last_asteroid+2] = (float)(Math.sin(asteroid[last_asteroid+4])*speed); asteroid[last_asteroid+3] = (float)(Math.cos(asteroid[last_asteroid+4])*speed); asteroid_type[last_asteroid] = (int)(Math.random()*16)+size-32; asteroid_type[last_asteroid] = asteroid_type[j]; last_asteroid += 5; } break; } } // detect collisions between the ship and the asteroids for(int i = last_asteroid; (i-=5) >= 5; ) for(int j = 5; --j >= 0; ) { int size = (asteroid_type[i]/16+1)*16, x = (int)(asteroid[0]+ship_border[at][j] - asteroid[i]) - size, y = (int)(asteroid[1]+ship_border[at][j+5] - asteroid[i+1]) - size; if(Math.min(x*x, (x+w)*(x+w)) + Math.min(y*y, (y+h)*(y+h)) <= size*size) { a |= 5; for(int k = 11; --k >= 1 && highscore[k] < score; ) { highscore[k+1] = highscore[k]; highscore[k] = score; } for(last_shot = 131072; last_shot < 138072; last_shot+=5) { asteroid[last_shot] = asteroid[0]+ship_border[at][0]; asteroid[last_shot+1] = asteroid[1]+ship_border[at][5]; asteroid[last_shot+2] = (Math.random()-.5)/(Math.random()+0.2); asteroid[last_shot+3] = (Math.random()-.5)/(Math.random()+0.2); asteroid[last_shot+4] = (int)(Math.random()*h/8)+10; } } } } } System.exit(0); } // KeyListener implementation ******************************************************************************************* public void keyPressed(KeyEvent e) { final int k = e.getKeyCode(); if(k == KeyEvent.VK_SPACE && (a&15) == 0) a |= 2; else if(k == KeyEvent.VK_LEFT) a |= 16; else if(k == KeyEvent.VK_RIGHT) a |= 32; else if(k == KeyEvent.VK_UP) a |= 64; else if((a&5) == 5 && k != KeyEvent.VK_SPACE) a |= 8; } public void keyReleased(KeyEvent e) { final int k = e.getKeyCode(); if(k == KeyEvent.VK_SPACE && (a&128) == 128) a &= 0xffffff7d; if(k == KeyEvent.VK_LEFT) a &= 0xffffffef; if(k == KeyEvent.VK_RIGHT) a &= 0xffffffdf; if(k == KeyEvent.VK_UP) a &= 0xffffffbf; } public void keyTyped(KeyEvent e) {} }