1 // Raycasting technical demo 2 3 // Modified by Raziel 4 // Original version by F. Permadi: http://www.permadi.com/java/rayc/index.html 5 6 // Improvements made by Raziel: 7 // Minimap supports scrolling 8 // Collision detection 9 // Basic HUD support 10 // Textured walls 11 // Animated walls 12 // Variable resolution 13 // Bang! 14 15 16 // Original version disclaimer: 17 //********************************************************************// 18 //* F. PERMADI MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE *// 19 //* SUITABILITY OF *// 20 //* THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT *// 21 //* LIMITED *// 22 //* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *// 23 //* PARTICULAR PURPOSE *// 24 //********************************************************************// 25 26 27 import java.awt.*; 28 import java.awt.event.*; 29 import java.applet.*; 30 31 32 public class Rayc extends Applet implements Runnable, KeyListener { 33 34 35 Thread fThread; 36 volatile boolean stop; 37 38 39 static final int TILE_SIZE = 64; 40 static final int PROJECTIONPLANEWIDTH = 320; 41 static final int PROJECTIONPLANEHEIGHT = 200; 42 43 static final int ANGLE60 = PROJECTIONPLANEWIDTH; 44 static final int ANGLE30 = (ANGLE60/2); 45 static final int ANGLE15 = (ANGLE30/2); 46 static final int ANGLE90 = (ANGLE30*3); 47 static final int ANGLE180 = (ANGLE90*2); 48 static final int ANGLE270 = (ANGLE90*3); 49 static final int ANGLE360 = (ANGLE60*6); 50 static final int ANGLE0 = 0; 51 static final int ANGLE5 = (ANGLE30/6); 52 static final int ANGLE10 = (ANGLE5*2); 53 54 // trigonometric tables 55 float fSinTable[]; 56 float fISinTable[]; 57 float fCosTable[]; 58 float fICosTable[]; 59 float fTanTable[]; 60 float fITanTable[]; 61 float fFishTable[]; 62 float fXStepTable[]; 63 float fYStepTable[]; 64 65 // offscreen buffer 66 Image fOffscreenImage; 67 Graphics fOffscreenGraphics; 68 69 Image weapon; 70 Image walls[][]; 71 Image band[][]; 72 73 // player's attributes 74 int fPlayerX = 285; 75 int fPlayerY = 160; 76 int fPlayerArc = ANGLE90; 77 int fPlayerDistanceToTheProjectionPlane = 277; 78 int fPlayerHeight =32; 79 int fPlayerSpeed = 16; 80 int fProjectionPlaneYCenter = PROJECTIONPLANEHEIGHT/2; 81 // the following variables are used to keep the player coordinate in the overhead map 82 int fPlayerMapX, fPlayerMapY; 83 int fMinimapWidth = 5; 84 int mapOffsetY = 0; 85 86 // movement flag 87 boolean fKeyUp=false; 88 boolean fKeyDown=false; 89 boolean fKeyLeft=false; 90 boolean fKeyRight=false; 91 92 final int maxRes = 10; 93 final int minRes = 1; 94 int resolution = minRes; 95 96 // For Amusement Only 97 int fBang=-1; 98 Color fBangC; 99 int fBangX, fBangY; 100 int fBand = 0; 101 java.util.Random rnd; 102 103 104 static byte fMap[] = { 105 1,1,1,1,4,1,1,1,1,1,1,1,1,1,1,1,1,1,4,1,1,1,1, 106 1,0,0,5,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,5,0,0,1, 107 1,0,0,1,0,6,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,1, 108 1,1,1,1,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,1,1,1,1, 109 1,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,1,0,0,1, 110 1,0,0,5,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,5,0,0,1, 111 1,1,1,1,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,1,1,1,1, 112 1,0,0,5,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,5,0,0,1, 113 1,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,5,0,0,1, 114 1,1,1,1,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,1,3,1,1, 115 1,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,1, 116 1,0,0,5,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,1, 117 1,1,1,1,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,1,2,1,1, 118 1,0,0,5,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,5,0,0,1, 119 1,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,1,0,0,1, 120 1,1,1,1,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,1,1,1,1, 121 1,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,1,0,0,1, 122 1,0,0,5,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,5,0,0,1, 123 1,1,1,1,0,1,0,0,0,3,0,3,0,3,0,0,0,1,0,1,1,1,1, 124 1,0,0,5,0,1,1,1,1,1,0,0,0,1,1,1,1,1,0,5,0,0,1, 125 1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1, 126 1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1, 127 1,0,0,1,0,1,1,1,1,0,0,0,0,0,1,1,1,1,0,1,0,0,1, 128 1,0,0,5,0,1,0,0,5,0,0,0,0,0,0,0,0,1,0,5,0,0,1, 129 1,1,1,1,0,1,0,0,1,0,0,0,0,0,1,0,0,1,0,1,1,1,1, 130 1,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0,1,0,5,0,0,1, 131 7,0,0,1,0,1,0,0,1,0,0,0,0,0,1,0,0,1,0,1,0,0,1, 132 1,1,1,1,4,1,1,1,1,1,6,1,0,1,1,1,1,1,4,1,1,1,1, 133 134 0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0, 135 0,0,0,0,0,0,0,0,4,0,0,0,0,0,4,0,0,0,0,0,0,0,0, 136 0,0,0,0,0,0,0,0,4,0,0,0,0,0,4,0,0,0,0,0,0,0,0, 137 138 1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1, 139 1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 140 1,1,1,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,1,1,1, 141 1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 142 1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 143 1,1,1,1,1,1,5,1,1,0,0,0,0,0,1,1,5,1,1,1,1,1,1, 144 1,0,0,0,0,0,0,1,7,0,0,0,0,0,7,1,0,0,0,0,0,0,1, 145 1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1, 146 1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 147 1,0,0,0,0,0,0,0,1,0,0,0,0,0,6,0,0,0,0,0,0,0,1, 148 1,0,0,0,0,0,0,0,6,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 149 1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 150 1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 151 1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 152 1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 153 1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 154 1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 155 1,0,0,0,0,0,0,0,1,0,0,0,0,0,6,0,0,0,0,0,0,0,1, 156 1,0,0,0,0,0,0,0,6,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 157 1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 158 1,1,1,1,1,1,1,1,2,0,0,0,0,0,2,1,1,1,1,1,1,1,1, 159 1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 160 1,0,0,0,0,0,0,0,1,0,0,0,0,0,6,0,0,0,0,0,0,0,1, 161 1,0,0,0,0,0,0,0,6,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 162 1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 163 1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 164 1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 165 1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 166 1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 167 1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 168 1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1, 169 1,0,0,0,0,0,0,0,6,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 170 1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 171 1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1, 172 173 0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,1, 0,0,0,0, 174 0,0,0,1,1,1,1,0,1,0,0,0,0,0,0,0,0,0,3, 0,0,0,0, 175 0,0,0,1,0,0,1,0,2,0,0,0,0,0,0,0,0,0,8, 0,0,0,0, 176 0,0,0,1,0,0,5,0,1,0,0,0,0,0,0,0,0,0,9, 0,0,0,0, 177 0,0,0,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,12,0,0,0,0, 178 0,0,0,1,1,1,1,1,7,0,0,0,0,0,0,0,0,0,13,0,0,0,0, 179 0,0,0,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,14,0,0,0,0, 180 0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,11,0,0,0,0, 181 0,0,0,1,0,0,1,0,2,0,0,0,0,0,0,0,0,0,10,0,0,0,0, 182 0,0,0,1,1,1,1,0,1,0,0,0,0,0,0,0,0,0,3, 0,0,0,0, 183 0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,1, 0,0,0,0, 184 185 1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1, 186 1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 187 1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 188 1,0,0,0,0,0,0,0,1,0,0,0,0,0,6,0,0,0,0,0,0,0,1, 189 1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 190 1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 191 1,0,0,0,0,0,0,0,6,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 192 1,0,0,0,0,0,0,0,1,0,0,0,0,0,2,0,0,0,0,0,0,0,1, 193 1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1, 194 1,0,0,0,0,0,0,0,2,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 195 1,0,0,0,0,0,0,0,1,0,0,0,0,0,6,0,0,0,0,0,0,0,1, 196 1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 197 1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 198 1,0,0,0,0,0,0,0,6,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 199 1,0,0,0,0,0,0,0,1,0,0,0,0,0,2,0,0,0,0,0,0,0,1, 200 1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1, 201 1,0,0,0,0,0,0,0,2,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 202 1,0,0,0,0,0,0,0,1,0,0,0,0,0,6,0,0,0,0,0,0,0,1, 203 1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 204 1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 205 1,0,0,0,0,0,0,0,6,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 206 1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 207 1,1,1,1,1,1,1,1,1,1,4,2,4,1,1,1,1,1,1,1,1,1,1 208 }; 209 210 static final int MAP_WIDTH = 23; 211 static final int MAP_HEIGHT = fMap.length/MAP_WIDTH; 212 213 214 float arcToRad(float arcAngle) { 215 return ((float)(arcAngle*Math.PI)/(float)ANGLE180); 216 } 217 218 219 // Creates tigonometric values to speed up the engine 220 public void createTables() { 221 222 int i; 223 float radian; 224 fSinTable = new float[ANGLE360+1]; 225 fISinTable = new float[ANGLE360+1]; 226 fCosTable = new float[ANGLE360+1]; 227 fICosTable = new float[ANGLE360+1]; 228 fTanTable = new float[ANGLE360+1]; 229 fITanTable = new float[ANGLE360+1]; 230 fFishTable = new float[ANGLE60+1]; 231 fXStepTable = new float[ANGLE360+1]; 232 fYStepTable = new float[ANGLE360+1]; 233 234 for (i=0; i<=ANGLE360; i++) { 235 236 // get the radian value (the last addition is to avoid division by 0, try removing 237 // that and you'll see a hole in the wall when a ray is at 0, 90, 180, or 270 degree) 238 239 radian = arcToRad(i) + (float)(0.0001); 240 fSinTable[i]=(float)Math.sin(radian); 241 fISinTable[i]=(1.0F/(fSinTable[i])); 242 fCosTable[i]=(float)Math.cos(radian); 243 fICosTable[i]=(1.0F/(fCosTable[i])); 244 fTanTable[i]=(float)Math.tan(radian); 245 fITanTable[i]=(1.0F/fTanTable[i]); 246 247 248 // you can see that the distance between xi is the same 249 // if we know the angle 250 // _____|_/next xi______________ 251 // | 252 // ____/|next xi_________ slope = tan = height / dist between xi's 253 // / | 254 // __/__|_________ dist between xi = height/tan where height=tile size 255 // old xi| 256 // distance between xi = x_step[view_angle]; 257 // 258 // 259 260 261 // facing left 262 if (i>=ANGLE90 && i<ANGLE270) { 263 fXStepTable[i] = (float)(TILE_SIZE/fTanTable[i]); 264 if (fXStepTable[i]>0) 265 fXStepTable[i]=-fXStepTable[i]; 266 } 267 268 // facing right 269 else { 270 fXStepTable[i] = (float)(TILE_SIZE/fTanTable[i]); 271 if (fXStepTable[i]<0) 272 fXStepTable[i]=-fXStepTable[i]; 273 } 274 275 // facing down 276 if (i>=ANGLE0 && i<ANGLE180) { 277 fYStepTable[i] = (float)(TILE_SIZE*fTanTable[i]); 278 if (fYStepTable[i]<0) 279 fYStepTable[i]=-fYStepTable[i]; 280 } 281 282 // facing up 283 else { 284 fYStepTable[i] = (float)(TILE_SIZE*fTanTable[i]); 285 if (fYStepTable[i]>0) 286 fYStepTable[i]=-fYStepTable[i]; 287 } 288 289 } 290 291 for (i=-ANGLE30; i<=ANGLE30; i++) { 292 radian = arcToRad(i); 293 // we don't have negative angle, so make it start at 0 294 // this will give range 0 to 320 295 fFishTable[i+ANGLE30] = (float)(1.0F/Math.cos(radian)); 296 } 297 298 } 299 300 301 void loadBitmaps() { 302 303 weapon = getImage(getClass().getResource("images/gun.png")); 304 prepareImage(weapon, this); 305 306 final int nWalls = 15; 307 308 walls = new Image[nWalls][2]; 309 walls[0][0] = null; 310 walls[0][1] = null; 311 walls[1][0] = getImage(getClass().getResource("images/cath01.png")); 312 walls[1][1] = getImage(getClass().getResource("images/cath01d.png")); 313 walls[2][0] = getImage(getClass().getResource("images/cath04.png")); 314 walls[2][1] = getImage(getClass().getResource("images/cath04d.png")); 315 walls[3][0] = getImage(getClass().getResource("images/cath03.png")); 316 walls[3][1] = getImage(getClass().getResource("images/cath03d.png")); 317 walls[4][0] = getImage(getClass().getResource("images/window.png")); 318 walls[4][1] = walls[4][0]; 319 walls[5][0] = getImage(getClass().getResource("images/door1.png")); 320 walls[5][1] = getImage(getClass().getResource("images/door1d.png")); 321 walls[6][0] = getImage(getClass().getResource("images/door2.png")); 322 walls[6][1] = walls[6][0]; 323 walls[7][0] = getImage(getClass().getResource("images/elev.png")); 324 walls[7][1] = getImage(getClass().getResource("images/elevd.png")); 325 326 327 // Band (lol) 328 band = new Image[4][2]; 329 band[0][0] = getImage(getClass().getResource("images/band00.png")); 330 band[0][1] = getImage(getClass().getResource("images/band01.png")); 331 band[1][0] = getImage(getClass().getResource("images/band10.png")); 332 band[1][1] = getImage(getClass().getResource("images/band11.png")); 333 band[2][0] = getImage(getClass().getResource("images/band20.png")); 334 band[2][1] = getImage(getClass().getResource("images/band21.png")); 335 band[3][0] = getImage(getClass().getResource("images/band30.png")); 336 band[3][1] = getImage(getClass().getResource("images/band31.png")); 337 walls[8][1] = band[0][0]; 338 walls[9][1] = band[1][0]; 339 walls[10][1] = band[2][0]; 340 walls[11][1] = band[3][0]; 341 walls[12][1] = getImage(getClass().getResource("images/rocks1.png")); 342 walls[13][1] = getImage(getClass().getResource("images/rocks2.png")); 343 walls[14][1] = getImage(getClass().getResource("images/rocks3.png")); 344 345 for (int i=1; i<nWalls; i++) { 346 prepareImage(walls[i][0], this); 347 prepareImage(walls[i][1], this); 348 } 349 for (int i=0; i<4; i++) 350 prepareImage(band[i][1], this); 351 352 } 353 354 355 public void start() { 356 this.addKeyListener(this); 357 358 createTables(); 359 loadBitmaps(); 360 361 rnd = new java.util.Random(); 362 fThread = new Thread(this); 363 fThread.start(); 364 } 365 366 367 public void run() { 368 369 requestFocus(); 370 371 // create offscreen buffer 372 fOffscreenImage=createImage(getSize().width, getSize().height); 373 fOffscreenGraphics=fOffscreenImage.getGraphics(); 374 375 bigFatLoop(); 376 377 } 378 379 380 public void stop() { 381 if((fThread != null) && fThread.isAlive()) 382 stop = true; 383 } 384 385 386 public void paint(Graphics g) { 387 if (fOffscreenImage!=null) 388 g.drawImage(fOffscreenImage, 0, 0, this); 389 } 390 391 392 public void keyPressed(KeyEvent e) { 393 394 switch (e.getKeyCode()) 395 { 396 case KeyEvent.VK_UP: 397 case KeyEvent.VK_W: 398 fKeyUp=true; 399 break; 400 case KeyEvent.VK_DOWN: 401 case KeyEvent.VK_S: 402 fKeyDown=true; 403 break; 404 case KeyEvent.VK_LEFT: 405 case KeyEvent.VK_A: 406 fKeyLeft=true; 407 break; 408 case KeyEvent.VK_RIGHT: 409 case KeyEvent.VK_D: 410 fKeyRight=true; 411 break; 412 413 case KeyEvent.VK_SPACE: 414 fBang=5; 415 fBangX = rnd.nextInt()%50 - 15; 416 fBangY = rnd.nextInt()%40 - 10; 417 fBangC = new Color(rnd.nextFloat(), rnd.nextFloat(), rnd.nextFloat()); 418 break; 419 420 case KeyEvent.VK_R: 421 if (resolution > minRes) 422 --resolution; 423 break; 424 case KeyEvent.VK_F: 425 if (resolution < maxRes) 426 ++resolution; 427 break; 428 429 default: 430 } 431 432 } 433 434 public void keyReleased(KeyEvent e) { 435 436 switch (e.getKeyCode()) 437 { 438 case KeyEvent.VK_UP: 439 case KeyEvent.VK_W: 440 fKeyUp=false; 441 break; 442 case KeyEvent.VK_DOWN: 443 case KeyEvent.VK_S: 444 fKeyDown=false; 445 break; 446 case KeyEvent.VK_LEFT: 447 case KeyEvent.VK_A: 448 fKeyLeft=false; 449 break; 450 case KeyEvent.VK_RIGHT: 451 case KeyEvent.VK_D: 452 fKeyRight=false; 453 break; 454 default: 455 } 456 457 } 458 459 public void keyTyped(KeyEvent e) {} 460 461 462 private void bigFatLoop() { 463 464 while(!stop) { 465 // rotate left 466 if (fKeyLeft) { 467 if ((fPlayerArc-=ANGLE10) < ANGLE0) 468 fPlayerArc+=ANGLE360; 469 } 470 471 // rotate right 472 else if (fKeyRight) { 473 if ((fPlayerArc+=ANGLE10) >= ANGLE360) 474 fPlayerArc-=ANGLE360; 475 } 476 477 // _____ _ 478 // |\ arc | 479 // | \ y 480 // | \ | 481 // - 482 // |--x--| 483 // 484 // sin(arc)=y/diagonal 485 // cos(arc)=x/diagonal where diagonal=speed 486 487 float playerXDir = fCosTable[fPlayerArc]; 488 float playerYDir = fSinTable[fPlayerArc]; 489 float playerXMargin = 5*playerXDir; 490 float playerYMargin = 5*playerYDir; 491 492 // move forward 493 if (fKeyUp) { 494 int fPlayerXinc = fPlayerX + (int)(playerXDir*fPlayerSpeed); 495 int fPlayerYinc = fPlayerY + (int)(playerYDir*fPlayerSpeed); 496 if (fMap[(int)((fPlayerXinc+playerXMargin)/TILE_SIZE) 497 + MAP_WIDTH * (int)((fPlayerY+playerYMargin)/TILE_SIZE)] == 0) 498 fPlayerX = fPlayerXinc; 499 if (fMap[(int)((fPlayerX+playerXMargin)/TILE_SIZE) 500 + MAP_WIDTH * (int)((fPlayerYinc+playerYMargin)/TILE_SIZE)] == 0) 501 fPlayerY = fPlayerYinc; 502 } 503 504 // move backward 505 else if (fKeyDown) { 506 int fPlayerXinc = fPlayerX - (int)(playerXDir*fPlayerSpeed); 507 int fPlayerYinc = fPlayerY - (int)(playerYDir*fPlayerSpeed); 508 if (fMap[(int)((fPlayerXinc-playerXMargin)/TILE_SIZE) 509 + MAP_WIDTH * (int)((fPlayerY-playerYMargin)/TILE_SIZE)] == 0) 510 fPlayerX = fPlayerXinc; 511 if (fMap[(int)((fPlayerX-playerXMargin)/TILE_SIZE) 512 + MAP_WIDTH * (int)((fPlayerYinc-playerYMargin)/TILE_SIZE)] == 0) 513 fPlayerY = fPlayerYinc; 514 } 515 516 // Band stuff 517 switch (fBand++) { 518 case 0: 519 walls[9][1] = band[1][0]; 520 walls[11][1] = band[3][0]; 521 break; 522 523 case 1: 524 walls[8][1] = band[0][0]; 525 walls[10][1] = band[2][0]; 526 break; 527 528 case 3: 529 walls[8][1] = band[0][1]; 530 walls[10][1] = band[2][1]; 531 break; 532 533 case 4: 534 walls[9][1] = band[1][1]; 535 walls[11][1] = band[3][1]; 536 break; 537 538 case 6: 539 fBand = -1; 540 break; 541 542 default: 543 break; 544 } 545 546 547 render(); 548 549 try { 550 Thread.sleep(50); 551 } catch (Exception sleepProblem) { 552 showStatus("Insomnia"); 553 } 554 } 555 556 } 557 558 559 560 private void drawBackground() { 561 562 /* 563 // sky 564 int c=25; 565 int r; 566 for (r=0; r<PROJECTIONPLANEHEIGHT/2; r+=10) { 567 fOffscreenGraphics.setColor(new Color(c, 125, 225)); 568 fOffscreenGraphics.fillRect(0, r, PROJECTIONPLANEWIDTH, 10); 569 c+=20; 570 } 571 572 // ground 573 c=22; 574 for (; r<PROJECTIONPLANEHEIGHT; r+=15) { 575 fOffscreenGraphics.setColor(new Color(c, 20, 20)); 576 fOffscreenGraphics.fillRect(0, r, PROJECTIONPLANEWIDTH, 15); 577 c+=15; 578 } 579 */ 580 581 fOffscreenGraphics.setColor(Color.gray); 582 fOffscreenGraphics.fillRect(0, 0, 583 PROJECTIONPLANEWIDTH, PROJECTIONPLANEHEIGHT/2); 584 fOffscreenGraphics.setColor(Color.darkGray); 585 fOffscreenGraphics.fillRect(0, PROJECTIONPLANEHEIGHT/2, 586 PROJECTIONPLANEWIDTH, PROJECTIONPLANEHEIGHT/2); 587 588 } 589 590 591 592 private void drawOverheadMap() { 593 594 fPlayerMapX=PROJECTIONPLANEWIDTH+(int)(((float)fPlayerX/(float)TILE_SIZE) * fMinimapWidth); 595 fPlayerMapY=(int)(((float)fPlayerY/(float)TILE_SIZE) * fMinimapWidth); 596 597 int plrOffsetY = fPlayerMapY/fMinimapWidth; 598 int mapTilesY = PROJECTIONPLANEHEIGHT/fMinimapWidth; 599 600 if (plrOffsetY <= mapTilesY/2) 601 mapOffsetY = 0; 602 else if (plrOffsetY >= MAP_HEIGHT - (mapTilesY/2)) 603 mapOffsetY = MAP_HEIGHT - mapTilesY; 604 else 605 mapOffsetY = plrOffsetY - (mapTilesY/2); 606 607 for (int u=0; u<MAP_WIDTH; u++) { 608 for (int v=0; v<mapTilesY; v++) { 609 if (fMap[(v+mapOffsetY)*MAP_WIDTH+u] != 0) { 610 fOffscreenGraphics.setColor(Color.cyan); 611 } else { 612 fOffscreenGraphics.setColor(Color.black); 613 } 614 fOffscreenGraphics.fillRect(PROJECTIONPLANEWIDTH+(u*fMinimapWidth), (v*fMinimapWidth), 615 fMinimapWidth, fMinimapWidth); 616 } 617 } 618 619 } 620 621 622 623 private void drawRayOnOverheadMap(float x, float y) { 624 625 // draw line from the player position to the position where the ray 626 // intersect with wall 627 fOffscreenGraphics.setColor(Color.yellow); 628 fOffscreenGraphics.drawLine(fPlayerMapX, 629 fPlayerMapY - mapOffsetY * fMinimapWidth, 630 (int)(PROJECTIONPLANEWIDTH 631 + ((float)(x * fMinimapWidth)/(float)TILE_SIZE)), 632 (int)(((float)(y * fMinimapWidth)/(float)TILE_SIZE) 633 - mapOffsetY * fMinimapWidth)); 634 635 // draw a red line indication the player's direction 636 fOffscreenGraphics.setColor(Color.red); 637 fOffscreenGraphics.drawLine(fPlayerMapX, 638 fPlayerMapY - mapOffsetY * fMinimapWidth, 639 (int)(fPlayerMapX + fCosTable[fPlayerArc]*10), 640 (int)((fPlayerMapY + fSinTable[fPlayerArc]*10) 641 - mapOffsetY * fMinimapWidth)); 642 } 643 644 private void drawHUD() { 645 646 // Weapon 647 fOffscreenGraphics.drawImage(weapon, 648 (PROJECTIONPLANEWIDTH-weapon.getWidth(this))/2, 649 (PROJECTIONPLANEHEIGHT-weapon.getHeight(this)), 650 this); 651 652 if (fBang > 0) { 653 // Alright, it's kinda dirty, but who cares? 654 --fBang; 655 fOffscreenGraphics.setColor(fBangC); 656 fOffscreenGraphics.setFont(new Font("Monospaced", Font.BOLD|Font.CENTER_BASELINE, 20)); 657 fOffscreenGraphics.drawString("*Bang!*", PROJECTIONPLANEWIDTH/2-22 + fBangX, 658 PROJECTIONPLANEHEIGHT*3/4 + fBangY); 659 } 660 661 } 662 663 664 public void render() { 665 666 drawBackground(); 667 drawOverheadMap(); 668 669 int verticalGrid; // horizotal or vertical coordinate of intersection 670 int horizontalGrid; // theoritically, this will be multiple of TILE_SIZE 671 // , but some trick did here might cause 672 // the values off by 1 673 int distToNextVerticalGrid; // how far to the next bound (this is multiple of 674 int distToNextHorizontalGrid; // tile size) 675 float xIntersection; // x and y intersections 676 float yIntersection; 677 float distToNextXIntersection; 678 float distToNextYIntersection; 679 680 int xGridIndex; // the current cell that the ray is in 681 int yGridIndex; 682 683 float distToVerticalGridBeingHit; // the distance of the x and y ray intersections from 684 float distToHorizontalGridBeingHit; // the viewpoint 685 686 int castArc, castColumn; 687 688 float dist; 689 int slice, material, wallColor; 690 691 692 // Field of view is 60 degree with the point of view 693 // (player's direction in the middle) 694 // 30 30 695 // ^ 696 // \ | / 697 // \|/ 698 // v 699 // We will trace the rays starting from the leftmost ray 700 701 702 castArc = fPlayerArc - ANGLE30; 703 704 if (castArc < 0) 705 castArc += ANGLE360; 706 707 708 for (castColumn=0; castColumn<PROJECTIONPLANEWIDTH; castColumn+=resolution) { 709 710 /* Horizontal Collision */ 711 712 // Ray is facing down 713 if (castArc > ANGLE0 && castArc < ANGLE180) { 714 horizontalGrid = (fPlayerY/TILE_SIZE) * TILE_SIZE + TILE_SIZE; 715 distToNextHorizontalGrid = TILE_SIZE; 716 717 xIntersection = (fITanTable[castArc] * (horizontalGrid - fPlayerY)) + fPlayerX; 718 } 719 720 // Ray is facing up 721 else { 722 horizontalGrid = (fPlayerY/TILE_SIZE) * TILE_SIZE; 723 distToNextHorizontalGrid = -TILE_SIZE; 724 725 xIntersection = (fITanTable[castArc] * (horizontalGrid - fPlayerY)) + fPlayerX; 726 727 --horizontalGrid; 728 } 729 730 731 // Looking for horizontal wall 732 if ((castArc == ANGLE0) || (castArc == ANGLE180)) { 733 distToHorizontalGridBeingHit = Float.MAX_VALUE; 734 735 } else { 736 737 distToNextXIntersection = fXStepTable[castArc]; 738 739 while (true) { 740 741 xGridIndex = (int)(xIntersection/TILE_SIZE); 742 // in the picture, yGridIndex will be 1 743 yGridIndex = (horizontalGrid/TILE_SIZE); 744 745 if ((xGridIndex >= MAP_WIDTH) || 746 (yGridIndex >= MAP_HEIGHT) || 747 (xGridIndex < 0) || 748 (yGridIndex < 0)) { 749 distToHorizontalGridBeingHit = Float.MAX_VALUE; 750 break; 751 752 } else if ((fMap[yGridIndex*MAP_WIDTH+xGridIndex]) != 0) { 753 distToHorizontalGridBeingHit = (xIntersection-fPlayerX)*fICosTable[castArc]; 754 break; 755 756 } else { 757 xIntersection += distToNextXIntersection; 758 horizontalGrid += distToNextHorizontalGrid; 759 } 760 761 } 762 } 763 764 765 766 /* Vertical Collision */ 767 768 // Ray facing right 769 if (castArc < ANGLE90 || castArc > ANGLE270) { 770 771 verticalGrid = TILE_SIZE + (fPlayerX/TILE_SIZE) * TILE_SIZE; 772 distToNextVerticalGrid = TILE_SIZE; 773 774 yIntersection = (fTanTable[castArc] * (verticalGrid - fPlayerX)) + fPlayerY; 775 776 } 777 778 // Ray facing left 779 else { 780 781 verticalGrid = (fPlayerX/TILE_SIZE) * TILE_SIZE; 782 distToNextVerticalGrid = -TILE_SIZE; 783 784 yIntersection = (fTanTable[castArc] * (verticalGrid - fPlayerX)) + fPlayerY; 785 786 verticalGrid--; 787 788 } 789 790 // Looking for vertical wall 791 if ((castArc == ANGLE90) || (castArc == ANGLE270)) { 792 793 distToVerticalGridBeingHit = Float.MAX_VALUE; 794 795 } else { 796 797 distToNextYIntersection = fYStepTable[castArc]; 798 799 while (true) { 800 801 // compute current map position to inspect 802 xGridIndex = (verticalGrid/TILE_SIZE); 803 yGridIndex = (int)(yIntersection/TILE_SIZE); 804 805 if ((xGridIndex >= MAP_WIDTH) || 806 (yGridIndex >= MAP_HEIGHT) || 807 (xGridIndex < 0) || 808 (yGridIndex < 0)) { 809 distToVerticalGridBeingHit = Float.MAX_VALUE; 810 break; 811 812 } else if ((fMap[yGridIndex*MAP_WIDTH+xGridIndex]) != 0) { 813 distToVerticalGridBeingHit = (yIntersection-fPlayerY) * fISinTable[castArc]; 814 break; 815 816 } else { 817 yIntersection += distToNextYIntersection; 818 verticalGrid += distToNextVerticalGrid; 819 } 820 821 } 822 } 823 824 825 826 /* Drawing the wall slice */ 827 828 829 // Determining which ray strikes a closer wall. 830 831 if (distToHorizontalGridBeingHit < distToVerticalGridBeingHit) { 832 drawRayOnOverheadMap(xIntersection, horizontalGrid); 833 dist = distToHorizontalGridBeingHit; 834 material = fMap[(int)((int)(xIntersection/TILE_SIZE) 835 + MAP_WIDTH * (int)(horizontalGrid/TILE_SIZE))]; 836 slice = (int)(xIntersection % TILE_SIZE); 837 wallColor = 0; 838 } else { 839 drawRayOnOverheadMap(verticalGrid, yIntersection); 840 dist = distToVerticalGridBeingHit; 841 material = fMap[(int)((int)(verticalGrid/TILE_SIZE) 842 + MAP_WIDTH * (int)(yIntersection/TILE_SIZE))]; 843 slice = (int)(yIntersection % TILE_SIZE); 844 wallColor = 1; 845 } 846 847 // Fishbown effect compensation 848 dist /= fFishTable[castColumn]; 849 850 int projectedWallHeight = (int)(TILE_SIZE*(float)fPlayerDistanceToTheProjectionPlane/dist); 851 int topOfWall = fProjectionPlaneYCenter - (int)(projectedWallHeight*0.5F); 852 int bottomOfWall = fProjectionPlaneYCenter + (int)(projectedWallHeight*0.5F); 853 854 fOffscreenGraphics.drawImage(walls[material][wallColor], 855 castColumn, topOfWall, 856 castColumn+resolution, bottomOfWall, 857 slice, 0, 858 slice+1, 64, 859 this); 860 // 861 // Next ray 862 castArc += resolution; 863 if (castArc >= ANGLE360) 864 castArc -= ANGLE360; 865 866 } 867 868 drawHUD(); 869 870 paint(getGraphics()); 871 872 } 873 874 875 }
Las marcas de fábrica y los Copyright pertenecen a sus respectivos propietarios.