Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- //********************************************************************//
- //* F. PERMADI MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE *//
- //* SUITABILITY OF *//
- //* THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT *//
- //* LIMITED *//
- //* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *//
- //* PARTICULAR PURPOSE *//
- //********************************************************************//
- import java.awt.*;
- import java.applet.*;
- //*******************************************************************//
- //* main class
- //*******************************************************************//
- public class Rayc extends Applet implements Runnable
- {
- // this is Java's stuff
- Thread fThread;
- // size of tile (wall height)
- static final int TILE_SIZE = 64;
- static final int WALL_HEIGHT = 64;
- static final int PROJECTIONPLANEWIDTH = 320;
- static final int PROJECTIONPLANEHEIGHT = 200;
- static final int ANGLE60 = PROJECTIONPLANEWIDTH;
- static final int ANGLE30 = (ANGLE60/2);
- static final int ANGLE15 = (ANGLE30/2);
- static final int ANGLE90 = (ANGLE30*3);
- static final int ANGLE180 = (ANGLE90*2);
- static final int ANGLE270 = (ANGLE90*3);
- static final int ANGLE360 = (ANGLE60*6);
- static final int ANGLE0 = 0;
- static final int ANGLE5 = (ANGLE30/6);
- static final int ANGLE10 = (ANGLE5*2);
- // trigonometric tables
- float fSinTable[];
- float fISinTable[];
- float fCosTable[];
- float fICosTable[];
- float fTanTable[];
- float fITanTable[];
- float fFishTable[];
- float fXStepTable[];
- float fYStepTable[];
- // offscreen buffer
- Image fOffscreenImage;
- Graphics fOffscreenGraphics;
- // player's attributes
- int fPlayerX = 100;
- int fPlayerY = 160;
- int fPlayerArc = ANGLE0;
- int fPlayerDistanceToTheProjectionPlane = 277;
- int fPlayerHeight =32;
- int fPlayerSpeed = 16;
- int fProjectionPlaneYCenter = PROJECTIONPLANEHEIGHT/2;
- // the following variables are used to keep the player coordinate in the overhead map
- int fPlayerMapX, fPlayerMapY, fMinimapWidth;
- // movement flag
- boolean fKeyUp=false;
- boolean fKeyDown=false;
- boolean fKeyLeft=false;
- boolean fKeyRight=false;
- // 2 dimensional map
- byte fMap[];
- static final byte W=1; // wall
- static final byte O=0; // opening
- static final int MAP_WIDTH=12;
- static final int MAP_HEIGHT=12;
- //*******************************************************************//
- //* Convert arc to radian
- //*******************************************************************//
- float arcToRad(float arcAngle)
- {
- return ((float)(arcAngle*Math.PI)/(float)ANGLE180);
- }
- //*******************************************************************//
- //* Create tigonometric values to make the program runs faster.
- //*******************************************************************//
- public void createTables()
- {
- int i;
- float radian;
- fSinTable = new float[ANGLE360+1];
- fISinTable = new float[ANGLE360+1];
- fCosTable = new float[ANGLE360+1];
- fICosTable = new float[ANGLE360+1];
- fTanTable = new float[ANGLE360+1];
- fITanTable = new float[ANGLE360+1];
- fFishTable = new float[ANGLE60+1];
- fXStepTable = new float[ANGLE360+1];
- fYStepTable = new float[ANGLE360+1];
- for (i=0; i<=ANGLE360;i++)
- {
- // get the radian value (the last addition is to avoid division by 0, try removing
- // that and you'll see a hole in the wall when a ray is at 0, 90, 180, or 270 degree)
- radian = arcToRad(i) + (float)(0.0001);
- fSinTable[i]=(float)Math.sin(radian);
- fISinTable[i]=(1.0F/(fSinTable[i]));
- fCosTable[i]=(float)Math.cos(radian);
- fICosTable[i]=(1.0F/(fCosTable[i]));
- fTanTable[i]=(float)Math.tan(radian);
- fITanTable[i]=(1.0F/fTanTable[i]);
- // you can see that the distance between xi is the same
- // if we know the angle
- // _____|_/next xi______________
- // |
- // ____/|next xi_________ slope = tan = height / dist between xi's
- // / |
- // __/__|_________ dist between xi = height/tan where height=tile size
- // old xi|
- // distance between xi = x_step[view_angle];
- //
- //
- // facine left
- // facing left
- if (i>=ANGLE90 && i<ANGLE270)
- {
- fXStepTable[i] = (float)(TILE_SIZE/fTanTable[i]);
- if (fXStepTable[i]>0)
- fXStepTable[i]=-fXStepTable[i];
- }
- // facing right
- else
- {
- fXStepTable[i] = (float)(TILE_SIZE/fTanTable[i]);
- if (fXStepTable[i]<0)
- fXStepTable[i]=-fXStepTable[i];
- }
- // FACING DOWN
- if (i>=ANGLE0 && i<ANGLE180)
- {
- fYStepTable[i] = (float)(TILE_SIZE*fTanTable[i]);
- if (fYStepTable[i]<0)
- fYStepTable[i]=-fYStepTable[i];
- }
- // FACING UP
- else
- {
- fYStepTable[i] = (float)(TILE_SIZE*fTanTable[i]);
- if (fYStepTable[i]>0)
- fYStepTable[i]=-fYStepTable[i];
- }
- }
- for (i=-ANGLE30; i<=ANGLE30; i++)
- {
- radian = arcToRad(i);
- // we don't have negative angle, so make it start at 0
- // this will give range 0 to 320
- fFishTable[i+ANGLE30] = (float)(1.0F/Math.cos(radian));
- }
- // CERATE A SIMPLE MAP
- byte[] map=
- {
- W,W,W,W,W,W,W,W,W,W,W,W,
- W,O,O,O,O,O,O,O,O,O,O,W,
- W,O,O,O,O,O,O,O,O,O,O,W,
- W,O,O,O,O,O,O,O,W,O,O,W,
- W,O,O,W,O,W,O,O,W,O,O,W,
- W,O,O,W,O,W,W,O,W,O,O,W,
- W,O,O,W,O,O,W,O,W,O,O,W,
- W,O,O,O,W,O,W,O,W,O,O,W,
- W,O,O,O,W,O,W,O,W,O,O,W,
- W,O,O,O,W,W,W,O,W,O,O,W,
- W,O,O,O,O,O,O,O,O,O,O,W,
- W,W,W,W,W,W,W,W,W,W,W,W
- };
- fMap=map;
- }
- //*******************************************************************//
- //* Called when program starts
- //*******************************************************************//
- public void start()
- {
- createTables();
- fThread=new Thread(this);
- fThread.start();
- }
- //*******************************************************************//
- //* Running thread
- //*******************************************************************//
- public void run()
- {
- requestFocus();
- // create offscreen buffer
- fOffscreenImage=createImage(size().width, size().height);
- fOffscreenGraphics=fOffscreenImage.getGraphics();
- while(true)
- {
- // rotate left
- if (fKeyLeft)
- {
- if ((fPlayerArc-=ANGLE10)<ANGLE0)
- fPlayerArc+=ANGLE360;
- }
- // rotate right
- else if (fKeyRight)
- {
- if ((fPlayerArc+=ANGLE10)>=ANGLE360)
- fPlayerArc-=ANGLE360;
- }
- // _____ _
- // |\ arc |
- // | \ y
- // | \ |
- // -
- // |--x--|
- //
- // sin(arc)=y/diagonal
- // cos(arc)=x/diagonal where diagonal=speed
- float playerXDir=fCosTable[fPlayerArc];
- float playerYDir=fSinTable[fPlayerArc];
- // move forward
- if (fKeyUp)
- {
- fPlayerX+=(int)(playerXDir*fPlayerSpeed);
- fPlayerY+=(int)(playerYDir*fPlayerSpeed);
- }
- // move backward
- else if (fKeyDown)
- {
- fPlayerX-=(int)(playerXDir*fPlayerSpeed);
- fPlayerY-=(int)(playerYDir*fPlayerSpeed);
- }
- render();
- try
- {
- Thread.sleep(50);
- }
- catch (Exception sleepProblem)
- {
- showStatus("Sleep problem");
- }
- }
- }
- //*******************************************************************//
- //* Draw background image
- //*******************************************************************//
- public void drawBackground()
- {
- // sky
- int c=25;
- int r;
- for (r=0; r<PROJECTIONPLANEHEIGHT/2; r+=10)
- {
- fOffscreenGraphics.setColor(new Color(c, 125, 225));
- fOffscreenGraphics.fillRect(0, r, PROJECTIONPLANEWIDTH, 10);
- c+=20;
- }
- // ground
- c=22;
- for (; r<PROJECTIONPLANEHEIGHT; r+=15)
- {
- fOffscreenGraphics.setColor(new Color(c, 20, 20));
- fOffscreenGraphics.fillRect(0, r, PROJECTIONPLANEWIDTH, 15);
- c+=15;
- }
- }
- //*******************************************************************//
- //* Draw map on the right side
- //*******************************************************************//
- public void drawOverheadMap()
- {
- fMinimapWidth=5;
- for (int u=0; u<MAP_WIDTH; u++)
- {
- for (int v=0; v<MAP_HEIGHT; v++)
- {
- if (fMap[v*MAP_WIDTH+u]==W)
- {
- fOffscreenGraphics.setColor(Color.cyan);
- }
- else
- {
- fOffscreenGraphics.setColor(Color.black);
- }
- fOffscreenGraphics.fillRect(PROJECTIONPLANEWIDTH+(u*fMinimapWidth),
- (v*fMinimapWidth), fMinimapWidth, fMinimapWidth);
- }
- }
- fPlayerMapX=PROJECTIONPLANEWIDTH+(int)(((float)fPlayerX/(float)TILE_SIZE) * fMinimapWidth);
- fPlayerMapY=(int)(((float)fPlayerY/(float)TILE_SIZE) * fMinimapWidth);
- }
- //*******************************************************************//
- //* Draw ray on the overhead map (for illustartion purpose)
- //* This is not part of the ray-casting process
- //*******************************************************************//
- public void drawRayOnOverheadMap(float x, float y)
- {
- fOffscreenGraphics.setColor(Color.yellow);
- // draw line from the player position to the position where the ray
- // intersect with wall
- fOffscreenGraphics.drawLine(fPlayerMapX, fPlayerMapY,
- (int)(PROJECTIONPLANEWIDTH+((float)(x*fMinimapWidth)/(float)TILE_SIZE)),
- (int)(((float)(y*fMinimapWidth)/(float)TILE_SIZE)));
- // draw a red line indication the player's direction
- fOffscreenGraphics.setColor(Color.red);
- fOffscreenGraphics.drawLine(fPlayerMapX, fPlayerMapY,
- (int)(fPlayerMapX+fCosTable[fPlayerArc]*10),
- (int)(fPlayerMapY+fSinTable[fPlayerArc]*10));
- }
- //*******************************************************************//
- //* Renderer
- //*******************************************************************//
- public void render()
- {
- drawBackground();
- drawOverheadMap();
- int verticalGrid; // horizotal or vertical coordinate of intersection
- int horizontalGrid; // theoritically, this will be multiple of TILE_SIZE
- // , but some trick did here might cause
- // the values off by 1
- int distToNextVerticalGrid; // how far to the next bound (this is multiple of
- int distToNextHorizontalGrid; // tile size)
- float xIntersection; // x and y intersections
- float yIntersection;
- float distToNextXIntersection;
- float distToNextYIntersection;
- int xGridIndex; // the current cell that the ray is in
- int yGridIndex;
- float distToVerticalGridBeingHit; // the distance of the x and y ray intersections from
- float distToHorizontalGridBeingHit; // the viewpoint
- int castArc, castColumn;
- castArc = fPlayerArc;
- // field of view is 60 degree with the point of view (player's direction in the middle)
- // 30 30
- // ^
- // \ | /
- // \|/
- // v
- // we will trace the rays starting from the leftmost ray
- castArc-=ANGLE30;
- // wrap around if necessary
- if (castArc < 0)
- {
- castArc=ANGLE360 + castArc;
- }
- for (castColumn=0; castColumn<PROJECTIONPLANEWIDTH; castColumn+=5)
- {
- // ray is between 0 to 180 degree (1st and 2nd quadrant)
- // ray is facing down
- if (castArc > ANGLE0 && castArc < ANGLE180)
- {
- // truncuate then add to get the coordinate of the FIRST grid (horizontal
- // wall) that is in front of the player (this is in pixel unit)
- // ROUND DOWN
- horizontalGrid = (fPlayerY/TILE_SIZE)*TILE_SIZE + TILE_SIZE;
- // compute distance to the next horizontal wall
- distToNextHorizontalGrid = TILE_SIZE;
- float xtemp = fITanTable[castArc]*(horizontalGrid-fPlayerY);
- // we can get the vertical distance to that wall by
- // (horizontalGrid-GLplayerY)
- // we can get the horizontal distance to that wall by
- // 1/tan(arc)*verticalDistance
- // find the x interception to that wall
- xIntersection = xtemp + fPlayerX;
- }
- // else, the ray is facing up
- else
- {
- horizontalGrid = (fPlayerY/TILE_SIZE)*TILE_SIZE;
- distToNextHorizontalGrid = -TILE_SIZE;
- float xtemp = fITanTable[castArc]*(horizontalGrid - fPlayerY);
- xIntersection = xtemp + fPlayerX;
- horizontalGrid--;
- }
- // LOOK FOR HORIZONTAL WALL
- if (castArc==ANGLE0 || castArc==ANGLE180)
- {
- distToHorizontalGridBeingHit=9999999F;//Float.MAX_VALUE;
- }
- // else, move the ray until it hits a horizontal wall
- else
- {
- distToNextXIntersection = fXStepTable[castArc];
- while (true)
- {
- xGridIndex = (int)(xIntersection/TILE_SIZE);
- // in the picture, yGridIndex will be 1
- yGridIndex = (horizontalGrid/TILE_SIZE);
- if ((xGridIndex>=MAP_WIDTH) ||
- (yGridIndex>=MAP_HEIGHT) ||
- xGridIndex<0 || yGridIndex<0)
- {
- distToHorizontalGridBeingHit = Float.MAX_VALUE;
- break;
- }
- else if ((fMap[yGridIndex*MAP_WIDTH+xGridIndex])!=O)
- {
- distToHorizontalGridBeingHit = (xIntersection-fPlayerX)*fICosTable[castArc];
- break;
- }
- // else, the ray is not blocked, extend to the next block
- else
- {
- xIntersection += distToNextXIntersection;
- horizontalGrid += distToNextHorizontalGrid;
- }
- }
- }
- // FOLLOW X RAY
- if (castArc < ANGLE90 || castArc > ANGLE270)
- {
- verticalGrid = TILE_SIZE + (fPlayerX/TILE_SIZE)*TILE_SIZE;
- distToNextVerticalGrid = TILE_SIZE;
- float ytemp = fTanTable[castArc]*(verticalGrid - fPlayerX);
- yIntersection = ytemp + fPlayerY;
- }
- // RAY FACING LEFT
- else
- {
- verticalGrid = (fPlayerX/TILE_SIZE)*TILE_SIZE;
- distToNextVerticalGrid = -TILE_SIZE;
- float ytemp = fTanTable[castArc]*(verticalGrid - fPlayerX);
- yIntersection = ytemp + fPlayerY;
- verticalGrid--;
- }
- // LOOK FOR VERTICAL WALL
- if (castArc==ANGLE90||castArc==ANGLE270)
- {
- distToVerticalGridBeingHit = 9999999;//Float.MAX_VALUE;
- }
- else
- {
- distToNextYIntersection = fYStepTable[castArc];
- while (true)
- {
- // compute current map position to inspect
- xGridIndex = (verticalGrid/TILE_SIZE);
- yGridIndex = (int)(yIntersection/TILE_SIZE);
- if ((xGridIndex>=MAP_WIDTH) ||
- (yGridIndex>=MAP_HEIGHT) ||
- xGridIndex<0 || yGridIndex<0)
- {
- distToVerticalGridBeingHit = Float.MAX_VALUE;
- break;
- }
- else if ((fMap[yGridIndex*MAP_WIDTH+xGridIndex])!=O)
- {
- distToVerticalGridBeingHit =(yIntersection-fPlayerY)*fISinTable[castArc];
- break;
- }
- else
- {
- yIntersection += distToNextYIntersection;
- verticalGrid += distToNextVerticalGrid;
- }
- }
- }
- // DRAW THE WALL SLICE
- float scaleFactor;
- float dist;
- int topOfWall; // used to compute the top and bottom of the sliver that
- int bottomOfWall; // will be the staring point of floor and ceiling
- // determine which ray strikes a closer wall.
- // if yray distance to the wall is closer, the yDistance will be shorter than
- // the xDistance
- if (distToHorizontalGridBeingHit < distToVerticalGridBeingHit)
- {
- // the next function call (drawRayOnMap()) is not a part of raycating rendering part,
- // it just draws the ray on the overhead map to illustrate the raycasting process
- drawRayOnOverheadMap(xIntersection, horizontalGrid);
- dist=distToHorizontalGridBeingHit;
- fOffscreenGraphics.setColor(Color.gray);
- }
- // else, we use xray instead (meaning the vertical wall is closer than
- // the horizontal wall)
- else
- {
- // the next function call (drawRayOnMap()) is not a part of raycating rendering part,
- // it just draws the ray on the overhead map to illustrate the raycasting process
- drawRayOnOverheadMap(verticalGrid, yIntersection);
- dist=distToVerticalGridBeingHit;
- fOffscreenGraphics.setColor(Color.darkGray);
- }
- // correct distance (compensate for the fishbown effect)
- dist /= fFishTable[castColumn];
- // projected_wall_height/wall_height = fPlayerDistToProjectionPlane/dist;
- int projectedWallHeight=(int)(WALL_HEIGHT*(float)fPlayerDistanceToTheProjectionPlane/dist);
- bottomOfWall = fProjectionPlaneYCenter+(int)(projectedWallHeight*0.5F);
- topOfWall = PROJECTIONPLANEHEIGHT-bottomOfWall;
- if (bottomOfWall>=PROJECTIONPLANEHEIGHT)
- bottomOfWall=PROJECTIONPLANEHEIGHT-1;
- //fOffscreenGraphics.drawLine(castColumn, topOfWall, castColumn, bottomOfWall);
- fOffscreenGraphics.fillRect(castColumn, topOfWall, 5, projectedWallHeight);
- // TRACE THE NEXT RAY
- castArc+=5;
- if (castArc>=ANGLE360)
- castArc-=ANGLE360;
- }
- // blit to screen
- paint(getGraphics());
- }
- //*******************************************************************//
- //* Called when leaving the page.
- //*******************************************************************//
- public void stop()
- {
- if((fThread != null) && fThread.isAlive())
- {
- fThread.stop();
- fThread = null;
- }
- }
- //*******************************************************************//
- //* Called everytime applet need painting or whenever repaint is
- //* called.
- //*******************************************************************//
- public void paint(Graphics g)
- {
- if (fOffscreenImage!=null)
- g.drawImage(fOffscreenImage, 0, 0, this);
- }
- //*******************************************************************//
- //* Detect keypress
- //*******************************************************************//
- public boolean keyDown(Event evt, int key)
- {
- switch (key)
- {
- case Event.UP:
- case 'i':
- case 'I':
- fKeyUp=true;
- break;
- case Event.DOWN:
- case 'k':
- case 'K':
- fKeyDown=true;
- break;
- case Event.LEFT:
- case 'j':
- case 'J':
- fKeyLeft=true;
- break;
- case Event.RIGHT:
- case 'l':
- case 'L':
- fKeyRight=true;
- break;
- default:
- }
- return true;
- }
- //*******************************************************************//
- //* Detect key release
- //*******************************************************************//
- public boolean keyUp(Event evt, int key)
- {
- switch (key)
- {
- case Event.UP:
- case 'i':
- case 'I':
- fKeyUp=false;
- break;
- case Event.DOWN:
- case 'k':
- case 'K':
- fKeyDown=false;
- break;
- case Event.LEFT:
- case 'j':
- case 'J':
- fKeyLeft=false;
- break;
- case Event.RIGHT:
- case 'l':
- case 'L':
- fKeyRight=false;
- break;
- default:
- }
- return true;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement