Skip to content

Instantly share code, notes, and snippets.

@svanellewee
Last active August 29, 2015 14:05
Show Gist options
  • Select an option

  • Save svanellewee/d9f7e89c382f4e0d5922 to your computer and use it in GitHub Desktop.

Select an option

Save svanellewee/d9f7e89c382f4e0d5922 to your computer and use it in GitHub Desktop.
var magnitude = function(xcomp,ycomp) { return Math.sqrt(xcomp*xcomp + ycomp*ycomp); };
var RESOLUTION = window.innerWidth/2; // 320;
var HEIGHT = window.innerHeight/2; //480;
var div = function(x,y){ return Math.floor(x/y) };
var mod
var CIRCLE = Math.PI * 2;
function Controls() {
this.codes = { 37: 'left', 39: 'right', 38: 'forward', 40: 'backward' };
this.states = { 'left': false, 'right': false, 'forward': false, 'backward': false };
document.addEventListener('keydown', this.onKey.bind(this, true), false);
document.addEventListener('keyup', this.onKey.bind(this, false), false);
document.addEventListener('touchstart', this.onTouch.bind(this), false);
document.addEventListener('touchmove', this.onTouch.bind(this), false);
document.addEventListener('touchend', this.onTouchEnd.bind(this), false);
}
Controls.prototype.onTouch = function(e) {
var t = e.touches[0];
this.onTouchEnd(e);
if (t.pageY < window.innerHeight * 0.5) this.onKey(true, { keyCode: 38 });
else if (t.pageX < window.innerWidth * 0.5) this.onKey(true, { keyCode: 37 });
else if (t.pageY > window.innerWidth * 0.5) this.onKey(true, { keyCode: 39 });
};
Controls.prototype.onTouchEnd = function(e) {
this.states = { 'left': false, 'right': false, 'forward': false, 'backward': false };
e.preventDefault();
e.stopPropagation();
};
Controls.prototype.onKey = function(val, e) {
var state = this.codes[e.keyCode];
if (typeof state === 'undefined') return;
//console.log(state);
this.states[state] = val;
e.preventDefault && e.preventDefault();
e.stopPropagation && e.stopPropagation();
};
var worldMap = [
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,0,0,2,2,2,2,2,0,0,0,0,3,0,3,0,3,0,0,0,1],
[1,0,0,0,0,0,2,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,3,0,0,0,3,0,0,0,1],
[1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,0,0,2,2,0,2,2,0,0,0,0,3,0,3,0,3,0,0,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,4,0,4,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,4,0,0,0,0,5,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,4,0,4,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,4,0,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] ];
// console.log(worldMap[0][0]);
function Renderer () {
var canvas = document.getElementById('display')
this.ctx = canvas.getContext('2d');
this.width = canvas.width = window.innerWidth *0.5;
this.height = canvas.height = window.innerHeight *0.5;
};
var renderer = new Renderer();
var plane_vector_X = 0.0, plane_vector_Y = 0.66; //the 2d raycaster version of camera plane
var main = function (posX, posY, view_direction_X, view_direction_Y) {
// so FOV = 2 * atan (0.66/1)
var ctx = renderer.ctx;
for(var x = 0; x < RESOLUTION; x++) {
var plane_vector_magnitude = 2*x/ (1.0*RESOLUTION) - 1 // -1 ... 0 ... 1
var rayPosX = 1.0*posX;
var rayPosY = 1.0*posY;
// vector addition! vec_ray = vec_dir + (vec_plane * plane_vector_magnitude);
// apply defaults to decrypt! remember plane_vector_magnitude is varying -1..0..+1
var ray_direction_vector_X = 1.0* (view_direction_X + plane_vector_X * plane_vector_magnitude);
var ray_direction_vector_Y = 1.0* (view_direction_Y + plane_vector_Y * plane_vector_magnitude);
var map_X_index = Math.floor(posX);
var map_Y_index = Math.floor(posY);
// distance**2 = x**2 + y **2
// distance**2/y**2 = x**2/y**2 + 1
// distance/y = sqrt(x**2/y**2 + 1)
var deltaDistX = Math.sqrt(1 + (ray_direction_vector_Y * ray_direction_vector_Y) / (ray_direction_vector_X * ray_direction_vector_X));
var deltaDistY = Math.sqrt(1 + (ray_direction_vector_X * ray_direction_vector_X) / (ray_direction_vector_Y * ray_direction_vector_Y));
var stepX, stepY;
var sideDistX, sideDistY;
if (ray_direction_vector_X < 0 ) {
stepX = -1;
sideDistX = (rayPosX - map_X_index) * deltaDistX;
} else {
stepX = 1;
sideDistX = (map_X_index + 1.0 - rayPosX) * deltaDistX;
}
if (ray_direction_vector_Y < 0) {
stepY = -1;
sideDistY = (rayPosY - map_Y_index) * deltaDistY;
} else {
stepY = 1;
sideDistY = (map_Y_index + 1.0 - rayPosY) * deltaDistY;
}
var side;
var hit = 0;
while (hit == 0) {
if (sideDistX < sideDistY) {
sideDistX +=deltaDistX;
map_X_index += stepX;
map_X_index = Math.floor(map_X_index)
side = 0;
} else {
sideDistY += deltaDistY;
map_Y_index += stepY
map_Y_index = Math.floor(map_Y_index)
side = 1;
}
if (worldMap[map_X_index][map_Y_index] > 0)
hit = 1;
}
var perpWallDist = 0;
if (side == 0) {
perpWallDist = Math.abs((map_X_index - rayPosX + (1 - stepX) / 2.0) / ray_direction_vector_X);
} else {
perpWallDist = Math.abs((map_Y_index - rayPosY + (1 - stepY) / 2.0) / ray_direction_vector_Y);
}
var lineHeight = Math.abs(HEIGHT/perpWallDist); // how big is the wall slither?
lineHeight = Math.floor(lineHeight);
var drawStart = -lineHeight / 2 + HEIGHT/2;
drawStart = drawStart < 0 ? 0 : Math.floor(drawStart);
var drawEnd = lineHeight / 2 + HEIGHT/2;
drawEnd = drawEnd >= HEIGHT ? HEIGHT-1 : Math.ceil(drawEnd);
var color = "#000000";
switch(worldMap[map_X_index][map_Y_index]) {
case 1: color = "#FF00FF"; break;
case 2: color = "#0000FF"; break;
case 3: color = "#FF0000"; break;
case 4: color = "#00FF00"; break;
default: color = "#FFFFFF"; break;
}
ctx.fillStyle = color;
ctx.fillRect(x, drawStart, 1, Math.abs(drawEnd-drawStart) );
}
};
// init state
var posX = 22.0, posY = 12.0;
//var view_direction_X = -0.5, dirY = 0.5;
var view_direction_X = -1.0, view_direction_Y = 0.0;
var controls = new Controls();
var rotate = function(angle) {
angle = angle % CIRCLE;
var cos_a = Math.cos(angle);
var sin_a = Math.sin(angle);
var new_plane_X, new_plane_Y;
var new_view_X, new_view_Y;
new_view_X = view_direction_X*cos_a - view_direction_Y*sin_a;
new_view_Y = view_direction_X*sin_a + view_direction_Y*cos_a;
new_plane_X = plane_vector_X*cos_a - plane_vector_Y*sin_a;
new_plane_Y = plane_vector_X*sin_a + plane_vector_Y*cos_a;
plane_vector_X = new_plane_X;
plane_vector_Y = new_plane_Y;
view_direction_X = new_view_X;
view_direction_Y = new_view_Y;
}
var walk = function(distance) {
var dx = view_direction_X * distance;
var dy = view_direction_Y * distance;
posX += dx;
posY += dy;
}
var update = function(seconds) {
// console.log(seconds,controls.states);
if (controls.states.left) rotate(Math.PI * seconds);
if (controls.states.right) rotate(-Math.PI * seconds);
if (controls.states.forward) walk(3 * seconds);
if (controls.states.backward) walk(-3 * seconds);
renderer.ctx.clearRect(0,0,RESOLUTION, HEIGHT);
main(posX,posY, view_direction_X, view_direction_Y);
}
var lastTime;
var frame = function(time) {
var seconds = (time - lastTime) / 1000;
lastTime = time;
if (seconds < 0.2) update(seconds);
requestAnimationFrame(frame);
}
frame(0)
//main(posX,posY, view_direction_X, view_direction_Y);
// the code required for demo, but not the point of the tut
var CIRCLE = Math.PI * 2;
var MOBILE = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent)
function Controls() {
this.codes = { 37: 'left', 39: 'right', 38: 'forward', 40: 'backward' };
this.states = { 'left': false, 'right': false, 'forward': false, 'backward': false };
document.addEventListener('keydown', this.onKey.bind(this, true), false);
document.addEventListener('keyup', this.onKey.bind(this, false), false);
document.addEventListener('touchstart', this.onTouch.bind(this), false);
document.addEventListener('touchmove', this.onTouch.bind(this), false);
document.addEventListener('touchend', this.onTouchEnd.bind(this), false);
}
Controls.prototype.onTouch = function(e) {
var t = e.touches[0];
this.onTouchEnd(e);
if (t.pageY < window.innerHeight * 0.5) this.onKey(true, { keyCode: 38 });
else if (t.pageX < window.innerWidth * 0.5) this.onKey(true, { keyCode: 37 });
else if (t.pageY > window.innerWidth * 0.5) this.onKey(true, { keyCode: 39 });
};
Controls.prototype.onTouchEnd = function(e) {
this.states = { 'left': false, 'right': false, 'forward': false, 'backward': false };
e.preventDefault();
e.stopPropagation();
};
Controls.prototype.onKey = function(val, e) {
var state = this.codes[e.keyCode];
if (typeof state === 'undefined') return;
this.states[state] = val;
e.preventDefault && e.preventDefault();
e.stopPropagation && e.stopPropagation();
};
function Player(x, y, direction) {
this.x = x;
this.y = y;
this.direction = direction;
this.weapon = null;
try {
this.weapon = new Bitmap('assets/knife_hand.png', 319, 320);
}catch (e) {
// ignore it!
alert("NO PNG!");
}
this.paces = 0;
}
Player.prototype.rotate = function(angle) {
this.direction = (this.direction + angle + CIRCLE) % (CIRCLE);
console.log("rotated to :"+this.direction)
};
Player.prototype.walk = function(distance, map) {
var dx = Math.cos(this.direction) * distance;
var dy = Math.sin(this.direction) * distance;
if (map && map.get(this.x + dx, this.y) <= 0) this.x += dx;
if (map && map.get(this.x, this.y + dy) <= 0) this.y += dy;
this.paces += distance;
console.log("walking to:"+this.paces);
};
Player.prototype.update = function(controls, map, seconds) {
if (controls.left) this.rotate(-Math.PI * seconds);
if (controls.right) this.rotate(Math.PI * seconds);
if (controls.forward) this.walk(3 * seconds, map);
if (controls.backward) this.walk(-3 * seconds, map);
};
function GameLoop() {
this.frame = this.frame.bind(this);
this.lastTime = 0;
this.callback = function() {};
}
GameLoop.prototype.start = function(callback) {
this.callback = callback;
requestAnimationFrame(this.frame);
};
GameLoop.prototype.frame = function(time) {
var seconds = (time - this.lastTime) / 1000;
this.lastTime = time;
if (seconds < 0.2) this.callback(seconds);
requestAnimationFrame(this.frame);
};
var display = document.getElementById('display');
var player = new Player(15.3, -1.2, Math.PI * 0.3);
var controls = new Controls();
var loop = new GameLoop();
//map.randomize();
var map = null;
loop.start(function frame(seconds) {
//map.update(seconds);
player.update(controls.states, map, seconds);
//camera.render(player, map);
});
<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>canvasfun</title>
</head>
<body style='background: #000; margin: 0; padding: 0; width: 100%; height: 100%;'>
<canvas id='display' width='1' height='1' style='width: 100%; height: 100%;' />
<script src="boilerplate.js"></script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment