Raycaster

SubmittedDecember 21, 2016

5.0 rating based on 1 rating

Featured Screenshot

Author

blue_knight

Download Script Package

Raycaster.qst

Description

This is a fun little script that renders the current screens in first person using raycasting and column rendering.
X, Y is on the horizontal plane.
Z is up in 3D space.
This script has no set limits, except the map limit 16x8 screens.

Script Contents

/////////////////////////////////////////////////////
/////// Raycasting Engine Script, version 0.2 ///////
/////// by blue_knight ///////
/////////////////////////////////////////////////////
/////////////////////////////////////////////////////
// This is a fun little script
// that renders the current screens in first person
// using raycasting and column rendering.
// X, Y is on the horizontal plane.
// Z is up in 3D space.
// This script has no set limits, except the map limit
// 16x8 screens.
/////////////////////////////////////////////////////

const int scrWidth = 256;
const int scrHeight = 176;

ffc script Raycaster
{
/////////////////////////////
// startX : start X position in virtual space.
// startY : start Y position in virtual space.
// BackgroundTiles : Tile index for the background.
/////////////////////////////
void run(int startX, int startY, int BackgroundTiles)
{
//use virtual coordinates for Link's position.
//since he is not displayed, we don't bother keeping local coordinates, so Link's position is forced to the center.
float camPos_X = startX;
float camPos_Y = startY;
float camDir_X;
float camDir_Y;
float camYaw = 0;
float moveSpeed = 1.0;
float playerRadius = 16;

//ginormous array...
int map_combo[1024]; //holds the 32x32 area centered on the camera.
int map_left_x=0;
int map_left_y=0;

int map_cenX = 0;
int map_cenY = 0;

int curScreen = Game->GetCurScreen();
int curMap = Game->GetCurMap();

while (true)
{
//update the movement.
if ( Link->InputLeft )
camYaw-=2;
else if ( Link->InputRight )
camYaw+=2;

if (camYaw = 360 )
camYaw -= 360;

camDir_X = Cos(camYaw);
camDir_Y = Sin(camYaw);

float newPosX = camPos_X;
float newPosY = camPos_Y;
float travelDirX;
float travelDirY;
bool bCollisionNeeded = false;
if ( Link->InputUp )
{
newPosX = newPosX + camDir_X*moveSpeed;
newPosY = newPosY + camDir_Y*moveSpeed;
bCollisionNeeded = true;

travelDirX = camDir_X;
travelDirY = camDir_Y;
}
else if ( Link->InputDown )
{
newPosX = newPosX - camDir_X*moveSpeed;
newPosY = newPosY - camDir_Y*moveSpeed;
bCollisionNeeded = true;

travelDirX = -camDir_X;
travelDirY = -camDir_Y;
}

//Super basic collision for now.
//If we're intersecting a solid combo after moving, we revert the move.
//note that we actually check ahead in the direction of travel so we don't get too close to
//the wall.
if ( bCollisionNeeded )
{
if ( ComboSolid( Floor(newPosX+travelDirX*playerRadius), Floor(newPosY+travelDirY*playerRadius), curMap, curScreen ) )
{
newPosX = camPos_X;
newPosY = camPos_Y;
}
}
camPos_X = newPosX;
camPos_Y = newPosY;

//Read the area around the camera into an array
//whenever we change cells.
//This way we don't have to call GetComboFromTileIndex() or Game->GetComboData()
//up to 16 times per ray.
//This improves our performance by 40% or more.
int mx = camPos_X>>4;
int my = camPos_Y>>4;
if ( mx != map_cenX || my != map_cenY )
{
map_cenX = mx;
map_cenY = my;
map_left_x = Max(mx-16,0);
map_left_y = Max(my-16,0);
for (int y=0; y<32; y++)
{
int yy = y+map_left_y;
for (int x=0; xX = 128;
Link->Y = 88;
Link->InputLeft = false;
Link->InputRight = false;
Link->InputUp = false;
Link->InputDown = false;
Link->InputA = false;
Link->InputB = false;
Link->InputL = false;
Link->InputR = false;
Link->InputStart = false;
Link->InputEx1 = false;
Link->InputEx2 = false;
Link->InputEx3 = false;
Link->InputEx4 = false;
}

bool ComboSolid(int x, int y, int map, int curScr)
{
bool solid = false;

int cx = (x>>4)&15;
int cy = (y>>4)%11;

int scr = curScr;
scr += ( x>>8 );
scr += ( Floor(y/176)<GetComboSolid( map, scr, (cy<>4 );
scr += ( Floor(ty/11)<GetComboData( map, scr, (cy<DrawTile(1, 0, 0, BackgroundTiles, 1, 11, 0, 256, 176, 0, 0, 0, 0, false, 128);

//Note that we're pixel doubling for performance, later this should be an option
//for people with fast computers.
for (int x=0; x<scrWidth; x+=2)
{
int mx = mapX;
int my = mapY;

//compute the screen ray from the x position and camera paramters.
float sx = (2 * x/scrWidth) - 1;
float rayDirX = camDirX + planeX*sx;
float rayDirY = camDirY + planeY*sx;

//compute the DDA parameters, will detail in some documentation later.
float rXX = rayDirX*rayDirX;
float rYY = rayDirY*rayDirY;

float deltaDistX = 32767;
float deltaDistY = 32767;
if ( rXX != 0 ) deltaDistX = Sqrt( 1.0 + rYY/rXX );
if ( rYY != 0 ) deltaDistY = Sqrt( 1.0 + rXX/rYY );

int stepX;
int stepY;
float sideDistX;
float sideDistY;
if ( rayDirX < 0 )
{ stepX = -1; sideDistX = (camX - mx) * deltaDistX; }
else
{ stepX = 1; sideDistX = (mx + 1.0 - camX)*deltaDistX; }

if ( rayDirY < 0 )
{ stepY = -1; sideDistY = (camY - my)*deltaDistY; }
else
{ stepY = 1; sideDistY = (my + 1.0 - camY)*deltaDistY; }

//now that we have the DDA parameters, actually walk the ray and find the closest intersecting wall.
int hit = 0;
int iter = 0;
int side;
while (hit == 0 && iter < MAX_STEPS)
{
if ( sideDistX < sideDistY )
{
sideDistX += deltaDistX;
mx += stepX;
side = 0;
if ( mx

= maxTileX )
break;
}
else
{
sideDistY += deltaDistY;
my += stepY;
side = 1;
if ( my

= maxTileY )
break;
}
hit = map_combo[ ((my-map_left_y)<<5)+(mx-map_left_x) ];
iter++;
}

//if we've hit something we figure out the column to render.
if ( hit )
{
float wallDist;
int u;
//compute the distance to the wall and the texture coordinate.
if ( side == 0 )
{
wallDist = (mx - camX + (1 - stepX)*0.5 ) / rayDirX;
u = camY + rayDirY*wallDist;
}
else
{
wallDist = (my - camY + (1 - stepY)*0.5 ) / rayDirY;
u = camX + rayDirX*wallDist;
}
//we use 16x16 tiles for textures.
u = Floor( u*16 )&15;

//compute the wall height.
wallDist = Abs(wallDist);
float height = 256.0 / wallDist;

//compute the column dimensions.
int y0 = Floor(cenY - height*0.5);
int y1 = Floor(cenY + height*0.5);

if ( y0 240 ) y1 = 240;

//shading depends on the "side" (so some faces are darker then others)
//and the distance.
int shading = Floor(wallDist/2) + side;
if ( shading > 7 ) shading = 7;
//textures are hard to author for this, but we compute the final tile we need for this column based on
//the texture coordinate and shading.
int tile = Game->ComboTile(hit) + u+(shading*20);

//Quad() doesn't work well here, but DrawTile() does since we're rendering a screen aligned quad.
Screen->DrawTile(1, x, y0, tile, 1, 1, 0, 2, (y1-y0), 0, 0, 0, 0, false, 128);
}
}
}
}

One review of this entry
5.0 rating based on 1 rating
5.0 rating based on 1 rating

Recent Resources