Skip to content

Instantly share code, notes, and snippets.

@shuuki
Last active September 2, 2016 18:33
Show Gist options
  • Save shuuki/3a2912df1010c7451999b13eacbe60b0 to your computer and use it in GitHub Desktop.
Save shuuki/3a2912df1010c7451999b13eacbe60b0 to your computer and use it in GitHub Desktop.
beefy asteroids style spaceship movement
.controls
b Controls
ul
li Forward/back thrusters: Up/Down or W/S
li Rotation thrusters: Left/Right arrows
li Lateral thrusters: A/D
/** event listeners */
window.addEventListener("load", function() {
Sim.init();
Sim.update();
}, false);
window.addEventListener("keyup", function(event) {
Key.onKeyup(event);
}, false);
window.addEventListener("keydown", function(event) {
Key.onKeydown(event);
//console.log(event.keyCode)
}, false);
/** utilities */
var Util = {
degToRad: function(degrees) {
return degrees * (pi/180)
},
radToDeg: function(radians) {
return radians * (180/pi)
}
}
// cache pi for speed
const pi = Math.PI;
/** keyboard events */
var Key =
{
// index of recognized keys
BACKSPACE: 8,
LEFT: 37,
UP: 38,
RIGHT: 39,
DOWN: 40,
A: 65,
D: 68,
W: 87,
S: 83,
// cache of keys currently pressed
pressed: {},
// methods for tracking key state
isDown: function(keyCode)
{
return this.pressed[keyCode];
},
onKeydown: function(event)
{
this.pressed[event.keyCode] = true;
},
onKeyup: function(event)
{
delete this.pressed[event.keyCode];
}
}
/** canvas manipulation */
var Canvas = {}
Canvas.make = function(target)
{
let canvas = document.createElement("canvas"),
base = target;
canvas.id = "display";
canvas.width = 320;
canvas.height = 200;
base.appendChild(canvas);
}
/** the simulation */
var Sim = {};
Sim.init = function()
{
// start the clock
this.time = 0;
// set up canvas
Canvas.make(document.getElementsByTagName("body")[0]);
this.canvas = document.getElementById("display");
this.context = this.canvas.getContext("2d");
// experiment for later: do stuff with clicks
this.canvas.addEventListener("click", function() {
let x = event.pageX - this.offsetLeft;
let y = event.pageY - this.offsetTop;
console.log(x, y)
//show where clicked, needs to go into a layer queue
//Sim.context.fillRect(x-1, y-1, 3, 3);
}, false);
// declare entities to simulate
this.ship = new Ship(this.canvas);
}
Sim.update = function()
{
// kick off the next frame
requestAnimationFrame(this.update.bind(this));
// update clock
let now = new Date().getTime();
this.delta = now - (this.time || now);
this.time = now;
// update entities
this.ship.update(this.delta);
// render scene
this.render();
}
Sim.render = function()
{
// clear frame
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.context.beginPath()
// add some debug text
let debug = /*"T " + this.time + " Δ " + */(1000 / this.delta).toFixed(0) + "fps";
this.context.font = "12px monospace";
this.context.fillText(debug, 10, 20);
// render entities
this.ship.draw(this.context);
}
/** ship actor */
function Ship(domain)
{
this.domain = domain;
this.x = domain.width / 2;
this.y = domain.height / 2;
this.xSpeed = 0;
this.ySpeed = 0;
this.TurnSpeed = 0;
this.Direction = pi * 1.5;
}
Ship.prototype.update = function(increment)
{
// physics variables
var delta = increment / 1000,
// space
Friction = 0.000001,
TurnFriction = 0.00001,
// thrusters
thrustMain = 1.1,
thrustLateral = 0.7,
maxSpeed = 4,
maxTurn = 0.08;
// movement controls
if (Key.isDown(Key.UP) || Key.isDown(Key.W))
{
this.xSpeed += Math.cos(this.Direction) * (thrustMain * delta);
this.ySpeed += Math.sin(this.Direction) * (thrustMain * delta);
}
if (Key.isDown(Key.DOWN) || Key.isDown(Key.S))
{
this.xSpeed -= Math.cos(this.Direction) * (thrustLateral * delta);
this.ySpeed -= Math.sin(this.Direction) * (thrustLateral * delta);
}
if (Key.isDown(Key.A))
{
this.xSpeed += Math.cos(this.Direction - (pi / 2)) * (thrustLateral * delta);
this.ySpeed += Math.sin(this.Direction - (pi / 2)) * (thrustLateral * delta);
}
if (Key.isDown(Key.D))
{
this.xSpeed -= Math.cos(this.Direction - (pi / 2)) * (thrustLateral * delta);
this.ySpeed -= Math.sin(this.Direction - (pi / 2)) * (thrustLateral * delta);
}
// calculate length of the speed vector using pythagoras
var SpeedVectorLength = Math.sqrt((this.xSpeed * this.xSpeed) + (this.ySpeed * this.ySpeed));
// if moving, decrease speed with friction
if (SpeedVectorLength > 0)
{
this.xSpeed -= (this.xSpeed / SpeedVectorLength) * Friction;
this.ySpeed -= (this.ySpeed / SpeedVectorLength) * Friction;
}
// if speed is faster than max speed, decrease speed
if (SpeedVectorLength > maxSpeed)
{
this.xSpeed += (this.xSpeed / SpeedVectorLength) * (maxSpeed - SpeedVectorLength);
this.ySpeed += (this.ySpeed / SpeedVectorLength) * (maxSpeed - SpeedVectorLength);
}
this.x += this.xSpeed;
this.y += this.ySpeed;
// Rotation Movement (Keys Left, Right)
// controls
if (Key.isDown(Key.LEFT)) {
let thrust = maxTurn + this.TurnSpeed;
this.TurnSpeed -= thrust * delta;
}
if (Key.isDown(Key.RIGHT)) {
let thrust = maxTurn - this.TurnSpeed;
this.TurnSpeed += thrust * delta;
}
// limit turn speed
if (this.TurnSpeed > maxTurn) this.TurnSpeed = maxTurn;
if (this.TurnSpeed < -maxTurn) this.TurnSpeed = -maxTurn;
this.Direction += this.TurnSpeed;
// bound maximum direction to one rotation
if (this.Direction > pi * 2) { this.Direction -= pi * 2; }
if (this.Direction < 0) { this.Direction += pi * 2; }
// apply friction to rotation
if (this.TurnSpeed > TurnFriction) this.TurnSpeed -= TurnFriction;
if (this.TurnSpeed < -TurnFriction) this.TurnSpeed += TurnFriction;
// if friction is greater than speed, stop
if (this.TurnSpeed < TurnFriction && this.TurnSpeed > -TurnFriction) this.TurnSpeed = 0;
// reset ship to other side of canvas if it leaves domain, asteroids style
if (this.x > this.domain.width) this.x = 0;
if (this.x < 0) this.x = this.domain.width;
if (this.y > this.domain.height) this.y = 0;
if (this.y < 0) this.y = this.domain.height;
}
Ship.prototype.draw = function(context)
{
let shipAngle = 0.8,
shipSize = 8;
context.lineWidth = 2;
context.arc(this.x, this.y, shipSize, (this.Direction-pi-shipAngle), (this.Direction-pi+shipAngle), false);
context.arc(this.x, this.y, shipSize, this.Direction, this.Direction, false);
context.arc(this.x, this.y, shipSize, (this.Direction-pi-shipAngle), (this.Direction-pi+shipAngle), false);
//context.fillRect(this.x - 2, this.y - 2, 4, 4);
//context.arc(this.x, this.y, 4, 0, 2 * pi, false);
//context.fillText(Util.radToDeg(this.Direction).toFixed(0), 10, 40);
context.fill();
context.stroke();
context.closePath();
}
body {
background: silver;
margin: 0;
}
canvas {
background: white;
}
.controls {
position: absolute;
bottom: 0;
font: 10pt monospace;
opacity: 0.5;
ul, li {
margin: 0;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment