This game demonstrates bullets, states, multiple enemies (with a pathing pattern), score, and various input methods (keyboard/mouse)
The showcase player uses a modified version of Processing.js in combination with jsweet to let students program their apps in Java code while still allowing for browser support.
Content created by students is scaled to fit the showcase frame while maintaining aspect ratio and cursor position. This is why some projects may appear blurry in fullscreen, or why some small details may not be visible on a small screen
<iframe width='500px' height='400px' src='https://nest.ktbyte.com/nest#534' allowfullscreen></iframe>
int GAME_START = 0, GAME_RUN = 1, LEVEL_UP = 2, GAME_OVER = 3; int state = GAME_START, level, score, levelUpTimer; ArrayList<Enemy> enemies = new ArrayList<Enemy>(); ArrayList<EnemyBullet> ebullets = new ArrayList<EnemyBullet>(); ArrayList<ShipBullet> mybullets = new ArrayList<ShipBullet>(); ArrayList<Shield> shields = new ArrayList<Shield>(); Ship ship = new Ship(); float minX = 1e9, maxX = -1e9; //min positions of the enemies class ShipBullet extends Bullet { ShipBullet() { x = ship.x; y = ship.y; d = 2; } void move() { y -= 5; } void display() { ellipse(x, y, d, d); } } class Ship { float maxHealth = 100, health = 0; float x, y; Ship() { y = height - 40; health= maxHealth; } boolean collide(Bullet b) { if(dist(x,y,b.x,b.y) < (20 + b.d)/2) { health -= 10; return true; } return false; } void fire() { if (frameCount % 10 == 0) mybullets.add(new ShipBullet()); } void move() { x = mouseX; y = height - 40; } void display() { float w = 40; pushMatrix(); translate(x, y); triangle(0, -10, 20, 10, -20, 10); popMatrix(); pushMatrix(); translate(x + - 20, y + 10); rectMode(CORNER); float percentHealth =(health/maxHealth); fill(255- percentHealth*255,percentHealth*255,0); rect(0,0,w*percentHealth,2); popMatrix(); } } class Bullet { float x, y, d; boolean offScreen() { return x < 0 || y < 0 || x > width || y > width; } } class ShieldSection { float x, y, d = 3; ShieldSection(float sx, float sy, int r, int c, int rows, int cols) { float h = 3, w = 3; y = sy + (r - (rows/2))*h; x = sx + (c - (cols/2))*w; } boolean collide(Bullet b) { return dist(b.x, b.y, x, y) < (d + b.d)/2; } void display() { ellipse(x, y, d, d); } } class Shield { float x, y; float sideMargin = 100; ArrayList<ShieldSection> sections = new ArrayList<ShieldSection>(); Shield(int index, int total) { float between = (width - sideMargin * 2)/(total-1); x = index * between + sideMargin; y = height - 70; int rows = 8, cols = 12; for (int r = 0; r < rows; r++) { for (int c = 0; c < cols; c++) { sections.add(new ShieldSection(x, y, r, c, rows, cols)); } } } boolean collide(Bullet b) { for (int i = 0; i < sections.size (); i++) { ShieldSection section = sections.get(i); if (section.collide(b)) { sections.remove(i); return true; } } return false; } void display() { for (int i = 0; i < sections.size (); i++) { ShieldSection section = sections.get(i); section.display(); } } } class EnemyBullet extends Bullet { EnemyBullet(Enemy e) { x = e.x; y = e.y; d = 3; } void move() { y += 5; } void display() { noStroke(); ellipse(x, y, d, d); } } class Enemy { float x, y, d = 20; int frameStart; Enemy(int r, int c) { y = r * 35 + 60; x = c * 60 + 60; minX = min(minX, x); maxX = max(maxX, x); frameStart = frameCount; } boolean collide(Bullet b) { return dist(x, y, b.x, b.y) < (d+b.d)/2; } void fire() { if (random(0, 1) < 0.001 + 0.002 * level) { ebullets.add(new EnemyBullet(this)); } } void move() { float speed = level * 0.5; float leftMargin = minX; float rightMarginBegin = width - maxX; float range = rightMarginBegin - leftMargin; int framesLeftRight = round(range / speed); int delayFrames = 60 / level; int f = frameCount - frameStart; int firstJump = framesLeftRight+delayFrames; int secondMove = firstJump + delayFrames; int secondJump = secondMove + framesLeftRight + delayFrames; int sum = secondJump + delayFrames; int[][] ranges = { { 0, framesLeftRight } , { firstJump, firstJump } , { secondMove, secondMove+framesLeftRight } , { secondJump, secondJump } }; if (f % sum >= ranges[0][0] && f % sum <= ranges[0][1]) x+=speed; if (f % sum >= ranges[1][0] && f % sum <= ranges[1][1]) y+=30; if (f % sum >= ranges[2][0] && f % sum <= ranges[2][1]) x-=speed; if (f % sum >= ranges[3][0] && f % sum <= ranges[3][1]) y+=30; } void display() { if (frameCount % 40 < 20) { ellipse(x, y, d, d); } else { rectMode(CENTER); rect(x, y, d, d); } } } void setup() { size(600, 600); } void draw() { if (state == GAME_START) { background(0); fill(255); textAlign(CENTER); text("Click to begin and fire", width/2, height/2); if (mousePressed) { startGame(1); } } else if (state == GAME_RUN) { runGame(); if(enemies.size() == 0) { state = LEVEL_UP; levelUpTimer = frameCount + 30; } } else if (state == LEVEL_UP) { background(0); fill(255); textAlign(CENTER); text("New level: "+(level+1), width/2, height/2); if(frameCount - levelUpTimer > 0) { startGame(level+1); } } else if (state == GAME_OVER) { background(0); fill(255); textAlign(CENTER); text("Game over! Your score: "+score, width/2, height/2); text("Press a key to restart", width/2, height/4*3); if (keyPressed) { startGame(1); } } } void startGame(int lev) { state = GAME_RUN; level = lev; enemies.clear(); shields.clear(); mybullets.clear(); ebullets.clear(); ship = new Ship(); for (int r = 0; r < 6; r++) { for (int c = 0; c < 6; c++) { enemies.add(new Enemy(r, c)); } } for (int i = 0; i < 4; i++) { shields.add(new Shield(i, 4)); } if(level == 1) score = 0; } void runGame() { background(0); stroke(50); line(0,height - 100,width,height-100); for (int i = 0; i < enemies.size (); i++) { Enemy enemy = enemies.get(i); enemy.display(); enemy.move(); enemy.fire(); } for (int i = 0; i < shields.size (); i++) { Shield shield = shields.get(i); shield.display(); } for (int i = 0; i < ebullets.size (); i++) { EnemyBullet ebullet = ebullets.get(i); ebullet.display(); ebullet.move(); if (ebullet.offScreen()) { ebullets.remove(i--); continue; } if(ship.collide(ebullet)) { ebullets.remove(i--); continue; } for (int j = 0; j < shields.size (); j++) { Shield shield = shields.get(j); if (shield.collide(ebullet)) { ebullets.remove(i--); break; } } } if (mousePressed) { ship.fire(); } for (int i = 0; i < mybullets.size (); i++) { ShipBullet b = mybullets.get(i); b.move(); b.display(); if (b.offScreen()) { mybullets.remove(i--); continue; } for (int j = 0; j < shields.size (); j++) { Shield shield = shields.get(j); if (shield.collide(b)) { mybullets.remove(i--); break; } } } for (int i = 0; i < enemies.size (); i++) { Enemy enemy = enemies.get(i); if(enemy.y + enemy.d >= height-100) { state = GAME_OVER; } for (int j = 0; j < mybullets.size (); j++) { ShipBullet b = mybullets.get(j); if (enemy.collide(b)) { mybullets.remove(j); enemies.remove(i--); score += 10; break; } } } ship.move(); ship.display(); fill(255); text("Level: "+level, width-50, 20); text("Score: "+score, 40, 20); } void keyPressed() { if(key == 'z') enemies.clear(); //z to cheat }