Raycaster
SubmittedDecember 21, 2016
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.
/////////////////////////////////////////////////////
/////// 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