Last active
October 13, 2015 19:57
-
-
Save Cacodaimon/4247610 to your computer and use it in GitHub Desktop.
Simple untextured Raycaster example from cacodaemon.de
This file contains hidden or 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
/* | |
* Simple JavaScript game manager. | |
* | |
* By Guido Krömer <[email protected]> | |
* | |
*/ | |
function GameManager () { | |
var canvas = null; | |
var ctx = null; | |
var delta = 0; | |
var lastTimeStamp = null; | |
var freePos = -1; | |
var gameObjects = new Array(); | |
this.width = 0; | |
this.height = 0; | |
this.init = function (canvas) { | |
lastTimeStamp = new Date().getTime(); | |
canvas = canvas; | |
ctx = canvas.getContext('2d'); | |
this.width = canvas.width; | |
this.height = canvas.height; | |
window.requestAnimFrame = (function(){ | |
return window.requestAnimationFrame || | |
window.webkitRequestAnimationFrame || | |
window.mozRequestAnimationFrame || | |
window.oRequestAnimationFrame || | |
window.msRequestAnimationFrame || | |
function (callback) { | |
window.setTimeout(callback, 1000 / 120); | |
}; | |
})(); | |
(function animloop(){ | |
requestAnimFrame(animloop); | |
timerStart(); | |
update(); | |
draw(); | |
timerEnd(); | |
})(); | |
} | |
this.addGameObject = function(gameObject) { | |
gameObject.init(this); | |
if (freePos != -1) { | |
gameObjects[this.freePos] = gameObject; | |
freePos = -1; | |
} | |
else { | |
gameObjects.push(gameObject); | |
} | |
}; | |
this.addGameObjects = function(gameObjects) { | |
for (var i = gameObjects.length - 1; i >= 0; i--) { | |
this.addGameObject(gameObjects[i]); | |
} | |
}; | |
this.removeGameObject = function(gameObject) { | |
for (var i = gameObjects.length - 1; i >= 0; i--) { | |
if (gameObjects[i] == gameObject) { | |
gameObjects.splice(i, 1); | |
freePos = i; | |
return; | |
} | |
} | |
}; | |
this.forEachGameObject = function(callBack) { | |
for (var i = gameObjects.length - 1; i >= 0; i--) { | |
callBack(gameObject[i]); | |
} | |
}; | |
var update = function() { | |
for (var i = gameObjects.length - 1; i >= 0; i--) { | |
gameObjects[i].update(delta); | |
} | |
}; | |
var draw = function() { | |
for (var i = gameObjects.length - 1; i >= 0; i--) { | |
gameObjects[i].draw(ctx); | |
} | |
}; | |
var timerStart = function() { | |
var date = new Date(); | |
delta = date.getTime() - this.lastTimeStamp; | |
delta *= 0.01; | |
}; | |
var timerEnd = function() { | |
this.lastTimeStamp = new Date().getTime(); | |
}; | |
} |
This file contains hidden or 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
/* | |
* Abstract game object class. | |
* | |
* By Guido Krömer <[email protected]> | |
* | |
*/ | |
function GameObject () { | |
this.gameManager = null; | |
this.init = function (gameManager) { | |
this.gameManager = gameManager; | |
}; | |
this.update = function (delta) { }; | |
this.draw = function (ctx) { }; | |
} |
This file contains hidden or 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
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<title>Untextured Raycaster Demo from cacodaemon.de</title> | |
<style type="text/css"> | |
* { | |
padding: 0; | |
margin: 0; | |
image-rendering:optimizeSpeed; /* Legal fallback */ | |
image-rendering:-moz-crisp-edges; /* Firefox */ | |
image-rendering:-o-crisp-edges; /* Opera */ | |
image-rendering:-webkit-optimize-contrast; /* Chrome (and eventually Safari) */ | |
image-rendering:crisp-edges; /* CSS3 Proposed */ | |
image-rendering: optimize-contrast; /* Possible future browsers. */ | |
-ms-interpolation-mode:bicubic; /* IE8+ */ | |
} | |
#GameCanvas { | |
transform-origin: 0% 0%; | |
-ms-transform-origin: 0% 0%; | |
-webkit-transform-origin: 0% 0%; | |
-o-transform-origin: 0% 0%; | |
-moz-transform-origin: 0% 0%; | |
transform: scale(3, 3); | |
-ms-transform: scale(3, 3); | |
-webkit-transform: scale(3, 3); | |
-o-transform: scale(3, 3); | |
-moz-transform: scale(3, 3); | |
} | |
</style> | |
<script type="text/javascript" src="GameManager.js"></script> | |
<script type="text/javascript" src="GameObject.js"></script> | |
<script type="text/javascript" src="Keyboard.js"></script> | |
<script type="text/javascript" src="Raycaster.js"></script> | |
</head> | |
<body> | |
<canvas id="GameCanvas" /> | |
</body> | |
<script> | |
var scale = 3 | |
var gameCanvas = document.getElementById("GameCanvas"); | |
gameCanvas.width = window.innerWidth / scale; | |
gameCanvas.height = window.innerHeight / scale; | |
var gameManager = new GameManager(); | |
gameManager.init(gameCanvas); | |
gameManager.addGameObject(new Raycaster()); | |
</script> | |
</html> | |
This file contains hidden or 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
/* | |
* Simple JavaScipt keyboard helper. | |
* Based upon http://nokarma.org/2011/02/27/javascript-game-development-keyboard-input/index.html | |
* | |
*/ | |
var Key = { | |
pressed: [], | |
LEFT: 37, | |
UP: 38, | |
RIGHT: 39, | |
DOWN: 40, | |
ENTER: 13, | |
SPACE: 32, | |
PAGE_UP: 33, | |
PAGE_DOWN: 34, | |
W: 87, | |
A: 65, | |
S: 83, | |
D: 68, | |
isDown: function(keyCode) { | |
return this.pressed[keyCode]; | |
}, | |
reset: function(keyCode) { | |
this.pressed[keyCode] = false; | |
}, | |
onKeydown: function(event) { | |
this.pressed[event.keyCode] = true; | |
}, | |
onKeyup: function(event) { | |
delete this.pressed[event.keyCode]; | |
} | |
}; | |
window.addEventListener('keyup', function(event) { | |
Key.onKeyup(event); | |
}, false); | |
window.addEventListener('keydown', function(event) { | |
Key.onKeydown(event); | |
}, false); |
This file contains hidden or 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
/* | |
* Simple JavaScript Canvas raycaster (untextured). | |
* | |
* By Guido Krömer <[email protected]> | |
* | |
*/ | |
function Vector2() { | |
this.x; | |
this.y; | |
}; | |
var TILE = 48; | |
function Player(x, y, raycaster) { | |
this.x = x; | |
this.y = y; | |
this.angle = 0; | |
this.moveableForward = true; | |
this.moveableBackward = true; | |
this.raycaster = raycaster; | |
this.update = function(delta) { | |
if (Key.isDown(Key.UP) && this.moveableForward) { | |
this.x += Math.cos(this.angle) * (TILE / 3) * delta; | |
this.y += Math.sin(this.angle) * (TILE / 3) * delta; | |
} | |
if (Key.isDown(Key.DOWN) && this.moveableBackward) { | |
this.x -= Math.cos(this.angle) * (TILE / 3) * delta; | |
this.y -= Math.sin(this.angle) * (TILE / 3) * delta; | |
} | |
if (Key.isDown(Key.LEFT)) { | |
this.angle -= 0.05; | |
} | |
if (Key.isDown(Key.RIGHT)) { | |
this.angle += 0.05; | |
} | |
if (this.angle < 0) { | |
this.angle += Math.PI * 2; | |
} | |
if (Key.isDown(113)) { | |
Key.reset(113); | |
this.raycaster.preciseDistance = !this.raycaster.preciseDistance; | |
} | |
if (Key.isDown(115)) { | |
Key.reset(115); | |
this.raycaster.depthShading = !this.raycaster.depthShading; | |
} | |
this.angle %= Math.PI * 2; | |
} | |
}; | |
Player.prototype = new Vector2(); | |
function Raycaster() { | |
this.map = null; | |
var COLORS = ['', 'rgb(255, 0, 0)', 'rgb(0, 255, 0)', 'rgb(0, 0, 255)', 'rgb(255, 255, 0)', 'rgb(0, 255, 255)', 'rgb(255, 0, 255)', 'rgb(255, 255, 255)']; | |
var INFINITY = 1 / 0; | |
var SCR_W = 0; | |
var SCR_H = 0; | |
var SCR_W_HALF = 0; | |
var SCR_H_HALF = 0; | |
var WALL_H_FACTOR = 30; | |
var WALL_H = 0; | |
var RAY_ANGLE = 0; | |
var PI_TWO = Math.PI * 2; | |
var PI_HALF = Math.PI / 2; | |
var VOF = 60 * (Math.PI / 180); | |
var VOF_HALF = VOF / 2; | |
var TILE_QUATER = TILE / 2; | |
this.preciseDistance = true | |
this.depthShading = true | |
this.player = new Player(TILE * 3, TILE * 3, this); | |
this.getColor = function(lineElement) { | |
if (!this.depthShading) { | |
return COLORS[lineElement.color]; | |
} | |
var dist = lineElement.dist / (TILE_QUATER / 16); | |
var factor = Math.ceil(lineElement.north ? 255 - dist * 1.3 : 255 - dist * 1.5); | |
return COLORS[lineElement.color].replace('255', factor).replace('255', factor) | |
} | |
this.init = function (gameManager) { | |
this.gameManager = gameManager; | |
SCR_W_HALF = (SCR_W = this.gameManager.width) / 2; | |
SCR_H_HALF = (SCR_H = this.gameManager.height) / 2; | |
RAY_ANGLE = VOF / SCR_W; | |
WALL_H = SCR_H * WALL_H_FACTOR; | |
this.loadMap(); | |
} | |
this.draw = function(ctx) { | |
ctx.fillStyle = "black"; | |
ctx.fillRect(0, 0, this.gameManager.width, this.gameManager.height); | |
var lineElement = { | |
y: 0, | |
x: 0, | |
color: 0, | |
north: false, | |
dist: 0 | |
}; | |
var i = 0; | |
for (var rayAngle = -VOF_HALF; rayAngle < VOF_HALF; rayAngle += RAY_ANGLE) { | |
var dx = this.player.x + Math.cos(this.player.angle + rayAngle) * 100; | |
var dy = this.player.y + Math.sin(this.player.angle + rayAngle) * 100; | |
this.getLine(this.player.x, this.player.y, dx, dy, lineElement); | |
if (this.preciseDistance) { | |
var vX = this.player.x - lineElement.x; | |
var vY = this.player.y - lineElement.y; | |
lineElement.dist = Math.sqrt(vX * vX + vY * vY) * Math.cos(rayAngle); | |
} | |
var wallFactor = SCR_H_HALF / lineElement.dist * TILE_QUATER | |
ctx.strokeStyle = this.getColor(lineElement); | |
ctx.beginPath(); | |
ctx.moveTo(i, SCR_H_HALF - wallFactor); | |
ctx.lineTo(i, SCR_H_HALF + wallFactor); | |
ctx.closePath(); | |
ctx.stroke(); | |
if (i == SCR_W_HALF) { | |
this.player.moveableForward = lineElement.dist > 10; | |
} | |
i++; | |
} | |
this.drawMap(ctx); | |
} | |
this.getLine = function(x1, y1, x2, y2, lineElement) { | |
var dx = Math.abs(x2 - x1); | |
var dy = Math.abs(y2 - y1); | |
var sx = (x1 < x2) ? 1 : -1; | |
var sy = (y1 < y2) ? 1 : -1; | |
var err = dx - dy; | |
var e2; | |
var perviousTileX = 0; | |
var perviousTileY = 0; | |
var distance = 0; | |
while (!((x1 == x2) && (y1 == y2))) { | |
e2 = err << 1; | |
if (e2 > -dy) { | |
err -= dy; | |
x1 += sx; | |
distance++; | |
} | |
else if (e2 < dx) { | |
err += dx; | |
y1 += sy; | |
distance++; | |
} | |
var mapX = Math.floor(x1 / TILE); | |
var mapY = Math.floor(y1 / TILE); | |
if (this.map[mapY][mapX]) { | |
lineElement.y = y1; | |
lineElement.x = x1; | |
lineElement.color = this.map[mapY][mapX]; | |
lineElement.north = perviousTileX == mapX; | |
lineElement.dist = distance; | |
return; | |
} | |
perviousTileX = mapX; | |
perviousTileY = mapY; | |
} | |
} | |
this.update = function(delta) { | |
this.player.update(delta); | |
} | |
this.loadMap = function() { | |
this.map = [ | |
[5, 5, 5, 5, 5, 5, 5, 4, 0, 0, 0, 4, 5, 5, 5, 5, 5, 5, 5, 5], | |
[2, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 4], | |
[2, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 4], | |
[2, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 1, 1, 0, 4], | |
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 4], | |
[2, 0, 0, 1, 0, 1, 0, 0, 0, 2, 1, 2, 0, 0, 0, 0, 1, 1, 0, 4], | |
[2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 4], | |
[2, 0, 0, 2, 0, 2, 0, 0, 0, 2, 1, 2, 0, 0, 0, 0, 1, 1, 0, 4], | |
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4], | |
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4], | |
[2, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4], | |
[2, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4], | |
[2, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 2, 3, 4, 5, 6, 0, 4], | |
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4], | |
[2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 2, 3, 4, 5, 6, 3, 3] | |
]; | |
} | |
this.drawMap = function (ctx) { | |
ctx.fillStyle = 'BLACK' | |
ctx.fillRect(0, 0, this.map[0].length * 2, this.map.length * 2); | |
for (var y = 0; y < this.map.length; y++) { | |
for (var x = 0; x < this.map[y].length; x++) { | |
if (!this.map[y][x]) { | |
continue; | |
} | |
ctx.fillStyle = COLORS[this.map[y][x]]; | |
ctx.fillRect(x*2, y*2, 2, 2); | |
} | |
} | |
ctx.fillStyle = 'WHITE' | |
ctx.fillRect((this.player.x / TILE) * 2, (this.player.y / TILE) * 2, 4 ,4) | |
} | |
}; | |
Raycaster.prototype = new GameObject(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment