├── .gitignore ├── README.md ├── audio ├── explosion-0.mp3 ├── explosion-1.mp3 ├── explosion-2.mp3 ├── pew-0.mp3 ├── pew-1.mp3 └── pew-2.mp3 ├── collisionhelpers.js ├── docs ├── README.md ├── asteroid.js ├── audio │ ├── explosion-0.mp3 │ ├── explosion-1.mp3 │ ├── explosion-2.mp3 │ └── pew.mp3 ├── entity.js ├── hud.js ├── index.html ├── input.js ├── laser.js ├── libraries │ ├── p5.dom.js │ ├── p5.js │ └── p5.sound.js ├── ship.js └── sketch.js ├── entities ├── asteroid.js ├── entity.js ├── laser.js └── ship.js ├── index.html ├── input.js ├── libraries ├── p5.dom.js ├── p5.js └── p5.sound.js ├── managers ├── entitymanager.js ├── levelmanager.js └── uimanager.js ├── player.js ├── shape.js ├── sketch.js ├── ui ├── hud.js ├── playercontrols.js └── uielement.js └── world.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Asteroids 2 | 3 | [![Join the chat at https://gitter.im/CodingRainbow/Asteroids](https://badges.gitter.im/CodingRainbow/Asteroids.svg)](https://gitter.im/CodingRainbow/Asteroids?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | 5 | This repo is for the Asteroids project from Coding Challenge 45. 6 | 7 | ### Video Tutorials 8 | 1. Part 1: https://youtu.be/hacZU523FyM 9 | 2. Part 2: https://youtu.be/xTTuih7P0c0 10 | 11 | ### Play the game! 12 | * https://CodingTrain.github.io/asteroids-advanced/ 13 | 14 | ### Info 15 | 16 | The code here is an updated and improved version of the game based on viewer submissions via pull requests. The original code from the challenge itself can be found here: 17 | 18 | https://github.com/CodingRainbow/Rainbow-Code/tree/master/challenges/CC_46_Asteroids 19 | 20 | To submit a new issue, [file an issue describing your idea](https://github.com/CodingRainbow/Asteroids/issues/new) and [make a pull request](https://github.com/CodingRainbow/Asteroids/pulls)! If you make your own version of the game in a separate repo or fork, pull request this README file to add a link. 21 | 22 | I'll make a video about how to contribute soon too! 23 | 24 | ### Viewer Remixes of the Game 25 | 26 | * Add your name and link here! 27 | * Serge Korzh - https://github.com/qw8/Asteroids-1 28 | * Timo Köhler - https://navetk.github.io/Asteroids/ 29 | -------------------------------------------------------------------------------- /audio/explosion-0.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingTrain/asteroids-advanced/f1b08e345b2381f85bc1114361bd44780924d268/audio/explosion-0.mp3 -------------------------------------------------------------------------------- /audio/explosion-1.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingTrain/asteroids-advanced/f1b08e345b2381f85bc1114361bd44780924d268/audio/explosion-1.mp3 -------------------------------------------------------------------------------- /audio/explosion-2.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingTrain/asteroids-advanced/f1b08e345b2381f85bc1114361bd44780924d268/audio/explosion-2.mp3 -------------------------------------------------------------------------------- /audio/pew-0.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingTrain/asteroids-advanced/f1b08e345b2381f85bc1114361bd44780924d268/audio/pew-0.mp3 -------------------------------------------------------------------------------- /audio/pew-1.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingTrain/asteroids-advanced/f1b08e345b2381f85bc1114361bd44780924d268/audio/pew-1.mp3 -------------------------------------------------------------------------------- /audio/pew-2.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingTrain/asteroids-advanced/f1b08e345b2381f85bc1114361bd44780924d268/audio/pew-2.mp3 -------------------------------------------------------------------------------- /collisionhelpers.js: -------------------------------------------------------------------------------- 1 | function cross(v1, v2) { 2 | return v1.x * v2.y - v2.x * v1.y; 3 | } 4 | 5 | function lineIntersectCircle(l1, l2, c, r) { 6 | var dx = l2.x - l1.x; 7 | var dy = l2.y - l1.y; 8 | var dx2 = c.x - l1.x; 9 | var dy2 = c.y - l1.y; 10 | var x = dx * dx + dy * dy; 11 | var a = pow(dx2 * dy - dy2 * dx, 2) / x; 12 | return a <= r * r; 13 | } 14 | 15 | //intersection between two closed of lines 16 | function lineIntersect(l1v1, l1v2, l2v1, l2v2) { 17 | var base = p5.Vector.sub(l1v1, l2v1); 18 | var l1_vector = p5.Vector.sub(l1v2, l1v1); 19 | var l2_vector = p5.Vector.sub(l2v2, l2v1); 20 | var direction_cross = cross(l2_vector, l1_vector); 21 | var t = cross(base, l1_vector) / direction_cross; 22 | var u = cross(base, l2_vector) / direction_cross; 23 | if (t >= 0 && t <= 1 && u >= 0 && u <= 1) { 24 | return true; 25 | } else { 26 | return false; 27 | } 28 | } 29 | 30 | //intersection between one infinite line determined by point and angle and a closed of line 31 | function lineIntersect2(l1v1, angle, l2v1, l2v2) { 32 | var base = p5.Vector.sub(l1v1, l2v1); 33 | var l1_vector = p5.Vector.fromAngle(angle); 34 | var l2_vector = p5.Vector.sub(l2v2, l2v1); 35 | var direction_cross = cross(l2_vector, l1_vector); 36 | var t = cross(base, l1_vector) / direction_cross; 37 | if (t >= 0 && t <= 1) { 38 | return true; 39 | } else { 40 | return false; 41 | } 42 | } 43 | 44 | //intersection point between two infinite lines determined by two points each 45 | function lineIntersectionPoint(p1, p2, p3, p4) { 46 | var d = (p1.x - p2.x) * (p3.y - p4.y) - (p1.y - p2.y) * (p3.x - p4.x); 47 | if (d === 0) { 48 | return undefined; 49 | } 50 | var px = (cross(p1, p2) * (p3.x - p4.x) - (p1.x - p2.x) * cross(p3, p4)) / d; 51 | var py = (cross(p1, p2) * (p3.y - p4.y) - (p1.y - p2.y) * cross(p3, p4)) / d; 52 | return createVector(px, py); 53 | } 54 | 55 | //modulo that works with negative numbers because JS is stupid 56 | function mod(n, m) { 57 | return ((n % m) + m) % m; 58 | } -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Asteroids 2 | 3 | [![Join the chat at https://gitter.im/CodingRainbow/Asteroids](https://badges.gitter.im/CodingRainbow/Asteroids.svg)](https://gitter.im/CodingRainbow/Asteroids?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | 5 | This repo is for the Asteroids project from Coding Challenge 45. 6 | 7 | ### Video Tutorials 8 | 1. Part 1: https://youtu.be/hacZU523FyM 9 | 2. Part 2: https://youtu.be/xTTuih7P0c0 10 | 3. Stay tuned for Siraj's tutorial! 11 | 12 | ### Play the game! 13 | * https://codingrainbow.github.io/asteroids-advanced/ 14 | 15 | ### Info 16 | 17 | You are required to read and acknowledge [Issue #1](https://github.com/CodingRainbow/asteroids-advanced/issues/1). More info will be posted here soon. 18 | 19 | ### Viewer Remixes of the Game 20 | 21 | * Add your name and link here! 22 | * Serge Korzh - https://github.com/qw8/Asteroids-1 23 | * Timo Köhler - https://navetk.github.io/Asteroids/ 24 | -------------------------------------------------------------------------------- /docs/asteroid.js: -------------------------------------------------------------------------------- 1 | // Daniel Shiffman 2 | // http://codingrainbow.com 3 | // http://patreon.com/codingrainbow 4 | // Code for: https://youtu.be/hacZU523FyM 5 | 6 | function Asteroid(pos, r, size) { 7 | if (pos == null) { 8 | pos = createVector(random(width), random(height)); 9 | } 10 | 11 | r = r != null ? r * 0.5 : random(40, 60); 12 | Entity.call(this, pos.x, pos.y, r); 13 | 14 | this.vel = p5.Vector.random2D(); 15 | this.total = floor(random(7, 15)); 16 | 17 | //smaller asteroids go a bit faster 18 | this.size = size; 19 | switch(size) { 20 | case 1: 21 | this.vel.mult(1.5); break; 22 | case 0: 23 | this.vel.mult(2); break; 24 | } 25 | 26 | 27 | this.offset = []; 28 | for (var i = 0; i < this.total; i++) { 29 | this.offset[i] = random(-this.r * 0.2, this.r * 0.5); 30 | } 31 | 32 | Entity.prototype.setRotation.call(this, random(-0.03, 0.03)); 33 | 34 | this.render = function() { 35 | push(); 36 | stroke(255); 37 | noFill(); 38 | translate(this.pos.x, this.pos.y); 39 | rotate(this.heading); 40 | beginShape(); 41 | for (var i = 0; i < this.total; i++) { 42 | var angle = map(i, 0, this.total, 0, TWO_PI); 43 | var r = this.r + this.offset[i]; 44 | vertex(r * cos(angle), r * sin(angle)); 45 | } 46 | endShape(CLOSE); 47 | pop(); 48 | } 49 | 50 | this.playSoundEffect = function(soundArray){ 51 | soundArray[floor(random(0,soundArray.length))].play(); 52 | } 53 | 54 | this.breakup = function() { 55 | if(size > 0) 56 | return [new Asteroid(this.pos, this.r, this.size-1), new Asteroid(this.pos, this.r, this.size-1)]; 57 | else 58 | return []; 59 | } 60 | 61 | this.vertices = function() { 62 | var vertices = [] 63 | for(var i = 0; i < this.total; i++) { 64 | var angle = this.heading + map(i, 0, this.total, 0, TWO_PI); 65 | var r = this.r + this.offset[i]; 66 | vertices.push(p5.Vector.add(createVector(r * cos(angle), r * sin(angle)), this.pos)); 67 | } 68 | 69 | return vertices; 70 | } 71 | } 72 | 73 | Asteroid.prototype = Object.create(Entity.prototype); 74 | -------------------------------------------------------------------------------- /docs/audio/explosion-0.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingTrain/asteroids-advanced/f1b08e345b2381f85bc1114361bd44780924d268/docs/audio/explosion-0.mp3 -------------------------------------------------------------------------------- /docs/audio/explosion-1.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingTrain/asteroids-advanced/f1b08e345b2381f85bc1114361bd44780924d268/docs/audio/explosion-1.mp3 -------------------------------------------------------------------------------- /docs/audio/explosion-2.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingTrain/asteroids-advanced/f1b08e345b2381f85bc1114361bd44780924d268/docs/audio/explosion-2.mp3 -------------------------------------------------------------------------------- /docs/audio/pew.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingTrain/asteroids-advanced/f1b08e345b2381f85bc1114361bd44780924d268/docs/audio/pew.mp3 -------------------------------------------------------------------------------- /docs/entity.js: -------------------------------------------------------------------------------- 1 | function Entity(x, y, radius) 2 | { 3 | this.pos = createVector(x, y); 4 | this.r = radius; 5 | this.heading = 0; 6 | this.rotation = 0; 7 | this.vel = createVector(0, 0); 8 | this.accelMagnitude = 0; 9 | } 10 | 11 | Entity.prototype.update = function() { 12 | this.heading += this.rotation; 13 | 14 | // Accelerate using the heading and the accelMagnitude 15 | var force = p5.Vector.fromAngle(this.heading); 16 | force.mult(this.accelMagnitude); 17 | this.vel.add(force); 18 | 19 | this.pos.add(this.vel); 20 | this.edges(); 21 | } 22 | 23 | Entity.prototype.setAccel = function(magnitude) 24 | { 25 | this.accelMagnitude = magnitude; 26 | } 27 | 28 | Entity.prototype.edges = function() { 29 | if (this.pos.x > width + this.r) { 30 | this.pos.x = -this.r; 31 | } else if (this.pos.x < -this.r) { 32 | this.pos.x = width + this.r; 33 | } 34 | if (this.pos.y > height + this.r) { 35 | this.pos.y = -this.r; 36 | } else if (this.pos.y < -this.r) { 37 | this.pos.y = height + this.r; 38 | } 39 | } 40 | 41 | Entity.prototype.setRotation = function(rot) { 42 | this.rotation = rot; 43 | } 44 | -------------------------------------------------------------------------------- /docs/hud.js: -------------------------------------------------------------------------------- 1 | function Hud() { 2 | var size = 20; 3 | var padding = 10; 4 | var lifeWidth = 20; 5 | 6 | /* 7 | --0-- 8 | 1 2 9 | --3-- 10 | 4 5 11 | --6-- 12 | */ 13 | var digitMaps = [ 14 | //return a digit map 15 | [true, true, true, false, true, true, true], //0 16 | [false, false, true, false, false, true, false], //1 17 | [true, false, true, true, true, false, true], //2 18 | [true, false, true, true, false, true, true], //3 19 | [false, true, true, true, false, true, false], //4 20 | [true, true, false, true, false, true, true], //5 21 | [true, true, false, true, true, true, true], //6 22 | [true, false, true, false, false, true, false], //7 23 | [true, true, true, true, true, true, true], //8 24 | [true, true, true, true, false, true, true] //9 25 | 26 | ]; 27 | 28 | this.render = function() { 29 | var scoreString = "" + score; 30 | var digitPos = createVector((width / 2 - (scoreString.length * (size + padding)) / 2), padding); 31 | for(var i = 0; i < scoreString.length; i++) { 32 | var dmap = digitMaps[scoreString.charAt(i)]; 33 | drawDigit(dmap, i, digitPos); 34 | digitPos.x += size + padding; 35 | } 36 | drawLives(); 37 | if(lives < 0) { 38 | push(); 39 | textSize(32); 40 | fill(255); 41 | text("GAME OVER", (width / 2) - 100, height / 2); 42 | } 43 | } 44 | 45 | function drawLives() { 46 | push(); 47 | stroke(255); 48 | fill(0); 49 | var top = createVector((width / 2) + lifeWidth * 2, padding * 2 + size * 2); 50 | for(var i = 0; i < lives; i++) { 51 | triangle(top.x, top.y, top.x - lifeWidth / 2, top.y + 25, top.x + lifeWidth / 2, top.y + 25); 52 | top.x -= 20 + padding; 53 | } 54 | pop(); 55 | } 56 | 57 | //draws the digit based on the digit map 58 | function drawDigit(digitMap, index, pos) { 59 | push(); 60 | stroke(255); 61 | for(var i = 0; i < digitMap.length; i++) { 62 | if(digitMap[i] === true) 63 | drawLine(i, pos); 64 | } 65 | pop(); 66 | } 67 | 68 | //draws a line based on the line map 69 | function drawLine(lineMap, pos) { 70 | switch(lineMap) { 71 | case 0: 72 | line(pos.x, pos.y, pos.x + size, pos.y); 73 | break; 74 | case 1: 75 | line(pos.x, pos.y, pos.x, pos.y + size); 76 | break; 77 | case 2: 78 | line(pos.x + size, pos.y, pos.x + size, pos.y + size); 79 | break; 80 | case 3: 81 | line(pos.x, pos.y + size, pos.x + size, pos.y + size); 82 | break; 83 | case 4: 84 | line(pos.x, pos.y + size, pos.x, pos.y + 2 * size); 85 | break; 86 | case 5: 87 | line(pos.x + size, pos.y + size, pos.x + size, pos.y + 2 * size); 88 | break; 89 | case 6: 90 | line(pos.x, pos.y + size * 2, pos.x + size, pos.y + 2 * size); 91 | break; 92 | default: 93 | console.log("line map is invalid"); 94 | break; 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | Community Asteroids! - Coding Rainbow 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /docs/input.js: -------------------------------------------------------------------------------- 1 | var input = { 2 | listeners: {}, 3 | reset: function() { 4 | this.listeners = {}; 5 | }, 6 | registerAsListener: function(index, callback) { 7 | if (this.listeners[index] == undefined) { 8 | this.listeners[index] = []; 9 | } 10 | 11 | this.listeners[index].push(callback); 12 | }, 13 | handleEvent: function(char, code, press) { 14 | if (this.listeners[code] != undefined) { 15 | for (var i = 0; i < this.listeners[code].length; i++) { 16 | this.listeners[code][i](char, code, press); 17 | } 18 | } 19 | } 20 | }; 21 | 22 | function keyReleased() { 23 | input.handleEvent(key, keyCode, false); 24 | } 25 | 26 | function keyPressed() { 27 | input.handleEvent(key, keyCode, true); 28 | } 29 | -------------------------------------------------------------------------------- /docs/laser.js: -------------------------------------------------------------------------------- 1 | // Daniel Shiffman 2 | // http://codingrainbow.com 3 | // http://patreon.com/codingrainbow 4 | // Code for: https://youtu.be/hacZU523FyM 5 | 6 | var colors = [[248, 12, 18], [238, 17, 0], [255, 51, 17], [255, 68, 34], [255, 102, 68], [255, 153, 51], [254, 174, 45], [204, 187, 51], [208, 195, 16], [170, 204, 34], [105, 208, 37], [34, 204, 170], [18, 189, 185], [17, 170, 187], [68, 68, 221], [51, 17, 187], [59, 12, 189], [68, 34, 153]] 7 | 8 | function Laser(spos, angle) { 9 | Entity.call(this, spos.x, spos.y, 4); 10 | 11 | this.pos = createVector(spos.x, spos.y); 12 | this.vel = p5.Vector.fromAngle(angle); 13 | this.vel.mult(10); 14 | this.color = colors[floor(random(0,colors.length-1))]; 15 | 16 | this.render = function() { 17 | push(); 18 | stroke(this.color[0], this.color[1], this.color[2]); 19 | strokeWeight(this.r); 20 | point(this.pos.x, this.pos.y); 21 | pop(); 22 | } 23 | 24 | this.playSoundEffect = function(sound){ 25 | if (!sound.isPlaying()){ 26 | sound.play(); 27 | } 28 | } 29 | 30 | this.hits = function(asteroid) { 31 | var last_pos = p5.Vector.sub(this.pos, this.vel); 32 | var asteroid_vertices = asteroid.vertices(); 33 | for(var i = 0; i < asteroid_vertices.length - 1; i++) { 34 | if(lineIntersect(last_pos, this.pos, asteroid_vertices[i], asteroid_vertices[i + 1])) { 35 | return true; 36 | } 37 | } 38 | return false; 39 | } 40 | 41 | this.offscreen = function() { 42 | if (this.pos.x > width || this.pos.x < 0) { 43 | return true; 44 | } 45 | if (this.pos.y > height || this.pos.y < 0) { 46 | return true; 47 | } 48 | return false; 49 | } 50 | 51 | 52 | } 53 | 54 | Laser.prototype = Object.create(Entity.prototype); 55 | -------------------------------------------------------------------------------- /docs/ship.js: -------------------------------------------------------------------------------- 1 | // Daniel Shiffman 2 | // http://codingrainbow.com 3 | // http://patreon.com/codingrainbow 4 | // Code for: https://youtu.be/hacZU523FyM 5 | 6 | function Ship(pos, r) { 7 | Entity.call(this, width / 2, height / 2, 20); 8 | this.isDestroyed = false; 9 | this.destroyFrames = 600; 10 | this.shields = shieldTime; 11 | 12 | var scope = this; 13 | input.registerAsListener(" ".charCodeAt(0), function(char, code, press) { 14 | if (!press) { 15 | return; 16 | } 17 | 18 | var laser = new Laser(scope.pos, scope.heading); 19 | laser.playSoundEffect(laserSoundEffect); 20 | lasers.push(laser); 21 | }); 22 | input.registerAsListener(RIGHT_ARROW, function(char, code, press) { scope.setRotation(press ? 0.08 : 0); }); 23 | input.registerAsListener(LEFT_ARROW, function(char, code, press) { scope.setRotation(press ? -0.08 : 0); }); 24 | input.registerAsListener(UP_ARROW, function(char, code, press) { scope.setAccel(press ? 0.1 : 0); }); 25 | 26 | this.update = function() { 27 | Entity.prototype.update.call(this); 28 | this.vel.mult(0.99); 29 | if(this.isDestroyed) { 30 | for(var i = 0; i < this.brokenParts.length; i++) { 31 | this.brokenParts[i].pos.add(this.brokenParts[i].vel); 32 | this.brokenParts[i].heading += this.brokenParts[i].rot; 33 | } 34 | } else { 35 | this.vel.mult(0.99); 36 | } 37 | if (this.shields > 0) { 38 | this.shields -= 1; 39 | } 40 | } 41 | 42 | this.brokenParts = []; 43 | this.destroy = function() { 44 | this.isDestroyed = true; 45 | for(var i = 0; i < 4; i++) 46 | this.brokenParts[i] = { 47 | pos: this.pos.copy(), 48 | vel: p5.Vector.random2D(), 49 | heading: random(0, 360), 50 | rot: random(-0.07, 0.07) 51 | }; 52 | } 53 | 54 | this.hits = function(asteroid) { 55 | if (this.shields > 0) { 56 | return false; 57 | } 58 | var vertices = [ 59 | createVector(this.pos.x - 2/3 * this.r, this.pos.y - this.r), 60 | createVector(this.pos.x - 2/3 * this.r, this.pos.y + this.r), 61 | createVector(this.pos.x + 4/3 * this.r, this.pos.y + 0) 62 | ]; 63 | var asteroid_vertices = asteroid.vertices(); 64 | for(var i = 0; i < asteroid_vertices.length; i++) { 65 | for(var j = 0; j < vertices.length; j++) { 66 | var opposite = vertices.slice(0); 67 | opposite.splice(j, 1); 68 | if(lineIntersect(opposite[0], opposite[1], asteroid_vertices[i], asteroid_vertices[(i + 1) % asteroid_vertices.length])) { 69 | return true; 70 | } 71 | } 72 | } 73 | return false; 74 | } 75 | 76 | this.render = function() { 77 | if(this.isDestroyed) { 78 | for(var i = 0; i < this.brokenParts.length; i++) { 79 | push(); 80 | stroke(floor(255 * ((this.destroyFrames--) / 600))); 81 | var bp = this.brokenParts[i]; 82 | translate(bp.pos.x, bp.pos.y); 83 | rotate(bp.heading); 84 | line(-this.r / 2, -this.r / 2, this.r / 2, this.r / 2); 85 | pop(); 86 | } 87 | } else { 88 | push(); 89 | translate(this.pos.x, this.pos.y); 90 | rotate(this.heading); 91 | fill(0); 92 | var shieldCol = random(map(this.shields, 0, shieldTime, 255, 0), 255); 93 | stroke(shieldCol, shieldCol, 255); 94 | triangle(-2/3*this.r, -this.r, -2/3*this.r, this.r, 4/3*this.r, 0); 95 | 96 | if(this.accelMagnitude != 0) { 97 | translate(-this.r, 0); 98 | rotate(random(PI / 4, 3 * PI / 4)); 99 | line(0, 0, 0, 10); 100 | } 101 | 102 | pop(); 103 | } 104 | } 105 | } 106 | 107 | Ship.prototype = Object.create(Entity.prototype); -------------------------------------------------------------------------------- /docs/sketch.js: -------------------------------------------------------------------------------- 1 | // Daniel Shiffman 2 | // http://codingrainbow.com 3 | // http://patreon.com/codingrainbow 4 | // Code for: https://youtu.be/hacZU523FyM 5 | 6 | var ship; 7 | var hud; 8 | var asteroids = []; 9 | var lasers = []; 10 | var laserSoundEffect; 11 | var explosionSoundEffects = []; 12 | var canPlay = true; 13 | var shieldTime = 180; 14 | 15 | function preload() { 16 | laserSoundEffect = loadSound('audio/pew.mp3'); 17 | for (var i =0; i < 3; i++){ 18 | explosionSoundEffects[i] = loadSound('audio/explosion-'+i+'.mp3'); 19 | } 20 | } 21 | var score = 0; 22 | var lives = 3; 23 | var points = [100, 50, 20]; // small, med, large points 24 | var level = 0; 25 | 26 | function setup() { 27 | createCanvas(windowWidth, windowHeight); 28 | ship = new Ship(); 29 | hud = new Hud(); 30 | spawnAsteroids(); 31 | } 32 | 33 | function draw() { 34 | for(var i = 0; i < asteroids.length; i++) { 35 | if(ship.hits(asteroids[i]) && canPlay) { 36 | canPlay = false; 37 | ship.destroy(); 38 | input.reset(); 39 | setTimeout(function() { 40 | lives--; 41 | if(lives >= 0) { 42 | ship = new Ship(); 43 | canPlay = true; 44 | } 45 | }, 3000); 46 | } 47 | asteroids[i].update(); 48 | } 49 | 50 | for(var i = lasers.length - 1; i >= 0; i--) { 51 | lasers[i].update(); 52 | if(lasers[i].offscreen()) { 53 | lasers.splice(i, 1); 54 | 55 | continue; 56 | } 57 | 58 | for (var j = asteroids.length - 1; j >= 0; j--) { 59 | if (lasers[i].hits(asteroids[j])) { 60 | asteroids[j].playSoundEffect(explosionSoundEffects); 61 | score += points[asteroids[j].size]; 62 | var newAsteroids = asteroids[j].breakup(); 63 | asteroids = asteroids.concat(newAsteroids); 64 | asteroids.splice(j, 1); 65 | lasers.splice(i, 1); 66 | if(asteroids.length == 0) { 67 | level++; 68 | spawnAsteroids(); 69 | ship.shields = shieldTime; 70 | } 71 | break; 72 | } 73 | } 74 | } 75 | 76 | ship.update(); 77 | 78 | // Render 79 | background(0); 80 | 81 | for (var i = 0; i < asteroids.length; i++) { 82 | asteroids[i].render(); 83 | } 84 | 85 | for (var i = lasers.length - 1; i >= 0; i--) { 86 | lasers[i].render(); 87 | } 88 | 89 | ship.render(); 90 | hud.render(); 91 | } 92 | 93 | function spawnAsteroids() { 94 | for(var i = 0; i < level + 5; i++) { 95 | asteroids.push(new Asteroid(null, null, 2)); 96 | } 97 | } 98 | 99 | function cross(v1, v2) { 100 | return v1.x * v2.y - v2.x * v1.y; 101 | } 102 | 103 | function lineIntersect(l1v1, l1v2, l2v1, l2v2) { 104 | var base = p5.Vector.sub(l1v1, l2v1); 105 | var l1_vector = p5.Vector.sub(l1v2, l1v1); 106 | var l2_vector = p5.Vector.sub(l2v2, l2v1); 107 | var direction_cross = cross(l2_vector, l1_vector); 108 | var t = cross(base, l1_vector) / direction_cross; 109 | var u = cross(base, l2_vector) / direction_cross; 110 | if(t >= 0 && t <= 1 && u >= 0 && u <= 1) { 111 | return true; 112 | } else { 113 | return false; 114 | } 115 | } -------------------------------------------------------------------------------- /entities/asteroid.js: -------------------------------------------------------------------------------- 1 | // Daniel Shiffman 2 | // http://codingrainbow.com 3 | // http://patreon.com/codingrainbow 4 | // Code for: https://youtu.be/hacZU523FyM 5 | 6 | function Asteroid(world, params) { 7 | var levelmanager = params.levelmanager; 8 | params.pos = params.pos !== undefined ? params.pos : createVector(random(-world.halfwidth, world.halfwidth), random(-world.halfheight, world.halfheight)); 9 | params.r = params.r !== undefined ? params.r : random(80, 160); 10 | params.mass = params.mass !== undefined ? params.mass : PI * params.r * params.r; 11 | Entity.call(this, params); 12 | this.vel = params.vel !== undefined ? params.vel : createVector(0, 0); 13 | Entity.prototype.applyForce.call(this, params.force !== undefined ? params.force : p5.Vector.random2D().mult(5000)); 14 | Entity.prototype.applyTorque.call(this, random(-0.03, 0.03)); 15 | this.heading = params.heading !== undefined ? params.heading : 0; 16 | this.c = params.c !== undefined ? params.c : color(255); 17 | const minArea = 750; 18 | 19 | if (params.shape === undefined) { 20 | vertices = []; 21 | this.total = floor(random(7, 15)); 22 | var range = this.r * 0.5; 23 | for (var i = 0; i < this.total; i++) { 24 | var angle = map(i, 0, this.total, 0, TWO_PI); 25 | var r = this.r - random(0, range); 26 | vertices.push(createVector(r * cos(angle), r * sin(angle))); 27 | } 28 | this.shape = new Shape(vertices); 29 | } else { 30 | this.shape = params.shape; 31 | this.total = this.shape.vertices.length; 32 | var offset = this.shape.centroid; 33 | this.pos.add(offset.rotate(this.heading)); 34 | this.shape.recenter(); 35 | } 36 | 37 | if(params.debris === true) { 38 | this.shape.breakAnime(20); 39 | this.canCollide = false; 40 | this.rotation = 0; 41 | } 42 | else { 43 | levelmanager.recordAsteroidCreation(); 44 | } 45 | 46 | this.render = function() { 47 | push(); 48 | strokeWeight(3); 49 | colorMode(RGB); 50 | noFill(); 51 | if (this.canCollide) { 52 | stroke(255); 53 | } else { 54 | stroke(red(this.c), green(this.c), blue(this.c), this.shape.fade()); 55 | } 56 | translate(this.pos.x, this.pos.y); 57 | rotate(this.heading); 58 | if (!this.shape.draw()) this.dead = true; 59 | pop(); 60 | } 61 | 62 | this.collides = function(entity) { } 63 | 64 | this.collision = function(entity) { 65 | if (!this.dead && entity.toString() === "[object Laser]") { 66 | playSoundEffect(explosionSoundEffects[floor(random(0, explosionSoundEffects.length))]); 67 | 68 | var destroyedArea = 0; 69 | if (this.shape.area < minArea * 2) { 70 | this.c = entity.c; 71 | this.shape.breakAnime(20); 72 | this.canCollide = false; 73 | this.rotation = 0; 74 | destroyedArea = this.shape.area; 75 | } else { 76 | this.intersectionPoints = this.shape.intersections(p5.Vector.sub(p5.Vector.sub(entity.pos, entity.vel), this.pos).rotate(-this.heading), entity.vel.heading()-this.heading); 77 | if(this.intersectionPoints.length > 0) { 78 | var impact = this.intersectionPoints[0].copy().rotate(this.heading); 79 | impact.add(this.pos); 80 | 81 | vertices = []; 82 | for (var i = 0; i < TWO_PI; i += TWO_PI / 10) { 83 | var r = 30 + random(10); 84 | vertices.push(createVector(r * cos(i), r * sin(i))); 85 | } 86 | crateShape = new Shape(vertices); 87 | 88 | var newShapes = this.shape.sub(this.pos, this.heading, impact, 0, crateShape); 89 | 90 | newShapes = Shape.makeAsteroidSized(newShapes); 91 | 92 | while(newShapes[0].length == 1) { 93 | newShapes[0] = newShapes[0][0].splitAtWeakestPoint(); 94 | newShapes = Shape.makeAsteroidSized(newShapes); 95 | } 96 | 97 | var scope = this; 98 | world.addEndFrameTask(function() { 99 | for(var j = 0; j < newShapes[0].length; j++) { 100 | world.createEntity(Asteroid, { 101 | pos: scope.pos.copy(), 102 | r: newShapes[0][j].r, 103 | shape: newShapes[0][j], 104 | vel: scope.vel.copy(), 105 | heading: scope.heading, 106 | force: newShapes[0][j].centroid.copy().mult(50), 107 | levelmanager: levelmanager 108 | }); 109 | } 110 | for(j = 0; j < newShapes[1].length; j++) { 111 | world.createEntity(Asteroid, { 112 | pos: scope.pos.copy(), 113 | r: newShapes[1][j].r, 114 | shape: newShapes[1][j], 115 | vel: scope.vel.copy(), 116 | heading: scope.heading, 117 | debris: true, 118 | force: createVector(0, 0), 119 | c: entity.c, 120 | levelmanager: levelmanager 121 | }); 122 | } 123 | }); 124 | 125 | destroyedArea = this.shape.area; 126 | for( var j = 0; j < newShapes[0].length; j++ ) { 127 | destroyedArea -= newShapes[0][j].area; 128 | } 129 | 130 | this.dead = true; 131 | } 132 | } 133 | levelmanager.recordKill(entity.owner, destroyedArea); 134 | } 135 | } 136 | 137 | this.globalVertices = function() { 138 | return this.shape.globalVertices(this.pos, this.heading); 139 | } 140 | 141 | this.toString = function() { 142 | return "[object Asteroid]"; 143 | } 144 | } 145 | 146 | 147 | Asteroid.prototype = Object.create(Entity.prototype); -------------------------------------------------------------------------------- /entities/entity.js: -------------------------------------------------------------------------------- 1 | function Entity(params) { 2 | this.id = -1; 3 | this.canCollide = true; 4 | this.dead = false; 5 | this.mass = params.mass !== undefined ? params.mass : 1; 6 | this.heading = 0; 7 | this.pos = params.pos !== undefined ? params.pos : createVector(0, 0); 8 | this.r = params.r !== undefined ? params.r : 1; 9 | this.rotation = 0; 10 | this.vel = createVector(0, 0); 11 | this.force = createVector(0, 0); 12 | this.torque = 0; 13 | this.velMu = 0; 14 | this.rotMu = 0; 15 | this.velDrag = 0; 16 | this.rotDrag = 0; 17 | this.owner = params.owner !== undefined ? params.owner : -1; 18 | } 19 | 20 | Entity.prototype.registerId = function(id) { 21 | this.id = id; 22 | } 23 | 24 | Entity.prototype.edges = function() { 25 | if (this.pos.x - this.r > world.halfwidth) { 26 | this.pos.x = this.pos.x % world.halfwidth - world.halfwidth; 27 | } else if (this.pos.x + this.r < -world.halfwidth) { 28 | this.pos.x = this.pos.x % world.halfwidth + world.halfwidth; 29 | } 30 | if (this.pos.y - this.r > world.halfheight) { 31 | this.pos.y = this.pos.y % world.halfheight - world.halfheight; 32 | } else if (this.pos.y + this.r < -world.halfheight) { 33 | this.pos.y = this.pos.y % world.halfheight + world.halfheight; 34 | } 35 | var playerPos = world.getLocalPlayer().getEntity().pos; 36 | var relPos = p5.Vector.sub(this.pos, playerPos); 37 | var halfWinWid = windowWidth / 2; 38 | var halfWinHig = windowHeight / 2 39 | if (relPos.x + world.width - this.r < halfWinWid) this.pos.x += world.width; 40 | else if (relPos.x - world.width + this.r > -halfWinWid) this.pos.x -= world.width; 41 | if (relPos.y + world.height - this.r < halfWinHig) this.pos.y += world.height; 42 | else if (relPos.y - world.height + this.r > -halfWinHig) this.pos.y -= world.height; 43 | } 44 | 45 | Entity.prototype.applyForce = function(force) { 46 | this.force.add(force); 47 | } 48 | 49 | Entity.prototype.applyTorque = function(torque) { 50 | this.torque += torque; 51 | } 52 | 53 | Entity.prototype.predictVelocity = function() { 54 | var accel = this.force.copy().div(this.mass); 55 | return this.vel.copy().add(accel); 56 | } 57 | 58 | Entity.prototype.predictRotation = function() { 59 | var rotAccel = this.torque / this.mass; 60 | return this.rotation + rotAccel; 61 | } 62 | 63 | Entity.prototype.momentum = function() { 64 | var momentum = this.vel.copy(); 65 | momentum.mult(this.mass); 66 | return momentum; 67 | } 68 | 69 | const g = 9.81; 70 | 71 | Entity.prototype.calculateMu = function(breakThrough) { 72 | var R = this.mass * g; 73 | return breakThrough / R; 74 | } 75 | 76 | Entity.calculateDragCo = function(maxForce, maxVel) { 77 | return maxForce / (maxVel * maxVel); 78 | } 79 | 80 | Entity.calculateMoment = function(localPoint, force) { 81 | return cross(localPoint, force) / localPoint.mag(); 82 | } 83 | 84 | Entity.globalPoint = function(scope, localPoint) { 85 | var point = localPoint.copy(); 86 | point.rotate(scope.heading); 87 | point.add(scope.pos); 88 | return point; 89 | } 90 | 91 | Entity.prototype.collides = function(entity) { 92 | if (!(this.canCollide && entity.canCollide)) { 93 | return false; 94 | } 95 | 96 | var dx = this.pos.x - entity.pos.x; 97 | var dy = this.pos.y - entity.pos.y; 98 | var dr = this.r + entity.r; 99 | return dx * dx + dy * dy <= dr * dr; 100 | } 101 | 102 | Entity.prototype.collision = function() {} 103 | 104 | Entity.prototype.update = function() { 105 | if (this.dead) { 106 | return true; 107 | } 108 | 109 | var R = this.mass * g; 110 | if (this.velMu > 0) { 111 | var F = this.velMu * R; 112 | if (this.vel.magSq() > 0) { 113 | var normVel = this.vel.copy().normalize(); 114 | var frict = normVel.copy().mult(-F); 115 | this.applyForce(frict); 116 | if (p5.Vector.dot(this.vel, this.predictVelocity()) < 0) { 117 | frict.mult(-1); 118 | var force = normVel; 119 | force.mult(-p5.Vector.dot(normVel, this.force.copy())); 120 | this.applyForce(frict); 121 | this.applyForce(force); 122 | this.vel.mult(0); 123 | } 124 | } else if (this.force.magSq() > F * F) { 125 | this.applyForce(this.force.copy().normalize().mult(-F)); 126 | } else { 127 | this.force.mult(0); 128 | } 129 | } 130 | 131 | if (this.rotMu > 0) { 132 | var F = this.rotMu * R; 133 | if (this.rotation != 0) { 134 | this.applyTorque(-F * (this.rotation > 0 ? 1 : -1)); 135 | if ((this.rotation > 0) != (this.predictRotation() > 0)) { 136 | this.torque = 0; 137 | this.rotation = 0; 138 | } 139 | } else if (abs(this.torque) > F) { 140 | this.applyTorque(-F * (this.torque > 0 ? 1 : -1)); 141 | } else { 142 | this.torque = 0; 143 | } 144 | } 145 | 146 | if (this.velDrag != 0) { 147 | this.applyForce(this.vel.copy().mult(-this.velDrag * this.vel.mag())); 148 | } 149 | 150 | if (this.rotDrag > 0) { 151 | var drag = this.rotDrag * this.rotation * this.rotation; 152 | this.applyTorque(this.rotation > 0 ? -drag : drag); 153 | } 154 | 155 | // Acceleration 156 | if (this.force.x != 0 || this.force.y != 0) this.vel.add(this.force.div(this.mass)); 157 | // Rotational Acceleration 158 | if (this.torque != 0) this.rotation += this.torque / this.mass; 159 | 160 | this.pos.add(this.vel); 161 | this.heading += this.rotation; 162 | 163 | this.edges(); 164 | if (this.force.x != 0 || this.force.y != 0) this.force.mult(0); 165 | this.torque = 0; 166 | } 167 | 168 | Entity.prototype.render = function() { 169 | push(); 170 | translate(this.pos.x, this.pos.y); 171 | rotate(this.heading); 172 | fill(0); 173 | stroke(255); 174 | ellipse(this.pos.x, this.pos.y, this.r); 175 | pop(); 176 | } 177 | -------------------------------------------------------------------------------- /entities/laser.js: -------------------------------------------------------------------------------- 1 | // Daniel Shiffman 2 | // http://codingrainbow.com 3 | // http://patreon.com/codingrainbow 4 | // Code for: https://youtu.be/hacZU523FyM 5 | 6 | function Laser(world, params) { 7 | params.r = params.r !== undefined ? params.r : 4; 8 | Entity.call(this, params); 9 | 10 | this.vel = p5.Vector.fromAngle(params.heading).mult(40); 11 | this.vel.add(params.initialVel); 12 | this.c = params.c ? params.c : color(255); 13 | this.duration = params.duration !== undefined ? params.duration : 20; 14 | var maxDuration = this.duration; 15 | 16 | playSoundEffect(laserSoundEffect[floor(random(3))]); 17 | 18 | this.update = function() { 19 | this.duration--; 20 | return Entity.prototype.update.call(this) || this.duration < 0; 21 | } 22 | 23 | this.render = function() { 24 | push(); 25 | translate(this.pos.x, this.pos.y); 26 | colorMode(RGB); 27 | stroke(red(this.c), green(this.c), blue(this.c), 55 + 200 * this.duration / maxDuration); 28 | strokeWeight(this.r); 29 | strokeCap(SQUARE); 30 | var halfLine = this.vel.copy(); 31 | halfLine.mult(0.5); 32 | line(-halfLine.x, -halfLine.y, halfLine.x, halfLine.y); 33 | pop(); 34 | } 35 | 36 | 37 | this.collides = function(entity) { 38 | var tail = p5.Vector.sub(this.pos, this.vel.copy().mult(1.2)); 39 | if (entity.toString() !== "[object Asteroid]" 40 | || !Entity.prototype.collides.call(this, entity) || !lineIntersectCircle(this.pos, tail, entity.pos, entity.r)) { 41 | return false; 42 | } 43 | 44 | var verts = entity.shape.vertices; 45 | for (var i = 0, j = entity.total - 1; i < entity.total; j = i++) 46 | if (lineIntersect(Entity.globalPoint(entity, verts[i]), Entity.globalPoint(entity, verts[j]), this.pos, tail)) return true; 47 | return false; 48 | } 49 | 50 | this.collision = function(entity) { 51 | if (entity.toString() === "[object Asteroid]") { 52 | this.dead = true; 53 | } 54 | } 55 | 56 | this.toString = function() { 57 | return "[object Laser]"; 58 | } 59 | } 60 | 61 | Laser.prototype = Object.create(Entity.prototype); 62 | -------------------------------------------------------------------------------- /entities/ship.js: -------------------------------------------------------------------------------- 1 | // Daniel Shiffman 2 | // http://codingrainbow.com 3 | // http://patreon.com/codingrainbow 4 | // Code for: https://youtu.be/hacZU523FyM 5 | 6 | function Ship(world, params) { 7 | Entity.call(this, params); 8 | this.lives = params.lives !== undefined ? params.lives : 3; 9 | var shieldDuration = params.shieldDuration !== undefined ? params.shieldDuration : 180; 10 | this.shields = shieldDuration; 11 | var resetPos = this.pos.copy(); 12 | var respawnFramesReset = 300; 13 | var respawnFrames; 14 | this.mass = 1000; 15 | this.thrustPower = params.thrustPower !== undefined ? params.thrustPower : { 16 | forward: 200, 17 | backward: 100, 18 | left: 180, 19 | right: 180, 20 | stabilization: 200, 21 | rotation: 80 22 | }; 23 | this.maxThrust = params.maxThrust !== undefined ? params.maxThrust : 100; 24 | this.coefficients = { 25 | velMu: 1 / this.thrustPower.stabilization, 26 | rotMu: 0, 27 | velDrag: Entity.calculateDragCo(this.maxThrust, 15), 28 | rotDrag: 0 29 | } 30 | this.velMu = this.coefficients.velMu; 31 | this.velDrag = this.coefficients.velDrag; 32 | this.front = createVector(4 / 3 * this.r, 0); 33 | this.shape = new Shape([ 34 | createVector(-2 / 3 * this.r, -this.r), 35 | createVector(-2 / 3 * this.r, this.r), 36 | this.front 37 | ]); 38 | this.colors = [ 39 | color(243, 89 , 86 ), 40 | color(241, 197, 0 ), 41 | color(73 , 187, 108), 42 | color(36 , 148, 193), 43 | color(150, 89 , 167) 44 | ] 45 | this.colorIndex = 0; 46 | 47 | var fireColors = []; 48 | for (var i = 0; i * 10 <= 255; i++) { 49 | fireColors[i] = "rgb(255," + i * 10 + ",0)"; 50 | } 51 | 52 | var stabToggle = true; 53 | var rateOfFire = 40; 54 | var lastShot = 0; 55 | var scope = this; 56 | 57 | var inputs = { 58 | targetPoint: createVector(1, 0), 59 | thrustVector: createVector(0, 0), 60 | laser: false 61 | }; 62 | 63 | this.setInputs = function(targetPoint, thrustForward, thrustBackwards, thrustLeft, thrustRight, stabilizationToggle, laser) { 64 | var upRight = p5.Vector.dot(p5.Vector.fromAngle(this.heading), createVector(0, -1)); 65 | inputs.thrustVector = createVector( 66 | (thrustForward ? this.thrustPower.forward : 0) + (thrustBackwards ? -this.thrustPower.backward : 0), 67 | (upRight > -0.259 ? 1 : -1) * ((thrustRight ? this.thrustPower.right : 0) + (thrustLeft ? -this.thrustPower.left : 0)) 68 | ); 69 | inputs.targetPoint = targetPoint; 70 | inputs.laser = laser; 71 | 72 | if (stabilizationToggle) { 73 | stabToggle = !stabToggle; 74 | } 75 | } 76 | 77 | this.registerId = function(id) { 78 | Entity.prototype.registerId.call(this, id); 79 | } 80 | 81 | this.collides = function(entity) { 82 | if (this.shields > 0 || 83 | entity.toString() !== "[object Asteroid]" || 84 | !Entity.prototype.collides.call(this, entity)) { 85 | return false; 86 | } 87 | 88 | var verts = this.globalVertices(); 89 | var asteroid_vertices = entity.globalVertices(); 90 | for (var i = 0; i < asteroid_vertices.length; i++) { 91 | for (var j = 0; j < verts.length; j++) { 92 | if (lineIntersect(verts[j], verts[(j + 1) % verts.length], asteroid_vertices[i], asteroid_vertices[(i + 1) % asteroid_vertices.length])) { 93 | return true; 94 | } 95 | } 96 | } 97 | return false; 98 | } 99 | 100 | this.collision = function(entity) { 101 | if (entity.toString(entity) === "[object Asteroid]") { 102 | this.lives--; 103 | if (this.lives === 0 && this.owner !== -1) { 104 | world.getPlayer(this.owner).dead = true; 105 | } 106 | 107 | this.canCollide = false; 108 | this.shape.breakAnime(); 109 | respawnFrames = respawnFramesReset; 110 | } 111 | } 112 | 113 | this.regenShields = function() { 114 | this.shields = shieldDuration; 115 | } 116 | 117 | this.update = function() { 118 | 119 | if (this.canCollide) { 120 | var force = p5.Vector.sub(inputs.targetPoint.copy(), this.front.copy().rotate(this.heading)); 121 | force.normalize(); 122 | force.mult(this.thrustPower.rotation); 123 | this.applyTorque(Entity.calculateMoment(inputs.targetPoint, force)); 124 | var sinTheta = cross(p5.Vector.fromAngle(this.heading), force) / force.mag(); 125 | if (abs(sin(this.predictRotation())) > abs(sinTheta)) { 126 | this.torque = 0; 127 | this.rotation = 0; 128 | this.heading += asin(sinTheta); 129 | } 130 | 131 | this.applyForce(inputs.thrustVector.rotate(this.heading)); 132 | 133 | if (lastShot > 0) { 134 | lastShot--; 135 | } else if (inputs.laser) { 136 | var temp = this.colors[0]; 137 | world.addEndFrameTask(function(world) { 138 | world.createEntity(Laser, { 139 | pos: scope.front.copy().add(createVector(20, 0)).rotate(scope.heading).add(scope.pos), 140 | heading: scope.heading, 141 | c: scope.colors[scope.colorIndex], 142 | initialVel: scope.vel, 143 | owner: scope.owner 144 | }); 145 | scope.colorIndex++; 146 | scope.colorIndex %= scope.colors.length; 147 | }); 148 | lastShot = rateOfFire; 149 | } 150 | 151 | this.velMu = stabToggle && (inputs.thrustVector.x === 0 && inputs.thrustVector.y === 0) ? this.coefficients.velMu : 0; 152 | if (Entity.prototype.update.call(this)) { 153 | return true; 154 | } 155 | 156 | if (this.shields > 0) { 157 | this.shields--; 158 | } 159 | } 160 | } 161 | 162 | this.render = function() { 163 | push(); 164 | translate(this.pos.x, this.pos.y); 165 | rotate(this.heading); 166 | colorMode(RGB); 167 | noFill(); 168 | strokeWeight(3); 169 | if (!this.canCollide) { 170 | strokeCap(ROUND); 171 | stroke(255, 255, 255, this.shape.fade()); 172 | if (!this.shape.draw()) { 173 | this.reset(); 174 | if (this.lives === 0) this.dead = true; 175 | } 176 | } else { 177 | var shieldCol = random(map(this.shields, 0, shieldDuration, 255, 0), 255); 178 | stroke(shieldCol, shieldCol, 255); 179 | this.shape.draw(); 180 | if (inputs.thrust) { 181 | translate(-this.r, 0); 182 | rotate(random(PI / 4, 3 * PI / 4)); 183 | stroke(fireColors[floor(random(fireColors.length))]); 184 | line(0, 0, 0, 10); 185 | } 186 | } 187 | pop(); 188 | } 189 | 190 | this.reset = function() { 191 | this.canCollide = true; 192 | this.regenShields(); 193 | this.pos.set(resetPos.x, resetPos.y); 194 | this.vel.set(0, 0); 195 | this.lastShot = 0; 196 | } 197 | 198 | this.globalVertices = function() { 199 | return this.shape.globalVertices(this.pos, this.heading); 200 | } 201 | 202 | this.toString = function() { 203 | return "[object Ship]"; 204 | } 205 | } 206 | 207 | Ship.prototype = Object.create(Entity.prototype); 208 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | Community Asteroids! - Coding Rainbow 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 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /input.js: -------------------------------------------------------------------------------- 1 | var input = { 2 | listeners: { 3 | key: {}, 4 | mouse: {} 5 | }, 6 | 7 | registerMouseListener: function(id, index, callback) { 8 | if (this.listeners.mouse[index] == undefined) { 9 | this.listeners.mouse[index] = {}; 10 | } 11 | if (this.listeners.mouse[index][id] == undefined) { 12 | this.listeners.mouse[index][id] = []; 13 | } 14 | 15 | this.listeners.mouse[index][id].push(callback); 16 | }, 17 | 18 | deregisterMouseListener: function(id, index) { 19 | delete this.listeners.mouse[index][id]; 20 | }, 21 | 22 | handleMouseEvent: function(button, press) { 23 | if (this.listeners.mouse[button] != undefined) { 24 | for (var i in this.listeners.mouse[button]) { 25 | for (var j = 0; j < this.listeners.mouse[button][i].length; j++) { 26 | this.listeners.mouse[button][i][j](button, press); 27 | } 28 | } 29 | } 30 | }, 31 | 32 | registerKeyListener: function(id, index, callback) { 33 | if (this.listeners.key[index] == undefined) { 34 | this.listeners.key[index] = {}; 35 | } 36 | if (this.listeners.key[index][id] == undefined) { 37 | this.listeners.key[index][id] = []; 38 | } 39 | 40 | this.listeners.key[index][id].push(callback); 41 | }, 42 | 43 | deregisterKeyListener: function(id, index) { 44 | delete this.listeners.key[index][id]; 45 | }, 46 | 47 | handleKeyEvent: function(char, code, press) { 48 | if (this.listeners.key[code] != undefined) { 49 | for (var i in this.listeners.key[code]) { 50 | for (var j = 0; j < this.listeners.key[code][i].length; j++) { 51 | this.listeners.key[code][i][j](char, code, press); 52 | } 53 | } 54 | } 55 | } 56 | }; 57 | 58 | function keyReleased() { 59 | input.handleKeyEvent(key, keyCode, false); 60 | } 61 | 62 | function keyPressed() { 63 | input.handleKeyEvent(key, keyCode, true); 64 | } 65 | 66 | function mousePressed() { 67 | input.handleMouseEvent(mouseButton, true); 68 | } 69 | 70 | function mouseReleased() { 71 | input.handleMouseEvent(mouseButton, false); 72 | } 73 | -------------------------------------------------------------------------------- /libraries/p5.dom.js: -------------------------------------------------------------------------------- 1 | /*! p5.dom.js v0.2.13 Oct 1, 2016 */ 2 | /** 3 | *

The web is much more than just canvas and p5.dom makes it easy to interact 4 | * with other HTML5 objects, including text, hyperlink, image, input, video, 5 | * audio, and webcam.

6 | *

There is a set of creation methods, DOM manipulation methods, and 7 | * an extended p5.Element that supports a range of HTML elements. See the 8 | * 9 | * beyond the canvas tutorial for a full overview of how this addon works. 10 | * 11 | *

Methods and properties shown in black are part of the p5.js core, items in 12 | * blue are part of the p5.dom library. You will need to include an extra file 13 | * in order to access the blue functions. See the 14 | * using a library 15 | * section for information on how to include this library. p5.dom comes with 16 | * p5 complete or you can download the single file 17 | * 18 | * here.

19 | *

See tutorial: beyond the canvas 20 | * for more info on how to use this libary. 21 | * 22 | * @module p5.dom 23 | * @submodule p5.dom 24 | * @for p5.dom 25 | * @main 26 | */ 27 | 28 | (function (root, factory) { 29 | if (typeof define === 'function' && define.amd) 30 | define('p5.dom', ['p5'], function (p5) { (factory(p5));}); 31 | else if (typeof exports === 'object') 32 | factory(require('../p5')); 33 | else 34 | factory(root['p5']); 35 | }(this, function (p5) { 36 | // ============================================================================= 37 | // p5 additions 38 | // ============================================================================= 39 | 40 | /** 41 | * Searches the page for an element with the given ID, class, or tag name (using the '#' or '.' 42 | * prefixes to specify an ID or class respectively, and none for a tag) and returns it as 43 | * a p5.Element. If a class or tag name is given with more than 1 element, 44 | * only the first element will be returned. 45 | * The DOM node itself can be accessed with .elt. 46 | * Returns null if none found. You can also specify a container to search within. 47 | * 48 | * @method select 49 | * @param {String} name id, class, or tag name of element to search for 50 | * @param {String} [container] id, p5.Element, or HTML element to search within 51 | * @return {Object/p5.Element|Null} p5.Element containing node found 52 | * @example 53 | *

54 | * function setup() { 55 | * createCanvas(100,100); 56 | * //translates canvas 50px down 57 | * select('canvas').position(100, 100); 58 | * } 59 | *
60 | *
61 | * // these are all valid calls to select() 62 | * var a = select('#moo'); 63 | * var b = select('#blah', '#myContainer'); 64 | * var c = select('#foo', b); 65 | * var d = document.getElementById('beep'); 66 | * var e = select('p', d); 67 | *
68 | * 69 | */ 70 | p5.prototype.select = function (e, p) { 71 | var res = null; 72 | var container = getContainer(p); 73 | if (e[0] === '.'){ 74 | e = e.slice(1); 75 | res = container.getElementsByClassName(e); 76 | if (res.length) { 77 | res = res[0]; 78 | } else { 79 | res = null; 80 | } 81 | }else if (e[0] === '#'){ 82 | e = e.slice(1); 83 | res = container.getElementById(e); 84 | }else { 85 | res = container.getElementsByTagName(e); 86 | if (res.length) { 87 | res = res[0]; 88 | } else { 89 | res = null; 90 | } 91 | } 92 | if (res) { 93 | return wrapElement(res); 94 | } else { 95 | return null; 96 | } 97 | }; 98 | 99 | /** 100 | * Searches the page for elements with the given class or tag name (using the '.' prefix 101 | * to specify a class and no prefix for a tag) and returns them as p5.Elements 102 | * in an array. 103 | * The DOM node itself can be accessed with .elt. 104 | * Returns an empty array if none found. 105 | * You can also specify a container to search within. 106 | * 107 | * @method selectAll 108 | * @param {String} name class or tag name of elements to search for 109 | * @param {String} [container] id, p5.Element, or HTML element to search within 110 | * @return {Array} Array of p5.Elements containing nodes found 111 | * @example 112 | *
113 | * function setup() { 114 | * createButton('btn'); 115 | * createButton('2nd btn'); 116 | * createButton('3rd btn'); 117 | * var buttons = selectAll('button'); 118 | * 119 | * for (var i = 0; i < buttons.length; i++){ 120 | * buttons[i].size(100,100); 121 | * } 122 | * } 123 | *
124 | *
125 | * // these are all valid calls to selectAll() 126 | * var a = selectAll('.moo'); 127 | * var b = selectAll('div'); 128 | * var c = selectAll('button', '#myContainer'); 129 | * var d = select('#container'); 130 | * var e = selectAll('p', d); 131 | * var f = document.getElementById('beep'); 132 | * var g = select('.blah', f); 133 | *
134 | * 135 | */ 136 | p5.prototype.selectAll = function (e, p) { 137 | var arr = []; 138 | var res; 139 | var container = getContainer(p); 140 | if (e[0] === '.'){ 141 | e = e.slice(1); 142 | res = container.getElementsByClassName(e); 143 | } else { 144 | res = container.getElementsByTagName(e); 145 | } 146 | if (res) { 147 | for (var j = 0; j < res.length; j++) { 148 | var obj = wrapElement(res[j]); 149 | arr.push(obj); 150 | } 151 | } 152 | return arr; 153 | }; 154 | 155 | /** 156 | * Helper function for select and selectAll 157 | */ 158 | function getContainer(p) { 159 | var container = document; 160 | if (typeof p === 'string' && p[0] === '#'){ 161 | p = p.slice(1); 162 | container = document.getElementById(p) || document; 163 | } else if (p instanceof p5.Element){ 164 | container = p.elt; 165 | } else if (p instanceof HTMLElement){ 166 | container = p; 167 | } 168 | return container; 169 | } 170 | 171 | /** 172 | * Helper function for getElement and getElements. 173 | */ 174 | function wrapElement(elt) { 175 | if(elt.tagName === "INPUT" && elt.type === "checkbox") { 176 | var converted = new p5.Element(elt); 177 | converted.checked = function(){ 178 | if (arguments.length === 0){ 179 | return this.elt.checked; 180 | } else if(arguments[0]) { 181 | this.elt.checked = true; 182 | } else { 183 | this.elt.checked = false; 184 | } 185 | return this; 186 | }; 187 | return converted; 188 | } else if (elt.tagName === "VIDEO" || elt.tagName === "AUDIO") { 189 | return new p5.MediaElement(elt); 190 | } else { 191 | return new p5.Element(elt); 192 | } 193 | } 194 | 195 | /** 196 | * Removes all elements created by p5, except any canvas / graphics 197 | * elements created by createCanvas or createGraphics. 198 | * Event handlers are removed, and element is removed from the DOM. 199 | * @method removeElements 200 | * @example 201 | *
202 | * function setup() { 203 | * createCanvas(100, 100); 204 | * createDiv('this is some text'); 205 | * createP('this is a paragraph'); 206 | * } 207 | * function mousePressed() { 208 | * removeElements(); // this will remove the div and p, not canvas 209 | * } 210 | *
211 | * 212 | */ 213 | p5.prototype.removeElements = function (e) { 214 | for (var i=0; i 242 | * var myDiv; 243 | * function setup() { 244 | * myDiv = createDiv('this is some text'); 245 | * } 246 | * 247 | */ 248 | 249 | /** 250 | * Creates a <p></p> element in the DOM with given inner HTML. Used 251 | * for paragraph length text. 252 | * Appends to the container node if one is specified, otherwise 253 | * appends to body. 254 | * 255 | * @method createP 256 | * @param {String} html inner HTML for element created 257 | * @return {Object/p5.Element} pointer to p5.Element holding created node 258 | * @example 259 | *
260 | * var myP; 261 | * function setup() { 262 | * myP = createP('this is some text'); 263 | * } 264 | *
265 | */ 266 | 267 | /** 268 | * Creates a <span></span> element in the DOM with given inner HTML. 269 | * Appends to the container node if one is specified, otherwise 270 | * appends to body. 271 | * 272 | * @method createSpan 273 | * @param {String} html inner HTML for element created 274 | * @return {Object/p5.Element} pointer to p5.Element holding created node 275 | * @example 276 | *
277 | * var mySpan; 278 | * function setup() { 279 | * mySpan = createSpan('this is some text'); 280 | * } 281 | *
282 | */ 283 | var tags = ['div', 'p', 'span']; 284 | tags.forEach(function(tag) { 285 | var method = 'create' + tag.charAt(0).toUpperCase() + tag.slice(1); 286 | p5.prototype[method] = function(html) { 287 | var elt = document.createElement(tag); 288 | elt.innerHTML = typeof html === undefined ? "" : html; 289 | return addElement(elt, this); 290 | } 291 | }); 292 | 293 | /** 294 | * Creates an <img /> element in the DOM with given src and 295 | * alternate text. 296 | * Appends to the container node if one is specified, otherwise 297 | * appends to body. 298 | * 299 | * @method createImg 300 | * @param {String} src src path or url for image 301 | * @param {String} [alt] alternate text to be used if image does not load 302 | * @param {Function} [successCallback] callback to be called once image data is loaded 303 | * @return {Object/p5.Element} pointer to p5.Element holding created node 304 | * @example 305 | *
306 | * var img; 307 | * function setup() { 308 | * img = createImg('http://p5js.org/img/asterisk-01.png'); 309 | * } 310 | *
311 | */ 312 | p5.prototype.createImg = function() { 313 | var elt = document.createElement('img'); 314 | var args = arguments; 315 | var self; 316 | var setAttrs = function(){ 317 | self.width = elt.offsetWidth; 318 | self.height = elt.offsetHeight; 319 | if (args.length > 1 && typeof args[1] === 'function'){ 320 | self.fn = args[1]; 321 | self.fn(); 322 | }else if (args.length > 1 && typeof args[2] === 'function'){ 323 | self.fn = args[2]; 324 | self.fn(); 325 | } 326 | }; 327 | elt.src = args[0]; 328 | if (args.length > 1 && typeof args[1] === 'string'){ 329 | elt.alt = args[1]; 330 | } 331 | elt.onload = function(){ 332 | setAttrs(); 333 | } 334 | self = addElement(elt, this); 335 | return self; 336 | }; 337 | 338 | /** 339 | * Creates an <a></a> element in the DOM for including a hyperlink. 340 | * Appends to the container node if one is specified, otherwise 341 | * appends to body. 342 | * 343 | * @method createA 344 | * @param {String} href url of page to link to 345 | * @param {String} html inner html of link element to display 346 | * @param {String} [target] target where new link should open, 347 | * could be _blank, _self, _parent, _top. 348 | * @return {Object/p5.Element} pointer to p5.Element holding created node 349 | * @example 350 | *
351 | * var myLink; 352 | * function setup() { 353 | * myLink = createA('http://p5js.org/', 'this is a link'); 354 | * } 355 | *
356 | */ 357 | p5.prototype.createA = function(href, html, target) { 358 | var elt = document.createElement('a'); 359 | elt.href = href; 360 | elt.innerHTML = html; 361 | if (target) elt.target = target; 362 | return addElement(elt, this); 363 | }; 364 | 365 | /** INPUT **/ 366 | 367 | 368 | /** 369 | * Creates a slider <input></input> element in the DOM. 370 | * Use .size() to set the display length of the slider. 371 | * Appends to the container node if one is specified, otherwise 372 | * appends to body. 373 | * 374 | * @method createSlider 375 | * @param {Number} min minimum value of the slider 376 | * @param {Number} max maximum value of the slider 377 | * @param {Number} [value] default value of the slider 378 | * @param {Number} [step] step size for each tick of the slider (if step is set to 0, the slider will move continuously from the minimum to the maximum value) 379 | * @return {Object/p5.Element} pointer to p5.Element holding created node 380 | * @example 381 | *
382 | * var slider; 383 | * function setup() { 384 | * slider = createSlider(0, 255, 100); 385 | * slider.position(10, 10); 386 | * slider.style('width', '80px'); 387 | * } 388 | * 389 | * function draw() { 390 | * var val = slider.value(); 391 | * background(val); 392 | * } 393 | *
394 | * 395 | *
396 | * var slider; 397 | * function setup() { 398 | * colorMode(HSB); 399 | * slider = createSlider(0, 360, 60, 40); 400 | * slider.position(10, 10); 401 | * slider.style('width', '80px'); 402 | * } 403 | * 404 | * function draw() { 405 | * var val = slider.value(); 406 | * background(val, 100, 100, 1); 407 | * } 408 | *
409 | */ 410 | p5.prototype.createSlider = function(min, max, value, step) { 411 | var elt = document.createElement('input'); 412 | elt.type = 'range'; 413 | elt.min = min; 414 | elt.max = max; 415 | if (step === 0) { 416 | elt.step = .000000000000000001; // smallest valid step 417 | } else if (step) { 418 | elt.step = step; 419 | } 420 | if (typeof(value) === "number") elt.value = value; 421 | return addElement(elt, this); 422 | }; 423 | 424 | /** 425 | * Creates a <button></button> element in the DOM. 426 | * Use .size() to set the display size of the button. 427 | * Use .mousePressed() to specify behavior on press. 428 | * Appends to the container node if one is specified, otherwise 429 | * appends to body. 430 | * 431 | * @method createButton 432 | * @param {String} label label displayed on the button 433 | * @param {String} [value] value of the button 434 | * @return {Object/p5.Element} pointer to p5.Element holding created node 435 | * @example 436 | *
437 | * var button; 438 | * function setup() { 439 | * createCanvas(100, 100); 440 | * background(0); 441 | * button = createButton('click me'); 442 | * button.position(19, 19); 443 | * button.mousePressed(changeBG); 444 | * } 445 | * 446 | * function changeBG() { 447 | * var val = random(255); 448 | * background(val); 449 | * } 450 | *
451 | */ 452 | p5.prototype.createButton = function(label, value) { 453 | var elt = document.createElement('button'); 454 | elt.innerHTML = label; 455 | elt.value = value; 456 | if (value) elt.value = value; 457 | return addElement(elt, this); 458 | }; 459 | 460 | /** 461 | * Creates a checkbox <input></input> element in the DOM. 462 | * Calling .checked() on a checkbox returns if it is checked or not 463 | * 464 | * @method createCheckbox 465 | * @param {String} [label] label displayed after checkbox 466 | * @param {boolean} [value] value of the checkbox; checked is true, unchecked is false.Unchecked if no value given 467 | * @return {Object/p5.Element} pointer to p5.Element holding created node 468 | * @example 469 | *
470 | * var checkbox; 471 | * 472 | * function setup() { 473 | * checkbox = createCheckbox('label', false); 474 | * checkbox.changed(myCheckedEvent); 475 | * } 476 | * 477 | * function myCheckedEvent() { 478 | * if (this.checked()) { 479 | * console.log("Checking!"); 480 | * } else { 481 | * console.log("Unchecking!"); 482 | * } 483 | * } 484 | *
485 | */ 486 | p5.prototype.createCheckbox = function() { 487 | var elt = document.createElement('div'); 488 | var checkbox = document.createElement('input'); 489 | checkbox.type = 'checkbox'; 490 | elt.appendChild(checkbox); 491 | //checkbox must be wrapped in p5.Element before label so that label appears after 492 | var self = addElement(elt, this); 493 | self.checked = function(){ 494 | var cb = self.elt.getElementsByTagName('input')[0]; 495 | if (cb) { 496 | if (arguments.length === 0){ 497 | return cb.checked; 498 | }else if(arguments[0]){ 499 | cb.checked = true; 500 | }else{ 501 | cb.checked = false; 502 | } 503 | } 504 | return self; 505 | }; 506 | this.value = function(val){ 507 | self.value = val; 508 | return this; 509 | }; 510 | if (arguments[0]){ 511 | var ran = Math.random().toString(36).slice(2); 512 | var label = document.createElement('label'); 513 | checkbox.setAttribute('id', ran); 514 | label.htmlFor = ran; 515 | self.value(arguments[0]); 516 | label.appendChild(document.createTextNode(arguments[0])); 517 | elt.appendChild(label); 518 | } 519 | if (arguments[1]){ 520 | checkbox.checked = true; 521 | } 522 | return self; 523 | }; 524 | 525 | /** 526 | * Creates a dropdown menu <select></select> element in the DOM. 527 | * @method createSelect 528 | * @param {boolean} [multiple] [true if dropdown should support multiple selections] 529 | * @return {Object/p5.Element} pointer to p5.Element holding created node 530 | * @example 531 | *
532 | * var sel; 533 | * 534 | * function setup() { 535 | * textAlign(CENTER); 536 | * background(200); 537 | * sel = createSelect(); 538 | * sel.position(10, 10); 539 | * sel.option('pear'); 540 | * sel.option('kiwi'); 541 | * sel.option('grape'); 542 | * sel.changed(mySelectEvent); 543 | * } 544 | * 545 | * function mySelectEvent() { 546 | * var item = sel.value(); 547 | * background(200); 548 | * text("it's a "+item+"!", 50, 50); 549 | * } 550 | *
551 | */ 552 | p5.prototype.createSelect = function(mult) { 553 | var elt = document.createElement('select'); 554 | if (mult){ 555 | elt.setAttribute('multiple', 'true'); 556 | } 557 | var self = addElement(elt, this); 558 | self.option = function(name, value){ 559 | var opt = document.createElement('option'); 560 | opt.innerHTML = name; 561 | if (arguments.length > 1) 562 | opt.value = value; 563 | else 564 | opt.value = name; 565 | elt.appendChild(opt); 566 | }; 567 | self.selected = function(value){ 568 | var arr = []; 569 | if (arguments.length > 0){ 570 | for (var i = 0; i < this.elt.length; i++){ 571 | if (value.toString() === this.elt[i].value){ 572 | this.elt.selectedIndex = i; 573 | } 574 | } 575 | return this; 576 | }else{ 577 | if (mult){ 578 | for (var i = 0; i < this.elt.selectedOptions.length; i++){ 579 | arr.push(this.elt.selectedOptions[i].value); 580 | } 581 | return arr; 582 | }else{ 583 | return this.elt.value; 584 | } 585 | } 586 | }; 587 | return self; 588 | }; 589 | 590 | /** 591 | * Creates a radio button <input></input> element in the DOM. 592 | * The .option() method can be used to set options for the radio after it is 593 | * created. The .value() method will return the currently selected option. 594 | * 595 | * @method createRadio 596 | * @param {String} [divId] the id and name of the created div and input field respectively 597 | * @return {Object/p5.Element} pointer to p5.Element holding created node 598 | * @example 599 | *
600 | * var radio; 601 | * 602 | * function setup() { 603 | * radio = createRadio(); 604 | * radio.option("black"); 605 | * radio.option("white"); 606 | * radio.option("gray"); 607 | * radio.style('width', '60px'); 608 | * textAlign(CENTER); 609 | * fill(255, 0, 0); 610 | * } 611 | * 612 | * function draw() { 613 | * var val = radio.value(); 614 | * background(val); 615 | * text(val, width/2, height/2); 616 | * } 617 | *
618 | *
619 | * var radio; 620 | * 621 | * function setup() { 622 | * radio = createRadio(); 623 | * radio.option('apple', 1); 624 | * radio.option('bread', 2); 625 | * radio.option('juice', 3); 626 | * radio.style('width', '60px'); 627 | * textAlign(CENTER); 628 | * } 629 | * 630 | * function draw() { 631 | * background(200); 632 | * var val = radio.value(); 633 | * if (val) { 634 | * text('item cost is $'+val, width/2, height/2); 635 | * } 636 | * } 637 | *
638 | */ 639 | p5.prototype.createRadio = function() { 640 | var radios = document.querySelectorAll("input[type=radio]"); 641 | var count = 0; 642 | if(radios.length > 1){ 643 | var length = radios.length; 644 | var prev=radios[0].name; 645 | var current = radios[1].name; 646 | count = 1; 647 | for(var i = 1; i < length; i++) { 648 | current = radios[i].name; 649 | if(prev != current){ 650 | count++; 651 | } 652 | prev = current; 653 | } 654 | } 655 | else if (radios.length == 1){ 656 | count = 1; 657 | } 658 | var elt = document.createElement('div'); 659 | var self = addElement(elt, this); 660 | var times = -1; 661 | self.option = function(name, value){ 662 | var opt = document.createElement('input'); 663 | opt.type = 'radio'; 664 | opt.innerHTML = name; 665 | if (arguments.length > 1) 666 | opt.value = value; 667 | else 668 | opt.value = name; 669 | opt.setAttribute('name',"defaultradio"+count); 670 | elt.appendChild(opt); 671 | if (name){ 672 | times++; 673 | var ran = Math.random().toString(36).slice(2); 674 | var label = document.createElement('label'); 675 | opt.setAttribute('id', "defaultradio"+count+"-"+times); 676 | label.htmlFor = "defaultradio"+count+"-"+times; 677 | label.appendChild(document.createTextNode(name)); 678 | elt.appendChild(label); 679 | } 680 | return opt; 681 | }; 682 | self.selected = function(){ 683 | var length = this.elt.childNodes.length; 684 | if(arguments.length == 1) { 685 | for (var i = 0; i < length; i+=2){ 686 | if(this.elt.childNodes[i].value == arguments[0]) 687 | this.elt.childNodes[i].checked = true; 688 | } 689 | return this; 690 | } else { 691 | for (var i = 0; i < length; i+=2){ 692 | if(this.elt.childNodes[i].checked == true) 693 | return this.elt.childNodes[i].value; 694 | } 695 | } 696 | }; 697 | self.value = function(){ 698 | var length = this.elt.childNodes.length; 699 | if(arguments.length == 1) { 700 | for (var i = 0; i < length; i+=2){ 701 | if(this.elt.childNodes[i].value == arguments[0]) 702 | this.elt.childNodes[i].checked = true; 703 | } 704 | return this; 705 | } else { 706 | for (var i = 0; i < length; i+=2){ 707 | if(this.elt.childNodes[i].checked == true) 708 | return this.elt.childNodes[i].value; 709 | } 710 | return ""; 711 | } 712 | }; 713 | return self 714 | }; 715 | 716 | /** 717 | * Creates an <input></input> element in the DOM for text input. 718 | * Use .size() to set the display length of the box. 719 | * Appends to the container node if one is specified, otherwise 720 | * appends to body. 721 | * 722 | * @method createInput 723 | * @param {Number} [value] default value of the input box 724 | * @return {Object/p5.Element} pointer to p5.Element holding created node 725 | * @example 726 | *
727 | * function setup(){ 728 | * var inp = createInput(''); 729 | * inp.input(myInputEvent); 730 | * } 731 | * 732 | * function myInputEvent(){ 733 | * console.log('you are typing: ', this.value()); 734 | * } 735 | * 736 | *
737 | */ 738 | p5.prototype.createInput = function(value) { 739 | var elt = document.createElement('input'); 740 | elt.type = 'text'; 741 | if (value) elt.value = value; 742 | return addElement(elt, this); 743 | }; 744 | 745 | /** 746 | * Creates an <input></input> element in the DOM of type 'file'. 747 | * This allows users to select local files for use in a sketch. 748 | * 749 | * @method createFileInput 750 | * @param {Function} [callback] callback function for when a file loaded 751 | * @param {String} [multiple] optional to allow multiple files selected 752 | * @return {Object/p5.Element} pointer to p5.Element holding created DOM element 753 | */ 754 | p5.prototype.createFileInput = function(callback, multiple) { 755 | 756 | // Is the file stuff supported? 757 | if (window.File && window.FileReader && window.FileList && window.Blob) { 758 | // Yup, we're ok and make an input file selector 759 | var elt = document.createElement('input'); 760 | elt.type = 'file'; 761 | 762 | // If we get a second argument that evaluates to true 763 | // then we are looking for multiple files 764 | if (multiple) { 765 | // Anything gets the job done 766 | elt.multiple = 'multiple'; 767 | } 768 | 769 | // Function to handle when a file is selected 770 | // We're simplifying life and assuming that we always 771 | // want to load every selected file 772 | function handleFileSelect(evt) { 773 | // These are the files 774 | var files = evt.target.files; 775 | // Load each one and trigger a callback 776 | for (var i = 0; i < files.length; i++) { 777 | var f = files[i]; 778 | var reader = new FileReader(); 779 | function makeLoader(theFile) { 780 | // Making a p5.File object 781 | var p5file = new p5.File(theFile); 782 | return function(e) { 783 | p5file.data = e.target.result; 784 | callback(p5file); 785 | }; 786 | }; 787 | reader.onload = makeLoader(f); 788 | 789 | // Text or data? 790 | // This should likely be improved 791 | if (f.type.indexOf('text') > -1) { 792 | reader.readAsText(f); 793 | } else { 794 | reader.readAsDataURL(f); 795 | } 796 | } 797 | } 798 | 799 | // Now let's handle when a file was selected 800 | elt.addEventListener('change', handleFileSelect, false); 801 | return addElement(elt, this); 802 | } else { 803 | console.log('The File APIs are not fully supported in this browser. Cannot create element.'); 804 | } 805 | }; 806 | 807 | 808 | /** VIDEO STUFF **/ 809 | 810 | function createMedia(pInst, type, src, callback) { 811 | var elt = document.createElement(type); 812 | 813 | // allow src to be empty 814 | var src = src || ''; 815 | if (typeof src === 'string') { 816 | src = [src]; 817 | } 818 | for (var i=0; i