I'm working on a raycaster in ActionScript 3.0 for the fun of it, and as a learning experience.
I've got it up and running and its displaying me output as expected however I'm getting this strange bug where rays go through corners of blocks and the edges of blocks appear through walls.
Maybe somebody with more experience can point out what I'm doing wrong or maybe a fresh pair of eyes can spot a tiny bug I haven't noticed.
Thank you so much for your help!
Screenshots:
http://i55.tinypic.com/25koebm.jpg
http://i51.tinypic.com/zx5jq9.jpg
Relevant code:
function drawScene()
{
rays.graphics.clear();
rays.graphics.lineStyle(1, rgba(0x00,0x66,0x00));
var halfFov = (player.fov/2);
var numRays:int = ( stage.stageWidth / COLUMN_SIZE );
var prjDist = ( stage.stageWidth / 2 ) / Math.tan(toRad( halfFov ));
var angStep = ( player.fov / numRays );
for( var i:int = 0; i < numRays; i++ ) {
var rAng = ( ( player.angle - halfFov ) + ( angStep * i ) ) % 360;
if( rAng < 0 ) rAng += 360;
var ray:Object = castRay(player.position, rAng);
drawRaySlice(i*COLUMN_SIZE, prjDist, player.angle, ray);
}
}
function drawRaySlice(sx:int, prjDist, angle, ray:Object)
{
if( ray.distance >= MAX_DIST )
return;
var height:int = int(( TILE_SIZE / (ray.distance * Math.cos(toRad(angle-ray.angle))) ) * prjDist);
if( !height )
return;
var yTop = int(( stage.stageHeight / 2 ) - ( height / 2 ));
if( yTop < 0 ) yTop = 0;
var yBot = int(( stage.stageHeight / 2 ) + ( height / 2 ));
if( yBot > stage.stageHeight ) yBot = stage.stageHeight;
rays.graphics.moveTo(
(ray.origin.x / TILE_SIZE) * MINI_SIZE,
(ray.origin.y / TILE_SIZE) * MINI_SIZE
);
rays.graphics.lineTo(
(ray.hit.x / TILE_SIZE) * MINI_SIZE,
(ray.hit.y / TILE_SIZE) * MINI_SIZE
);
for( var x:int = 0; x < COLUMN_SIZE; x++ ) {
for( var y:int = yTop; y < yBot; y++ ) {
buffer.setPixel(sx+x, y, clrTable[ray.tile-1] >> ( ray.horz ? 1 : 0 ));
}
}
}
function castRay(origin:Point, angle):Object
{
// Return values
var rTexel = 0;
var rHorz = false;
var rTile = 0;
var rDist = MAX_DIST + 1;
var rMap:Point = new Point();
var rHit:Point = new Point();
// Ray angle and slope
var ra = toRad(angle) % ANGLE_360;
if( ra < ANGLE_0 ) ra += ANGLE_360;
var rs = Math.tan(ra);
var rUp = ( ra > ANGLE_0 && ra < ANGLE_180 );
var rRight = ( ra < ANGLE_90 || ra > ANGLE_270 );
// Ray position
var rx = 0;
var ry = 0;
// Ray step values
var xa = 0;
var ya = 0;
// Ray position, in map coordinates
var mx:int = 0;
var my:int = 0;
var mt:int = 0;
// Distance
var dx = 0;
var dy = 0;
var ds = MAX_DIST + 1;
// Horizontal intersection
if( ra != ANGLE_180 && ra != ANGLE_0 && ra != ANGLE_360 ) {
ya = ( rUp ? TILE_SIZE : -TILE_SIZE );
xa = ya / rs;
ry = int( origin.y / TILE_SIZE ) * ( TILE_SIZE ) + ( rUp ? TILE_SIZE : -1 );
rx = origin.x + ( ry - origin.y ) / rs;
mx = 0;
my = 0;
while( mx >= 0 && my >= 0 && mx < world.size.x && my < world.size.y ) {
mx = int( rx / TILE_SIZE );
my = int( ry / TILE_SIZE );
mt = getMapTile(mx,my);
if( mt > 0 && mt < 9 ) {
dx = rx - origin.x;
dy = ry - origin.y;
ds = ( dx * dx ) + ( dy * dy );
if( rDist >= MAX_DIST || ds < rDist ) {
rDist = ds;
rTile = mt;
rMap.x = mx;
rMap.y = my;
rHit.x = rx;
rHit.y = ry;
rHorz = true;
rTexel = int(rx % TILE_SIZE)
}
break;
}
rx += xa;
ry += ya;
}
}
// Vertical intersection
if( ra != ANGLE_90 && ra != ANGLE_270 ) {
xa = ( rRight ? TILE_SIZE : -TILE_SIZE );
ya = xa * rs;
rx = int( origin.x / TILE_SIZE ) * ( TILE_SIZE ) + ( rRight ? TILE_SIZE : -1 );
ry = origin.y + ( rx - origin.x ) * rs;
mx = 0;
my = 0;
while( mx >= 0 && my >= 0 && mx < world.size.x && my < world.size.y ) {
mx = int( rx / TILE_SIZE );
my = int( ry / TILE_SIZE );
mt = getMapTile(mx,my);
if( mt > 0 && mt < 9 ) {
dx = rx - origin.x;
dy = ry - origin.y;
ds = ( dx * dx ) + ( dy * dy );
if( rDist >= MAX_DIST || ds < rDist ) {
rDist = ds;
rTile = mt;
rMap.x = mx;
rMap.y = my;
rHit.x = rx;
rHit.y = ry;
rHorz = false;
rTexel = int(ry % TILE_SIZE);
}
break;
}
rx += xa;
ry += ya;
}
}
return {
angle: angle,
distance: Math.sqrt(rDist),
hit: rHit,
map: rMap,
tile: rTile,
horz: rHorz,
origin: origin,
texel: rTexel
};
}