Advertisement
ZoriaRPG

Zelda Classic Raycaster

Oct 1st, 2018
189
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 8.82 KB | None | 0 0
  1. /////////////////////////////////////////////////////
  2. /////// Raycasting Engine Script, version 0.2 ///////
  3. /////// by blue_knight                        ///////
  4. /////////////////////////////////////////////////////
  5. /////////////////////////////////////////////////////
  6. // This is a fun little script
  7. // that renders the current screens in first person
  8. // using raycasting and column rendering.
  9. // X, Y is on the horizontal plane.
  10. // Z is up in 3D space.
  11. // This script has no set limits, except the map limit
  12. // 16x8 screens.
  13. /////////////////////////////////////////////////////
  14.  
  15. const int scrWidth  = 256;
  16. const int scrHeight = 176;
  17.  
  18. ffc script Raycaster
  19. {
  20.     /////////////////////////////
  21.     // startX     : start X position in virtual space.
  22.     // startY     : start Y position in virtual space.
  23.     // BackgroundTiles : Tile index for the background.
  24.     /////////////////////////////
  25.     void run(int startX, int startY, int BackgroundTiles)
  26.     {
  27.         //use virtual coordinates for Link's position.
  28.         //since he is not displayed, we don't bother keeping local coordinates, so Link's position is forced to the center.
  29.         float camPos_X = startX;
  30.         float camPos_Y = startY;
  31.         float camDir_X;
  32.         float camDir_Y;
  33.         float camYaw = 0;
  34.         float moveSpeed = 1.0;
  35.         float playerRadius = 16;
  36.        
  37.         //ginormous array...
  38.         int map_combo[1024];    //holds the 32x32 area centered on the camera.
  39.         int map_left_x=0;
  40.         int map_left_y=0;
  41.        
  42.         int map_cenX = 0;
  43.         int map_cenY = 0;
  44.        
  45.         int curScreen = Game->GetCurScreen();
  46.         int curMap = Game->GetCurMap();
  47.    
  48.         while (true)
  49.         {
  50.             //update the movement.
  51.             if ( Link->InputLeft )
  52.                 camYaw-=2;
  53.             else if ( Link->InputRight )
  54.                 camYaw+=2;
  55.                
  56.             if (camYaw < 0 )
  57.                 camYaw += 360;
  58.             else if ( camYaw >= 360 )
  59.                 camYaw -= 360;
  60.            
  61.             camDir_X = Cos(camYaw);
  62.             camDir_Y = Sin(camYaw);
  63.            
  64.             float newPosX = camPos_X;
  65.             float newPosY = camPos_Y;
  66.             float travelDirX;
  67.             float travelDirY;
  68.             bool bCollisionNeeded = false;
  69.             if ( Link->InputUp )
  70.             {
  71.                 newPosX = newPosX + camDir_X*moveSpeed;
  72.                 newPosY = newPosY + camDir_Y*moveSpeed;
  73.                 bCollisionNeeded = true;
  74.                
  75.                 travelDirX = camDir_X;
  76.                 travelDirY = camDir_Y;
  77.             }
  78.             else if ( Link->InputDown )
  79.             {
  80.                 newPosX = newPosX - camDir_X*moveSpeed;
  81.                 newPosY = newPosY - camDir_Y*moveSpeed;
  82.                 bCollisionNeeded = true;
  83.                
  84.                 travelDirX = -camDir_X;
  85.                 travelDirY = -camDir_Y;
  86.             }
  87.            
  88.             //Super basic collision for now.
  89.             //If we're intersecting a solid combo after moving, we revert the move.
  90.             //note that we actually check ahead in the direction of travel so we don't get too close to
  91.             //the wall.
  92.             if ( bCollisionNeeded )
  93.             {
  94.                 if ( ComboSolid( Floor(newPosX+travelDirX*playerRadius), Floor(newPosY+travelDirY*playerRadius), curMap, curScreen ) )
  95.                 {
  96.                     newPosX = camPos_X;
  97.                     newPosY = camPos_Y;
  98.                 }
  99.             }
  100.             camPos_X = newPosX;
  101.             camPos_Y = newPosY;
  102.            
  103.             //Read the area around the camera into an array
  104.             //whenever we change cells.
  105.             //This way we don't have to call GetComboFromTileIndex() or Game->GetComboData()
  106.             //up to 16 times per ray.
  107.             //This improves our performance by 40% or more.
  108.             int mx = camPos_X>>4;
  109.             int my = camPos_Y>>4;
  110.             if ( mx != map_cenX || my != map_cenY )
  111.             {
  112.                 map_cenX = mx;
  113.                 map_cenY = my;
  114.                 map_left_x = Max(mx-16,0);
  115.                 map_left_y = Max(my-16,0);
  116.                 for (int y=0; y<32; y++)
  117.                 {
  118.                     int yy = y+map_left_y;
  119.                     for (int x=0; x<32; x++)
  120.                     {
  121.                         int xx = x+map_left_x;
  122.                         map_combo[y*32+x] = GetComboFromTileIndex(xx, yy, curMap, curScreen);
  123.                     }
  124.                 }
  125.             }
  126.            
  127.             //Finally draw the world.
  128.             DrawWorld( camDir_X, camDir_Y, camPos_X, camPos_Y, BackgroundTiles, map_combo, map_left_x, map_left_y );
  129.            
  130.             DisableInput();
  131.            
  132.             Waitframe();
  133.         }
  134.     }
  135.    
  136.     void DisableInput()
  137.     {
  138.         Link->X = 128;
  139.         Link->Y = 88;
  140.         Link->InputLeft = false;
  141.         Link->InputRight = false;
  142.         Link->InputUp = false;
  143.         Link->InputDown = false;
  144.         Link->InputA = false;
  145.         Link->InputB = false;
  146.         Link->InputL = false;
  147.         Link->InputR = false;
  148.         Link->InputStart = false;
  149.         Link->InputEx1 = false;
  150.         Link->InputEx2 = false;
  151.         Link->InputEx3 = false;
  152.         Link->InputEx4 = false;
  153.     }
  154.    
  155.     bool ComboSolid(int x, int y, int map, int curScr)
  156.     {
  157.         bool solid = false;
  158.        
  159.         int cx = (x>>4)&15;
  160.         int cy = (y>>4)%11;
  161.        
  162.         int scr = curScr;
  163.         scr += ( x>>8 );
  164.         scr += ( Floor(y/176)<<4 );
  165.        
  166.         int s = Game->GetComboSolid( map, scr, (cy<<4)+cx );
  167.         if ( s != 0 )
  168.             solid = true;
  169.            
  170.         return solid;
  171.     }
  172.    
  173.     int GetComboFromTileIndex(int tx, int ty, int map, int curScr)
  174.     {
  175.         int cx = tx&15;
  176.         int cy = ty%11;
  177.        
  178.         int scr = curScr;
  179.         scr += ( tx>>4 );
  180.         scr += ( Floor(ty/11)<<4 );
  181.        
  182.         return Game->GetComboData( map, scr, (cy<<4)+cx );
  183.     }
  184.        
  185.     //draw the "3D" world.
  186.     void DrawWorld(float camDirX, float camDirY, float cX, float cY, int BackgroundTiles, int map_combo, int map_left_x, int map_left_y)
  187.     {
  188.         float cenY = scrHeight*0.5;
  189.        
  190.         //This camera has no pitch, though looking up and down can be faked later.
  191.         //Anyway, the camera plane is perpendicular to the camera direction,
  192.         //so a simple 2D "perp product" is good enough [i.e. P(x,y) = (-Dy,Dx)]
  193.         float planeX = -camDirY;
  194.         float planeY =  camDirX;
  195.                
  196.         //we do raycasting in "map space", where each tile = 1 unit.
  197.         float camX = cX/16;
  198.         float camY = cY/16;
  199.        
  200.         int mapX = Floor(camX);
  201.         int mapY = Floor(camY);
  202.        
  203.         //maximum extents, 8x8 screens for now.
  204.         int maxTileX = map_left_x+32;
  205.         int maxTileY = map_left_y+32;
  206.        
  207.         //maximum number of raycasting steps. Setting this lower
  208.         //will improve performance, setting it higher allows more distant walls to be rendered.
  209.         int MAX_STEPS = 16;
  210.        
  211.         //Draw the background.
  212.         Screen->DrawTile(1, 0, 0, BackgroundTiles, 1, 11, 0, 256, 176, 0, 0, 0, 0, false, 128);
  213.                
  214.         //Note that we're pixel doubling for performance, later this should be an option
  215.         //for people with fast computers.
  216.         for (int x=0; x<scrWidth; x+=2)
  217.         {
  218.             int mx = mapX;
  219.             int my = mapY;
  220.            
  221.             //compute the screen ray from the x position and camera paramters.
  222.             float sx = (2 * x/scrWidth) - 1;
  223.             float rayDirX = camDirX + planeX*sx;
  224.             float rayDirY = camDirY + planeY*sx;
  225.            
  226.             //compute the DDA parameters, will detail in some documentation later.
  227.             float rXX = rayDirX*rayDirX;
  228.             float rYY = rayDirY*rayDirY;
  229.            
  230.             float deltaDistX = 32767;
  231.             float deltaDistY = 32767;
  232.             if ( rXX != 0 ) deltaDistX = Sqrt( 1.0 + rYY/rXX );
  233.             if ( rYY != 0 ) deltaDistY = Sqrt( 1.0 + rXX/rYY );
  234.        
  235.             int stepX;
  236.             int stepY;
  237.             float sideDistX;
  238.             float sideDistY;
  239.             if ( rayDirX < 0 )
  240.             { stepX = -1; sideDistX = (camX - mx) * deltaDistX; }
  241.             else
  242.             { stepX = 1; sideDistX = (mx + 1.0 - camX)*deltaDistX; }
  243.            
  244.             if ( rayDirY < 0 )
  245.             { stepY = -1; sideDistY = (camY - my)*deltaDistY; }
  246.             else
  247.             { stepY = 1; sideDistY = (my + 1.0 - camY)*deltaDistY; }
  248.            
  249.             //now that we have the DDA parameters, actually walk the ray and find the closest intersecting wall.
  250.             int hit = 0;
  251.             int iter = 0;
  252.             int side;
  253.             while (hit == 0 && iter < MAX_STEPS)
  254.             {
  255.                 if ( sideDistX < sideDistY )
  256.                 {
  257.                     sideDistX += deltaDistX;
  258.                     mx += stepX;
  259.                     side = 0;
  260.                     if ( mx < map_left_x || mx >= maxTileX )
  261.                         break;
  262.                 }
  263.                 else
  264.                 {
  265.                     sideDistY += deltaDistY;
  266.                     my += stepY;
  267.                     side = 1;
  268.                     if ( my < map_left_y || my >= maxTileY )
  269.                         break;
  270.                 }
  271.                 hit = map_combo[ ((my-map_left_y)<<5)+(mx-map_left_x) ];
  272.                 iter++;
  273.             }
  274.            
  275.             //if we've hit something we figure out the column to render.
  276.             if ( hit )
  277.             {
  278.                 float wallDist;
  279.                 int u;
  280.                 //compute the distance to the wall and the texture coordinate.
  281.                 if ( side == 0 )
  282.                 {
  283.                     wallDist = (mx - camX + (1 - stepX)*0.5 ) / rayDirX;
  284.                     u = camY + rayDirY*wallDist;
  285.                 }
  286.                 else
  287.                 {
  288.                     wallDist = (my - camY + (1 - stepY)*0.5 ) / rayDirY;
  289.                     u = camX + rayDirX*wallDist;
  290.                 }
  291.                 //we use 16x16 tiles for textures.
  292.                 u = Floor( u*16 )&15;
  293.                
  294.                 //compute the wall height.
  295.                 wallDist = Abs(wallDist);
  296.                 float height = 256.0 / wallDist;
  297.                
  298.                 //compute the column dimensions.
  299.                 int y0 = Floor(cenY - height*0.5);
  300.                 int y1 = Floor(cenY + height*0.5);
  301.                
  302.                 if ( y0 < -96 ) y0 = -96;
  303.                 if ( y1 > 240 ) y1 = 240;
  304.                
  305.                 //shading depends on the "side" (so some faces are darker then others)
  306.                 //and the distance.
  307.                 int shading = Floor(wallDist/2) + side;
  308.                 if ( shading > 7 ) shading = 7;
  309.                 //textures are hard to author for this, but we compute the final tile we need for this column based on
  310.                 //the texture coordinate and shading.
  311.                 int tile = Game->ComboTile(hit) + u+(shading*20);
  312.  
  313.                 //Quad() doesn't work well here, but DrawTile() does since we're rendering a screen aligned quad.
  314.                 Screen->DrawTile(1, x, y0, tile, 1, 1, 0, 2, (y1-y0), 0, 0, 0, 0, false, 128);
  315.             }
  316.         }
  317.     }
  318. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement