Add Paperoids example.

This commit is contained in:
Jonathan Puckey 2011-08-11 20:24:04 +02:00
parent e82a838085
commit 6455bf5e98

View file

@ -0,0 +1,525 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<title>Paperoids</title>
<link rel="stylesheet" href="paperoids.css">
<script type="text/javascript" src="js/Stats.js"></script>
<script type="text/javascript" src="js/paper.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();
document.getElementById('statholder').style.display = 'block';
}
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 statHolder = document.getElementById('statholder');
statHolder.style.display = (statHolder.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();
Game.updateStats();
}
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
updateStats: new function() {
var stats = new Stats();
// Align top-left
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.getElementById('statholder').appendChild( stats.domElement );
document.getElementById('statholder').style.display = "none";
return stats.update;
}
};
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 Symbol(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 crash = -1;
// 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)) {
crash = i;
}
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 (crash > -1) {
var hit = false;
var pos = Rocks.children[crash].position;
var shipSegments = this.item.firstChild.segments;
var index = Rocks.children[crash].typeIndex;
var tempRock = Rocks.shapes[index].clone();
tempRock.transform(Rocks.children[crash].matrix);
tempRock.remove();
var rockSegments = tempRock.segments;
// This code should be able to be simplified alot very soon,
// when Paper.js implements Path#intersects(path);
for (var i = 0; i < rockSegments.length - 1; i++) {
var rockLine = new Line(rockSegments[i].point,
rockSegments[i + 1].point, false);
var shipLine = new Line(shipSegments[0].point,
shipSegments[1].point, false);
var lineHit = shipLine.intersect(rockLine);
if (lineHit) {
hit = true;
break;
}
}
if (hit) Lives.remove();
}
}
};
};
var Bullets = new function() {
var group = new Group();
var children = group.children;
return {
fire: function(position, angle) {
if (children.length > 4) return;
var bullet = new Path.Circle(position, 0.5);
bullet.fillColor = 'white';
bullet.strokeWidth = 0;
bullet.data = {
vector: new Point({
angle: angle,
length: 10
}),
timeToDie: 58
};
bullet.position += bullet.data.vector;
group.addChild(bullet);
},
move: function() {
// check for bullet hit
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;
for (var r = 0; r < Rocks.children.length; r++) {
var rock = Rocks.children[r];
if (rock.bounds.contains(bullet.position) ) {
Score.update(rock.type);
Rocks.explode(rock);
if (rock.type < Rocks.TYPE_SMALL ) {
for (var i = 0; i < 2; i++) {
Rocks.add(1, rock.type + 4, rock.position);
}
}
rock.remove();
bullet.remove();
}
}
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 Symbol(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.typeIndex = randomRock;
rock.type = 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 = shapes[rock.typeIndex].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>
</head>
<body style="background:black">
<canvas style="position:absolute" id="canvas" resize></canvas>
<div style="position:absolute;right:51px;color:#ff0000;" id="statholder">FPS</div>
<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="index.html">Play again</a>?</div>
</body>
</html>