Skip to content

Instantly share code, notes, and snippets.

@grifdail
Created April 11, 2015 21:46
Show Gist options
  • Save grifdail/892b24d7691ef700de88 to your computer and use it in GitHub Desktop.
Save grifdail/892b24d7691ef700de88 to your computer and use it in GitHub Desktop.
Raycaster Javascript
function RayCastCamera(fov,resolution) {
this.fov = fov;
this.resolution = resolution; //How Many ray should we cast and draw;
}
RayCastCamera.prototype.render = function(map, pos, angle, walls) {
ctx.fillStyle = "black";
ctx.fillRect(0,0,width,height*0.5); //The Top of the Screen is black
ctx.fillStyle = "grey";
ctx.fillRect(0,height*0.5,width,height*0.5); // The bottom is grey
for(var i = 0; i<this.resolution; i++) {
var x = i / this.resolution - 0.5;
var deltaAngle = Math.atan2(x, this.fov); // Get the angle for this ray
var ray = map.cast(post, angle+deltaAngle, 10, ~1); // cast a ray, from the pos in the given angle, for a maximum of 10 unit and colliding whit everyblock except id=1
//Some maths to calculate the actual height of the wall based on the distance
//A wall, 1 tile directly in front of the player would fit the whole screen
var wallHeight = height / (ray.length * Math.cos(deltaAngle));
if (walls[ray.id]) { // We got an image for this wall type
var wall = walls[ray.id];
//We draw a pixel collum from the image
ctx.drawImage(wall(ray.offset*wall.width)|0,0,1,wall.height,i,(height-wallHeight)*0.5,1,wallHeight)
} else {
ctx.fillStyle = "white"; //We draw it white;
ctx.fillRect(i,(height-wallHeight)*0.5,1,wallHeight)
}
}
}
function Tilemap() {}
Tilemap.prototype.get = function(x,y) { // This function should return the content of the tile at X and Y... They may not be Integer
//Feel free to implement it has you would like
var i = Math.floor(x)+Math.floor(y)*this.width;
if (i>=0 && i<this.map.length) {
return this.map[i];
}
return 0;
}
Tilemap.prototype.cast = function(point, angle, range,bitmask) {
bitmask = bitmask || ~0;
var sin = Math.sin(angle);
var cos = Math.cos(angle);
//console.log(sin,cos,angle)
//debugger;
var length = 0;
var x = point.x;
var y = point.y;
var nextLengthX, nextLengthY, offset, id;
while (length < range && x>0 && y>0) {
//This is the trickiest part.
//Let's say you are in the middle of a case.
//We first need to know which case will we be entering first.
//the function step() calculate the distance until you cross the frontier with the next tile in a given direction
var nextLengthX = step(x,cos);
var nextLengthY = step(y,sin); // No need for a special function, we only need to switch the parameters
if (nextLengthX<nextLengthY) { // By comparice these distance we know what line will we cross first.
//We will enter a horizontal neightboor next
x = Math.round(x + nextLengthX * cos);
//Special note about round here
//We use that to fix float related error.
//This is only valid because at this step we can only be at a frontiers line (Interger X).
y = y + nextLengthX * sin; //We calculate the next Y
length += nextLengthX;
offset = y%1; //This is a shorcut for knowing witch wall portion we should draw
id = cos > 0 ? this.get(x,y) : this.get(x-1,y); //We get the value of the tile we're entering next
} else {
//Same thing here
x = x + nextLengthY * cos;
y = Math.round(y + nextLengthY * sin);
length += nextLengthY;
offset = x%1;
id = sin > 0 ? this.get(x,y) : this.get(x,y-1);
}
if (id & bitmask) { // Bitmasking to check if we stop
return {
x:x,y:y,length:length,id:id,offset:offset
}
}
}
return { //No wall in sight
x,y,length,id:0,outsideOfRange:true
}
function step(x, cos) {
//distance to the next tile depend of the direction
var nx = cos > 0 ? Math.ceil(x)-x : (x-Math.floor(x) || 1);
//Let's use thales Theorem
var distance = nx/Math.abs(cos);
return Number.isFinite(distance) ? ds : Infinity; //Avoid division by 0;
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment