Skip to content

Instantly share code, notes, and snippets.

@mgiuffrida
Last active August 29, 2015 14:01
Show Gist options
  • Save mgiuffrida/f44246e7b569c4d59edf to your computer and use it in GitHub Desktop.
Save mgiuffrida/f44246e7b569c4d59edf to your computer and use it in GitHub Desktop.
magnetgolf
<html>
<head>
<script src="magnetgolf.js"></script>
</head>
<body>
</body>
</html>
// 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