var canvas = document.getElementsByTagName("canvas")[0];
canvas.height = 300;
canvas.width = 300;
var ctx = canvas.getContext("2d");
var vec2 = function(_x,_y){
this.x = _x || 0;
this.y = _y || 0;
this.add = function(v){
var t = new vec2;
t.x = this.x + v.x;
t.y = this.y + v.y;
return t;
}
}
var Player = function(){
var fov = 15;
var direction;
var speed = 2;
var acceleration = new vec2(0,0);
var pos = new vec2(0,50*world.ts);
this.pos = function(){
return pos;
}
this.Draw = function(){
ctx.save();
ctx.fillStyle = "red";
ctx.fillRect(pos.x, pos.y, world.ts , world.ts);
ctx.restore();
}
this.Move = function(kc){
pos.x++;
if(pos.x > canvas.width)pos.x = 0;
}
this.Update = function(){
// var nextstep = pos.add(
// if()
}
this.RayCast = function(){
world.tileVisible("clear");
var rays = 3600;
var visible = [];
var angle = 360/rays;
for(var r = 0; r < rays; r++){
var p = new vec2(pos.x + world.ts/2, pos.y - 1 + world.ts/2);
var ang = (angle * r) * (Math.PI / 180);
for(var f = 0; f < fov; f++){
var pointing = new vec2(p.x + (world.ts*f) * Math.cos(ang), p.y + (world.ts*f) * Math.sin(ang));
if(world.getTileByCoord(pointing)){
world.tileVisible(pointing);
}else{
world.tileVisible(pointing);
break;
}
}
}
}
}
var WorldMap = function(){
var size = 100;
var ts = 3;
var visible = {};
var tiles = (function(){
var temp = [];
for(var y = 0; y < size; y++){
var row = [];
for(var x = 0; x < size; x++){
(y != 50 && Math.floor(Math.random()*100) > 90)? row.push(0) : row.push(1);
}
temp.push(row);
}
return temp;
})();
this.Draw = function(){
for(var y = 0; y < tiles.length; y++){
for(var x = 0; x < tiles[0].length; x++){
if(this.isVisible(x,y)){
if(tiles[y][x]){
// ctx.strokeRect(0 + (ts * x), 0 + (ts * y), ts, ts);
}else{
ctx.fillRect(0 + (ts * x), 0 + (ts * y), ts, ts);
}
}else{
ctx.save();
ctx.fillStyle = "grey";
ctx.fillRect(0 + (ts * x), 0 + (ts * y), ts, ts);
ctx.restore();
}
}
}
}
this.getPortion = function(cpos, fov, cb){
var temp = [];
var ini = new vec2(Math.round(cpos.x - fov.x/2), Math.round(cpos.y - fov.y/2));
for(var y = ini.y; y < ini.y + fov.y; y++){
for(var x = ini.x; x < ini.x + fov.x; x++){
if(tiles[y] && typeof tiles[y][x] == "number"){
if(cb) cb(new vec2(x,y));
else temp.push(new vec2(x,y));
}
}
}
if(!cb) return temp;
}
this.getTileByCoord = function(tile){
return tiles[Math.round(tile.y/ts)][Math.round(tile.x/ts)];
}
this.tileVisible = function(tile){
if(tile == "clear"){ delete visible; visible = {};}
if(!visible[Math.round(tile.x/ts)])
visible[Math.round(tile.x/ts)] = {};
visible[Math.round(tile.x/ts)][Math.round(tile.y/ts)] = true;
}
this.isVisible = function(tile, y){
if(!y){
if(visible[Math.round(tile.x/ts)] && visible[Math.round(tile.x/ts)][Math.round(tile.y/ts)])
return true;
return false;
}else{
if(visible[tile] && visible[tile][y])
return true
return false;
}
}
this.setVisibles = function(vis){
visible = vis;
}
this.map = tiles;
this.ts = ts;
}
function Raycast(args){
var map = args.map;
var center = args.center || {x: map.length/2,y: map[0].length/2};
var los = args.lengthOfSight || map.length/2;
var ts = args.tileSize || 5;
var prec = args.precision || 100;
var block = args.vBlock || [0];
var visibles = {};
var rays = 3600 * ((prec || 100)/100);
var los = los || map.length;
var visible = [];
var angle = 360/rays;
var getTileByCoord = function(tile){
return map[Math.round(tile.y/ts)][Math.round(tile.x/ts)];
}
var tileVisible_1 = function(tile){
if(!visibles[Math.round(tile.x / ts)])
visibles[Math.round(tile.x / ts)] = {};
visibles[Math.round(tile.x / ts)][Math.round(tile.y / ts)] = true;
}
var tileVisible = function(tile){
if(!visibles[Math.round(tile.x)])
visibles[Math.round(tile.x)] = {};
visibles[Math.round(tile.x)][Math.round(tile.y)] = true;
}
var getTilesCrossed = function(ini, end){
if(!window.first){
console.log(ini,end);
window.first = 1;
}
var steep = Math.abs(ini.y - end.x) > Math.abs(ini.y - end.x);
if (steep) {
ini = new vec2(ini.y, ini.x);
end = new vec2(end.y, end.x);
}
if (ini.x > end.x) {
ini = new vec2(ini.y, ini.x);
end = new vec2(end.y, end.x);
}
var deltax = end.x - ini.x;
var deltay = Math.abs(end.y - ini.y);
var error = 0;
var ystep;
var y = ini.y;
if(ini.y < end.y){
ystep = 1;
}else{
ystep = -1;
}
for (var x = ini.x; x <= end.x; x++) {
if(typeof map[y][x] == "number"){
var t = map[y][x];
if(steep)
tileVisible(new vec2(y,x));
else
tileVisible(new vec2(x,y));
if(block.indexOf(t) >= 0) break;
if(!steep){
error += deltay;
}
if (2 * error >= deltax) {
y += ystep;
error -= deltax;
}
}
}
}
var ini = new Date().getTime();
for(var r = 0; r < rays; r++){
var p = new vec2(center.x, center.y);
var ang = (angle * r) * (Math.PI / 180);
for(var f = 0; f < los; f++){
var pointing = new vec2(p.x + (ts*f) * Math.cos(ang), p.y -1 + (ts*f) * Math.sin(ang));
var t = parseInt(getTileByCoord(pointing));
if(typeof t == "number" && block.indexOf(t) < 0){
tileVisible_1(pointing);
}else{
tileVisible_1(pointing);
break;
}
}
}
// console.log("raycast in: ",new Date().getTime() - ini);
return visibles;
}
world = new WorldMap();
pj = new Player();
window.timer = setInterval(function(){
ctx.clearRect(0,0,canvas.width,canvas.height);
world.setVisibles(
Raycast({
map: world.map,
center: {x: pj.pos().x,y: pj.pos().y},
tileSize: world.ts,
lengthOfSight: 10,
precision: 100,
vBlock: [0]
})
);
world.Draw();
pj.Draw();
pj.Move();
},1000/60);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<canvas></canvas>