import processing.opengl.*; import traer.physics.*; /******************************************************************************************************** This game was written for a competition held by the TELIC Galley in Los Angeles. The constraint was to write a game that would work with a simple 5 Joystick serial setup (up, down, left, right, no buttons). The goal of this game is to use the available tools to divert the stream of pebbles into the bucket. Keyboard controls are as follows (in the order up, right, down, left):

Player 1: 2, w, a, q
Player 2: 4, r, d, e
Player 3: 6, y, g, t
Player 4: 8, i, j, u
Player 5: 0, p, l, o

Additionally, if you get stuck, pressing 'z' will reset the game. *********************************************************************************************************/ Joysticks joysticks; // contains the current state of the 5 joysticks boolean DEBUG = false; // this shows some println's when set to true. ParticleSystem phys; ParticleSystem nograv; float chuteLoc, chuteWidth; // where the pebbles spawn from Player[] players; // the five players Vector types; Bucket buck; // a bucket to catch them Timer time; // game timer int pebAtATime, maxPebbles; int pebCaught, pebLost; Vector pebbles; PFont statFont, titleFont; boolean collide; boolean stats = false; static final int TITLE = 0; static final int PLAY = 1; static final int SUMMARY = 2; int gameState; // setup gets called once void setup() { size(800, 600, OPENGL); frameRate(30); noCursor(); colorMode(HSB, 360, 99, 99); ellipseMode(CENTER_RADIUS); collide = true; if (!EMULATE) { println(Serial.list()); port = new Serial(this, Serial.list()[serialPortIndex], serialBaudRate); port.buffer(1); } joysticks = new Joysticks(); // leave this line in because it's how you find out about the joysticks players = new Player[5]; types = new Vector(5); gameState = TITLE; pebAtATime = 3; maxPebbles = 250; pebLost = 0; pebbles = new Vector(maxPebbles, 100); statFont = createFont("Courier", 16); titleFont = createFont("Verdana", 24); } // draw gets called over and over void draw() { if ( gameState == TITLE && allUp() ) initialize(); if ( gameState == SUMMARY && allUp() ) reset(); if ( gameState == PLAY ) update(); render(); if ( stats ) { fill(0); textFont(statFont); textSize(16); text("Framerate: " + frameRate, 10, 15); text("Number of Pebbles: " + pebbles.size(), 10, 30); } } void initialize() { phys = new ParticleSystem(5, 1); nograv = new ParticleSystem(0, 1); chuteLoc = random(70, width-70); chuteWidth = 5; for (int i = 0; i < 5; i++) { players[i] = new Player(i, new Type(i)); } types.add(new Type(ATTRACT)); types.add(new Type(REPEL)); types.add(new Type(FUNNL)); types.add(new Type(FUNNR)); types.add(new Type(BOARD)); float bx = random(55, width - 55); while ( abs(chuteLoc - bx) < 100 ) bx = random(55, width - 55); buck = new Bucket(bx, random(50, 500), 30, 60); time = new Timer(); time.start(); gameState = PLAY; } void reset() { phys.clear(); nograv.clear(); pebbles.clear(); chuteLoc = random(70, width-70); pebLost = 0; Collections.shuffle(types); for (int i = 0; i < players.length; i++) { players[i].setType((Type)types.get(i)); } float bx = random(55, width - 55); while ( abs(chuteLoc - bx) < 100 ) bx = random(55, width - 55); buck = new Bucket(bx, random(height/2, 500), 30, 60); time.start(); gameState = PLAY; } void update() { phys.tick(); nograv.tick(); for (int i = 0; i < players.length; i++) players[i].move(joysticks.get(i)); // make new pebbles for (int i = 0; pebbles.size() < maxPebbles && i < pebAtATime; i++) { Particle p = phys.makeParticle( random(5, 10), random(chuteLoc-chuteWidth/2, chuteLoc+chuteWidth/2), -5, 0); pebbles.add( new Pebble(p, color(40, 61, random(30, 99))) ); for (int j = 0; j < players.length; j++) { Player tpl = players[j]; if ( tpl.type == ATTRACT ) phys.makeAttraction(p, tpl.attr.p, 1000, 10*tpl.attr.radius() ); else if ( tpl.type == REPEL ) phys.makeAttraction(p, tpl.attr.p, -3000, 10*tpl.attr.radius() ); } } // manage the pebbles pebCaught = 0; for (int i = 0; i < pebbles.size(); i++) { Pebble peb = (Pebble)pebbles.get(i); peb.update(); if ( peb.isDead() ) { pebbles.remove(i--); pebLost++; continue; } // collide with other pebbles if ( collide ) { for (int j = i+1; j < pebbles.size(); j++) { peb.collide( (Pebble)pebbles.get(j) ); } } for (int j = 0; j < players.length; j++) players[j].collide(peb); // collide with other stuff buck.collide(peb); if ( peb.caught ) pebCaught++; } if ( pebCaught == maxPebbles ) { time.stop(); gameState = SUMMARY; } } void render() { background(128); if ( gameState == TITLE ) { textFont(titleFont); textSize(24); text("DIVERSION", width/2 - textWidth("DIVERSION")/2, height/2 - 20); textSize(12); String pressUp = "Press Up On All Joysticks To Start"; text(pressUp, width/2 - textWidth(pressUp)/2, height/2 + 20); } if ( gameState == PLAY || gameState == SUMMARY ) { noStroke(); for (int i = 0; i < pebbles.size(); i++) ((Pebble)pebbles.get(i)).render(); for (int i = 0; i < players.length; i++) players[i].render(); buck.render(); fill(0); time.display(650, 20, 16, false); text("Pebbles Caught: " + pebCaught, 650, 40); } if ( gameState == SUMMARY ) { fill(0, 128); rect(0, 0, width, height); textFont(titleFont); textSize(24); fill(255); text("SUCCESS!", width/2 - textWidth("SUCCESS!")/2, height/2 - 60); time.display(width/2, height/2, 20, true); String lost = "Pebbles Lost: " + pebLost; text(lost, width/2 - textWidth(lost)/2, height/2 + 60); String pressUp = "Press Up On All Joysticks To Start A New Game"; text(pressUp, width/2 - textWidth(pressUp)/2, height/2 + 120); } } boolean allUp() { return ( joysticks.get(0) == 1 && joysticks.get(1) == 1 && joysticks.get(2) == 1 && joysticks.get(3) == 1 && joysticks.get(4) == 1 ); } class Joysticks { int NEUTRAL,UP,RIGHT,DOWN,LEFT; int[] joystick; Joysticks() { NEUTRAL=0; UP=1; RIGHT=2; DOWN=3; LEFT=4; initialize(); } /* these are the functions you probably want to call from the outside */ int size() { return joystick.length; } int get(int joystickId) { return joystick[joystickId]; } String getText(int joystickId) { return translateDirectionToEnglish(joystick[joystickId]); } /* end "public" */ void initialize() { joystick = new int[5]; joystick[0]=NEUTRAL; joystick[1]=NEUTRAL; joystick[2]=NEUTRAL; joystick[3]=NEUTRAL; joystick[4]=NEUTRAL; } String translateDirectionToEnglish(int direction) { if (direction==NEUTRAL) return "NEUTRAL"; else if (direction==UP) return "UP"; else if (direction==RIGHT) return "RIGHT"; else if (direction==DOWN) return "DOWN"; else if (direction==LEFT) return "LEFT"; else return "ERROR"; } /* THIS FUNCTION IS CALLED BY THE serial functions OR Keyboard functions. IF SOMEONE PUSHES JOYSTICK 1 UP, THEN setJoystickPosition WILL GET CALLED WITH joystickID=1 AND direction=1. IF THE PERSON LETS GO, THE JOYSTICK GOES BACK TO NEUTRAL/ MIDDLE AND SO setJoystickPosition WILL BE CALLED AGAIN WITH joystickID=1 AND direction=0. */ void setJoystickPosition(int joystickID, int direction) { if (joystick[joystickID] != direction) { if (DEBUG) println("Joystick #" + joystickID + " changed to " + translateDirectionToEnglish(direction)); joystick[joystickID]=direction; } } }