Last active
August 29, 2015 14:01
-
-
Save mgiuffrida/f44246e7b569c4d59edf to your computer and use it in GitHub Desktop.
magnetgolf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<script src="magnetgolf.js"></script> | |
</head> | |
<body> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// free music: "The Waves Call Her Name" - Sycamore Drive | |
// http://alexnisnevich.github.io/untrusted/ | |
var FPS = 1; | |
var KEY_CODES = { | |
LEFT: 37, | |
UP: 38, | |
RIGHT: 39, | |
DOWN: 40, | |
}; | |
var keyStatus = {}; | |
var extendObj = function(childObj, parentObj) { | |
var tmpObj = function () {}; | |
tmpObj.prototype = parentObj.prototype; | |
childObj.prototype = new tmpObj(); | |
childObj.prototype.constructor = childObj; | |
}; | |
var check = 1; | |
function err(msg) { | |
if (check) { | |
throw msg; | |
} | |
} | |
function sign(num) { | |
return num ? num < 0 ? -1 : 1 : 0; | |
} | |
var world; | |
// POD objects | |
function pt(x, y) { | |
return {x: x, | |
y: y}; | |
} | |
var asMovable = function() { | |
this.getX = function() { | |
return this.pos.x; | |
}; | |
this.getY = function() { | |
return this.pos.y; | |
}; | |
this.getPos = function() { | |
// todo: return copy of pos | |
} | |
this.moveTo = function(x2, y2) { | |
this.pos.x = x2; | |
this.pos.y = y2; | |
}; | |
return this; | |
}; | |
var asItem = function() { | |
this.getWidth = function() { | |
return this.width; | |
}; | |
this.getHeight = function() { | |
return this.height; | |
}; | |
this.setSize = function(w, h) { | |
this.width = w; | |
this.height = h; | |
}; | |
return this; | |
}; | |
// how to navigate places: A* search | |
// clicking a place takes you to the entrance to that place | |
// and you face the normal of the entrance | |
// or use arrow keys | |
var Place = function(name) { | |
this.name = name; | |
this.items = []; | |
this.entrance = pt(0,0); | |
// Normal of entrance, in degrees. | |
this.normal = 0; | |
this.pos = pt(0,0); | |
this.width = 0; | |
this.height = 0; | |
} | |
Place.prototype = { | |
getContainer: function() { | |
return this.container; | |
}, | |
setContainer: function(place) { | |
this.container = place; | |
}, | |
getItems: function() { | |
return this.items.slice(); | |
}, | |
addItem: function(item) { | |
if (this.items.indexOf(item) > -1) { | |
return err("Error: item " + item.name + "already in place " + this.name); | |
} | |
this.items.push(item); | |
item.parent = this; | |
}, | |
getParent: function() { | |
return this.parent; | |
}, | |
setEntrance: function(x, y) { | |
this.entrance.x = x; | |
this.entrance.y = y; | |
}, | |
toString: function() { | |
return "Place " + this.name; | |
}, | |
draw: function() { | |
var ctx = canvas.getContext('2d'); // todo | |
ctx.clearRect(this.getX(), this.getY(), this.getWidth(), this.getHeight()); | |
ctx.strokeRect(this.getX(), this.getY(), this.getWidth(), this.getHeight()); | |
if (this.items) { | |
for (var i = 0; i < this.items.length; i++) | |
this.items[i].draw(); | |
} | |
}, | |
tick: function(delta) { | |
if (this.items) { | |
for (var i = 0; i < this.items.length; i++) | |
this.items[i].tick(delta); | |
} | |
}, | |
}; | |
asItem.call(asMovable.call(Place.prototype)); | |
// Magnetic ball. | |
var Ball = function() { | |
this.pos = pt(0,0); | |
this.width = 10; | |
this.height = 10; | |
this.speed = 350; // px/sec | |
this.vX = 0; | |
this.vY = 0; | |
this.deltaVelocity = 36; // px/sec/sec | |
this.deltaVelocityDiagonal = Math.sqrt(2*this.deltaVelocity*this.deltaVelocity); | |
this.friction = 24; // px/sec/sec | |
} | |
Ball.prototype = { | |
getContainer: function() { | |
return this.container; | |
}, | |
setContainer: function(place) { | |
this.container = place; | |
}, | |
getParent: function() { | |
return this.parent; | |
}, | |
setPolarity: function(polarity) { | |
this.polarity = polarity; | |
}, | |
toString: function() { | |
return "Ball: " + (this.polarity ? "North" : "South"); | |
}, | |
draw: function() { | |
var ctx = canvas.getContext('2d'); // todo | |
ctx.fillStyle = 'black'; | |
// todo - performance ctx.clearRect(this.getX(), this.getY(), this.getWidth(), this.getHeight()); | |
// ctx.translate(0.5, 0.5); | |
ctx.beginPath(); | |
ctx.arc(this.getX(), this.getY(), this.getWidth(), 0, 360) | |
ctx.fill(); // todo: shading | |
}, | |
tick: function(delta) { | |
// todo: this is wrong. the total absolute speed should be capped (proportionally). | |
if (Math.abs(this.vX) > this.speed) { | |
this.vX = this.speed * sign(this.vX); | |
} | |
if (Math.abs(this.vY) > this.speed) { | |
this.vY = this.speed * sign(this.vY); | |
} | |
for (var i = 0; i < world.items.length; i++) { | |
if (world.items[i] != this) { | |
diffX = this.getX() - world.items[i].getX(); | |
diffY = this.getY() - world.items[i].getY(); | |
var mag = Math.sqrt(Math.pow(diffX, 2) + Math.pow(diffY, 2)); | |
var mag3 = Math.pow(mag,3); | |
if (diffX != NaN && diffY != NaN) { | |
this.vX += diffX/mag3*100000; | |
this.vY += diffY/mag3*100000; | |
} | |
} | |
} | |
/* | |
sqrt(this.vX2**2 + this.vY2**2) = | |
sqrt(this.vX**2 + this.vY**2) - friction | |
this.vX2**2 + this.vY2**2 = | |
this.vX**2 + this.vY**2 + friction**2 | |
-2 * sqrt(this.vX**2 + this.vY**2) * friction | |
this.vY2**2 = | |
this.vX**2 + this.vY**2 + friction**2 | |
-2 * sqrt(this.vX**2 + this.vY**2) * friction | |
- this.vX2**2 | |
this.vX / this.vY = this.vX2 / this.vY2 | |
this.vY2 = (this.vX2 / this.vX * this.vY) | |
sqrt(this.vX**2 + this.vY**2 + friction**2 | |
-2 * sqrt(this.vX**2 + this.vY**2) * friction | |
- this.vX2**2 ) | |
= (this.vX2 / this.vX * this.vY) | |
this .. solve for vX2 (both sides) | |
this.vX**2 + this.vY**2 + friction**2 | |
-2 * sqrt(this.vX**2 + this.vY**2) * friction | |
- this.vX2**2 | |
= (this.vX2 / this.vX * this.vY) ** 2 | |
this.vX2 = sqrt( | |
this.vX**2 + this.vY**2 + friction**2 | |
-2 * sqrt(this.vX**2 + this.vY**2) * friction | |
- (this.vX2 / this.vX * this.vY) ** 2) | |
*/ | |
// avoid dividing by 0 | |
if (Math.abs(this.dY) < .003) { | |
if (this.vX > .001) { | |
this.vX -= this.friction; | |
if (this.vX < 0) this.vX = 0; | |
} else if (this.vX < -.001) { | |
this.vX += this.friction; | |
if (this.vX > 0) this.vX = 0; | |
} | |
} | |
if (Math.abs(this.dX) > .003) { | |
if (this.vY > .001) { | |
this.vY -= this.friction; | |
if (this.vY < 0) this.vY = 0; | |
} else if (this.vY < -.001) { | |
this.vY += this.friction; | |
if (this.vY > 0) this.vY = 0; | |
} | |
} | |
// this.vX / this.vY = this.vX2 / this.vY2 | |
var vX2 = Math.sqrt( | |
Math.pow(this.vX,2) + Math.pow(this.vY,2) + | |
Math.pow(this.friction,2) - | |
2 * Math.sqrt( | |
Math.pow(this.v,2) + Math.pow(this.v,2)) | |
* this.friction - | |
Math.pow(this.vX / this.vY * vY2, 2)); | |
var vY2 = vX2 / this.vX * this.vY | |
// lastly, apply friction | |
if (this.vX > .001) { | |
this.vX -= this.friction; | |
if (this.vX < 0) this.vX = 0; | |
} else if (this.vX < -.001) { | |
this.vX += this.friction; | |
if (this.vX > 0) this.vX = 0; | |
} | |
if (this.vY > .001) { | |
this.vY -= this.friction; | |
if (this.vY < 0) this.vY = 0; | |
} else if (this.vY < -.001) { | |
this.vY += this.friction; | |
if (this.vY > 0) this.vY = 0; | |
} | |
var deltaX = vX2 * delta / 1000; | |
var deltaY = vY2 * delta / 1000; | |
// console.log(this.vX + "\n" + this.vY); | |
this.moveTo(this.getX() + deltaX, this.getY() + deltaY); | |
}, | |
}; | |
asMovable.call(asItem.call(Ball.prototype)); | |
var Player = function() { | |
Ball.call(this); | |
} | |
extendObj(Player, Ball); | |
Player.prototype.draw = function() { | |
var ctx = canvas.getContext('2d'); | |
ctx.fillStyle = 'blue'; | |
ctx.beginPath(); | |
ctx.arc(this.getX(), this.getY(), this.getWidth(), 0, 360) | |
ctx.fill(); // todo: shading | |
}; | |
Player.prototype.tick = function(delta) { | |
var accelerated = false; | |
var moveLeft = keyStatus[KEY_CODES.LEFT]; | |
var moveRight = keyStatus[KEY_CODES.RIGHT]; | |
var moveUp = keyStatus[KEY_CODES.UP]; | |
var moveDown = keyStatus[KEY_CODES.DOWN]; | |
var deltaV = ((moveLeft || moveRight) && (moveUp || moveDown)) ? this.deltaVelocityDiagonal : this.deltaVelocity; | |
// deltaV *= delta / 1000; | |
if (moveLeft || moveRight) { | |
this.vX += deltaV * (moveLeft ? -1 : 1); | |
} | |
if (moveUp || moveDown) { | |
this.vY += deltaV * (moveUp ? -1 : 1); | |
} | |
Ball.prototype.tick.call(this, delta); // apply motion | |
} | |
var canvas; | |
var createCanvas = function() { | |
canvas = document.createElement('canvas'); | |
canvas.id = 'game'; | |
canvas.width = 500; | |
canvas.height = 400; | |
canvas.style.border = "1px solid red"; | |
document.body.appendChild(canvas); | |
}; | |
/* | |
function position(obj, x, y) { | |
obj.realX = x + obj.getX(); | |
obj.realY = y + obj.getY(); | |
if (obj.places) { | |
for (var i = 0; i < obj.places.length; i++) | |
position(obj.places[i], obj.realX, obj.realY) | |
} | |
} | |
function reposition(obj) { | |
var parent = obj.getParent(); | |
if (!parent) | |
position(obj, 0, 0); | |
else | |
position(obj, parent.realX, parent.realY); | |
} | |
*/ | |
function game() { | |
createCanvas(); | |
world = new Place('World'); | |
world.setSize(500, 400); | |
//var player = new Player(); | |
var b = new Ball(); | |
console.log(b.toString()); | |
world.addItem(b); | |
b.moveTo(200, 200); | |
var p = new Player(); | |
world.addItem(p); | |
p.moveTo(90,90); | |
function onkeydown(e) { | |
if (e.keyCode == KEY_CODES.LEFT || | |
e.keyCode == KEY_CODES.RIGHT || | |
e.keyCode == KEY_CODES.DOWN || | |
e.keyCode == KEY_CODES.UP) { | |
keyStatus[e.keyCode] = true; | |
} | |
} | |
function onkeyup(e) { | |
if (e.keyCode == KEY_CODES.LEFT || | |
e.keyCode == KEY_CODES.RIGHT || | |
e.keyCode == KEY_CODES.DOWN || | |
e.keyCode == KEY_CODES.UP) { | |
keyStatus[e.keyCode] = false; | |
} | |
} | |
document.addEventListener('keydown', onkeydown, false); | |
document.addEventListener('keyup', onkeyup, false); | |
//.setSize(200,100); | |
//world.addPlace(h); | |
//h.moveTo(100, 200); | |
var tock = 0;//new Date(); | |
function tick(timestamp) { | |
requestAnimationFrame(tick); | |
if (tock == 0) { | |
tock = timestamp; | |
return; | |
} | |
var newTock = timestamp;//new Date(); | |
delta = newTock - tock; | |
tock = newTock; | |
world.tick(delta); | |
world.draw(); | |
}; | |
requestAnimationFrame(tick); | |
//window.setInterval(tick, (1000 / FPS)); | |
} | |
game(); // jsfiddle | |
//document.addEventListener('DOMContentLoaded', game, false); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment