UNPKG

paper

Version:

The Swiss Army Knife of Vector Graphics Scripting

570 lines (514 loc) 18.8 kB
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"> <title>Paperoids</title> <script type="text/javascript" src="../../dist/paper-full.js"></script> <script type="text/paperscript" canvas="canvas"> // // // Paperoids - an Asteroids clone for paper.js // // Sorry nerds, no enemy ships or sound just yet. // // Version: 1.2.1 // Author: David Hirmes (@hirmes) // Date: 2011-08-09 // // Revision History: // 1.0 - 2011-07-13 - Initial release // 1.1 - 2011-07-13 - Optimizations by Jonathan Puckey (@jonathanpuckey) // 1.2 - 2011-07-16 - Refactored with closures (@jonathanpuckey) // 1.2.1- 2011-08-09 - bug fixes // var presets = { speed: 0.2, maxRockSpeed: 4.5, rockCount: 6, lives: 3, freeShipScore: 10000, freeShipIncrement: 10000 }; function initialize() { Rocks.add(presets.rockCount); Score.update(); Lives.initialize(); } function onKeyUp(event) { if (event.key == 'space') { Ship.moveTo(Point.random() * view.size); Ship.stop(); } if (event.key == 'z') { Ship.fire(); } // Show stats: if (event.key == 'f') { var stats = document.getElementById('stats'); if (stats) { stats.style.display = (stats.style.display == 'block') ? 'none' : 'block'; } } } function onFrame() { Bullets.move(); Rocks.iterateExplosions(); Ship.checkCollisions(); if (Key.isDown('left')) { Ship.turnLeft(); } if (Key.isDown('right')) { Ship.turnRight(); } if (Key.isDown('up')) { Ship.thrust(); } else { Ship.coast(); } Ship.move(); } project.currentStyle.strokeColor = 'white'; var Game = { roundDelay: false, over: function() { document.getElementById('gameover').style.display = 'block'; }, newRound: function() { Game.roundDelay = false; Rocks.add(presets.rockCount); }, // Stats.js by Mr. Doob - https://github.com/mrdoob/stats.js }; var assets = { destroyedShip: new function() { var group = new Group( new Path([-10, -8], [10, 0]), new Path([10, 0], [-10, 8]), new Path([-8, 4], [-8, -4]) ); group.visible = false; return group; }, explosion: new function() { var explosionPath = new Path.Circle(new Point(), 1); explosionPath.fillColor = 'white'; explosionPath.strokeColor = null; return new SymbolDefinition(explosionPath); } }; var Ship = new function() { var path = new Path([-10, -8], [10, 0], [-10, 8], [-8, 4], [-8, -4]); path.closed = true; var thrust = new Path([-8, -4], [-14, 0], [-8, 4]); var group = new Group(path, thrust); group.position = view.bounds.center; return { item: group, angle: 0, vector: new Point({ angle: 0.2, length: 1 }), turnLeft: function() { group.rotate(-3); this.angle -= 3; }, turnRight: function() { group.rotate(3); this.angle += 3; }, thrust: function() { thrust.visible = true; this.vector += new Point({ angle: this.angle, length: presets.speed }); if (this.vector.length > 8) { this.vector.length = 8; } }, stop: function() { this.vector.length = 0; }, fire: function() { if (!this.dying) Bullets.fire(this.item.position, this.angle); }, coast: function() { thrust.visible = false; this.vector *= .992; }, move: function() { group.position += this.vector; keepInView(group); }, moveTo: function(position) { group.position = position; keepInView(group); }, destroy: function() { this.destroyedShip = assets.destroyedShip.clone(); this.destroyedShip.position = this.item.position; this.destroyedShip.visible = true; this.item.visible = false; this.stop(); this.item.position = view.center; this.dying = true; }, destroyed: function() { this.item.visible = true; this.stop(); this.item.position = view.center; this.dying = false; this.destroyedShip.visible = false; }, checkCollisions: function() { var crashRock; // move rocks and do a hit-test // between bounding rect of rocks and ship for (var i = 0; i < Rocks.children.length; i++) { var rock = Rocks.children[i]; rock.position += rock.vector; if (rock.bounds.intersects(this.item.bounds)) crashRock = rock; keepInView(rock); } if (this.dying) { var children = this.destroyedShip.children; children[0].position.x++; children[1].position.x--; children[2].position.x--; children[2].position.y++; children[0].rotate(1); children[1].rotate(-1); children[2].rotate(1); this.destroyedShip.opacity *= 0.98; // don't update anything else if the ship is already dead. return; } // if bounding rect collision, do a line intersection test if (crashRock) { var tempRock = crashRock.symbol.definition.clone(); tempRock.transform(crashRock.matrix); tempRock.remove(); var intersections = this.item.firstChild.getIntersections(tempRock); if (intersections.length > 0) Lives.remove(); } } }; }; var Bullets = new function() { var group = new Group(); var children = group.children; function checkHits(bullet) { for (var r = 0; r < Rocks.children.length; r++) { var rock = Rocks.children[r]; if (rock.bounds.contains(bullet.position) ) { Score.update(rock.shapeType); Rocks.explode(rock); if (rock.shapeType < Rocks.TYPE_SMALL ) { for (var j = 0; j < 2; j++) { Rocks.add(1, rock.shapeType + 4, rock.position); } } rock.remove(); bullet.remove(); } } } return { fire: function(position, angle) { // We can only fire 5 bullets at a time: if (children.length == 5) return; var vector = new Point({ angle: angle, length: 10 }); var bullet = new Path.Circle({ center: position + vector, radius: 0.5, parent: group, fillColor: 'white', strokeWidth: 'white', strokeWidth: 0, data: { vector: vector, timeToDie: 58 } }); }, move: function() { for (var i = 0; i < children.length; i++) { var bullet = children[i]; bullet.data.timeToDie--; if (bullet.data.timeToDie < 1) { bullet.remove(); } else { bullet.position += bullet.data.vector; checkHits(bullet); keepInView(bullet); } } } }; }; var Rocks = new function() { var group = new Group(); var shapes = [ new Path( [-23, -40.5], [0, -30.5], [24, -40.5], [45, -21.5], [25, -12.5], [46, 9.5], [22, 38.5], [-10, 30.5], [-22, 40.5], [-46, 18.5], [-33, 0.5], [-44, -21.5], [-23, -40.5]), new Path( [-45, -9.5], [-12, -40.5], [24, -40.5], [46, -11.5], [45, 10.5], [24, 40.5], [0, 40.5], [0, 10.5], [-23, 38.5], [-46, 9.5], [-25, 0.5], [-45, -9.5]), new Path([-21.5, -39.5], [11.5, -39.5], [45.5, -20.5], [45.5, -8.5], [9.5, 0.5], [44.5, 21.5], [22.5, 39.5], [9.5, 31.5], [-20.5, 39.5], [-45.5, 10.5], [-45.5, -20.5], [-11.5, -21.5], [-21.5, -39.5]), new Path( [-22.5, -40.5], [-0.5, -19.5], [23.5, -39.5], [44.5, -21.5], [33.5, 0.5], [46.5, 19.5], [13.5, 40.5], [-22.5, 39.5], [-46.5, 18.5], [-46.5, -18.5], [-22.5, -40.5]) ]; // medium rocks for (var i = 4; i < 8; i++) { shapes[i] = shapes[i - 4].clone(); shapes[i].scale(0.5); } // small rocks for (var i = 8; i < 12; i++) { shapes[i] = shapes[i - 4].clone(); shapes[i].scale(0.4); } var rockSymbols = []; for (var i = 0; i < shapes.length; i++) { rockSymbols[i] = new SymbolDefinition(shapes[i]); } var explosions = new Group(); return { shapes: shapes, children: group.children, make: function(type, pos) { var randomRock = type + Math.floor(4 * Math.random()); var rock = rockSymbols[randomRock].place(); rock.position = pos ? pos : Point.random() * view.size; rock.vector = new Point({ angle: 360 * Math.random(), length: presets.maxRockSpeed * Math.random() + 0.1 }); rock.shapeType = type; return rock; }, add: function(amount, type, position) { for (var i = 0; i < amount; i++) { var rock = this.make(type || this.TYPE_BIG, position); group.addChild(rock); } }, explode: function(rock) { var boomRock = rock.symbol.definition.clone(); boomRock.position = rock.position; for (var i = 0; i < boomRock.segments.length; i++) { var segmentPoint = boomRock.segments[i].point; var placed = assets.explosion.place(segmentPoint); placed.vector = (placed.position - rock.position) * 0.1; explosions.addChild(placed); } boomRock.remove(); }, iterateExplosions: function() { for (var i = 0; i < explosions.children.length; i++) { var explosion = explosions.children[i]; explosion.vector.length *= .7; explosion.position += explosion.vector; explosion.opacity = explosion.vector.length; if (explosion.vector.length < 0.05 ) { explosion.remove(); // if no more rocks, wait a second and start a new round if (this.children.length < 1 && !Game.roundDelay) { Game.roundDelay = true; presets.rockCount += 2; setTimeout(Game.newRound, 1000); } } } }, TYPE_BIG: 0, TYPE_MEDIUM: 4, TYPE_SMALL: 8 }; }; var Score = new function() { var numberGroup = new Group( new Path([0, 0], [20, 0], [20, 27], [0, 27], [0, 0]), new Path([10, 0], [10, 27]), new Path([0, 0], [20, 0], [20, 14], [0, 14], [0, 27], [20, 27]), new Path([0, 0], [20, 0], [20, 14], [0, 14], [20, 14], [20, 27], [0, 27]), new Path([0, 0], [0, 14], [20, 14], [20, 0], [20, 27]), new Path([20, 0], [0, 0], [0, 14], [20, 14], [20, 27], [0, 27]), new Path([20, 0], [0, 0], [0, 27], [20, 27], [20, 14], [0, 14]), new Path([0, 0], [20, 0], [0, 27]), new Path([0, 0], [20, 0], [20, 27], [0, 27], [0, 0], [0, 14], [20, 14]), new Path([20, 14], [0, 14], [0, 0], [20, 0], [20, 27]) ); numberGroup.visible = false; var scoreDisplay = new Group(); var score = 0; return { update: function(type) { if (type == Rocks.TYPE_BIG) score += 20; if (type == Rocks.TYPE_MEDIUM) score += 50; if (type == Rocks.TYPE_SMALL) score += 100; if (score >= presets.freeShipScore) { Lives.add(1); presets.freeShipScore += presets.freeShipIncrement; } scoreDisplay.removeChildren(); var scoreString = score + ''; for (var i = 0; i < scoreString.length; i++) { var n = parseInt(scoreString[i], 10); scoreDisplay.addChild(numberGroup.children[n].clone()); scoreDisplay.lastChild.position = [22 + i * 24, 22]; } } }; }; var Lives = new function() { var currentLives; var shipPath = Ship.item.firstChild.clone(); project.activeLayer.addChild(shipPath); shipPath.visible = false; Ship.visible = false; var group = new Group(); return { initialize: function() { currentLives = presets.lives; this.display(); }, add: function() { currentLives++; this.display(); }, remove: function() { currentLives--; this.display(); Ship.destroy(); setTimeout(function() { if (currentLives == 0) { Game.over(); } else { Ship.destroyed(); } }, 1200); }, display: function() { group.removeChildren(); for (var i = 0;i < currentLives - 1; i++) { var copy = shipPath.clone(); copy.rotate(-90); copy.visible = true; group.addChild(copy); copy.position = [22+ i * copy.bounds.width, 53]; } } }; }; initialize(); // Stop left and right keyboard events from propagating. function onKeyDown(event) { if (event.key == 'left' || event.key == 'right') { return false; } } function keepInView(item) { var position = item.position; var itemBounds = item.bounds; var bounds = view.bounds; if (itemBounds.left > bounds.width) { position.x = -item.bounds.width; } if (position.x < -itemBounds.width) { position.x = bounds.width; } if (itemBounds.top > view.size.height) { position.y = -itemBounds.height; } if (position.y < -itemBounds.height) { position.y = bounds.height + itemBounds.height / 2; } } </script> <style type="text/css"> html, body { margin: 0; overflow: hidden; height: 100%; } canvas { width: 100%; height: 100%; } body { background: #000; font-family:Helvetica,Arial; } .footer { position:absolute; bottom:0; color:#ff0000; background-color: rgba(60,60,60,0.8); font-size:0.75em; padding:0.5em; color:#ddd; width: 100% } .footer a { color: #fff; font-weight: bold; text-decoration: none; border-bottom: 1px solid #555; } .footer b { background-color: #660000; padding-left: 0.25em; padding-right: 0.25em; } .gameover { display: none; position: absolute; left: 40%; top: 40%; color: #fff; background-color: rgba(60,60,60,0.8); padding: 32px; -moz-border-radius: 12px; -webkit-border-radius: 12px; border-radius: 12px; -moz-background-clip: padding; -webkit-background-clip: padding-box; background-clip: padding-box; } .gameover a { color: #fff; font-weight: bold; } #stats { position: absolute; left: auto !important; right: 0px; } </style> </head> <body> <canvas id="canvas" resize stats></canvas> <div id="footer" class="footer"><a href=#">Paperoids</a>. To Play: <b>&#8592;</b> and <b>&#8594;</b> to rotate. <b>&#8593;</b> for thrust. <b>Z</b> to fire. <b>F</b> to show FPS. Follow @<a href="http://twitter.com/#/hirmes">hirmes</a> for updates. Made with the amazing <a href="http://paperjs.org">Paper.js</a></div> <div id="gameover" class="gameover">Game Over. <a href="Paperoids.html">Play again</a>?</div> </body> </html>