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>