Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ==== raycast.html ====
- <html>
- <head>
- <title>Stuff!</title>
- <script type="text/javascript" src="raycast.js"></script>
- <script type="text/javascript" src="maze.js"></script>
- <script type="text/javascript">
- <!--
- function init() {
- raycaster_init();
- maze_init();
- genmaze();
- }
- window.onload = init;
- //-->
- </script>
- </head>
- <body>
- <div style="text-align:center;white-space:nowrap;font-family:monospace;font-size:8;background-color:black;color:white" id="divscreen"></div>
- <div style="text-align:center" id="divfps"></div>
- <div style="text-align:center">Up/Down move forward/backwards, Left/Right turn.</div>
- <div style="text-align:center;font-family:monospace" id="map"></div>
- <div id="debugscr"></div>
- </form>
- </body>
- </html>
- ==== maze.js ====
- var maze_width = 10;
- var maze_height = 10;
- var maze;
- var start_char = "?";
- var end_char = "!";
- var norm_char = "#";
- var rand_char = "@$%&~*";
- var maze_map;
- var maze_map_width = maze_width*2 + 1;
- var maze_map_height = maze_height*2 + 1;
- var mapdiv;
- var debugdiv;
- function Cell(x, y) {
- this.north = true;
- this.west = true;
- this.touched = false;
- this.back = null;
- this.x = x;
- this.y = y;
- }
- function maze_init() {
- maze_map = new Array(maze_map_height);
- for (var i = 0; i < maze_map_height; i++) {
- maze_map[i] = new Array(maze_map_width);
- }
- mapdiv = document.getElementById("map");
- debugdiv = document.getElementById("debugscr");
- }
- function genmaze() {
- maze = new Array(maze_height);
- for (var i = 0; i < maze_height; i++) {
- maze[i] = new Array(maze_width);
- for (var j = 0; j < maze_width; j++) {
- maze[i][j] = new Cell(j, i);
- }
- }
- var currx = Math.floor(Math.random() * maze_width);
- var curry = Math.floor(Math.random() * maze_height);
- var currcell = maze[curry][currx];
- do { // This is the main depth-first-search loop, it creates our maze
- // Touch the current cell
- currcell.touched = true;
- // Get a random direction
- var valid_dirs = [];
- if (!(curry <= 0 || maze[curry-1][currx].touched || !currcell.north)) {
- valid_dirs.push(0);
- }
- if (!(curry >= maze_height-1 || maze[curry+1][currx].touched || !maze[curry+1][currx])) {
- valid_dirs.push(1);
- }
- if (!(currx <= 0 || maze[curry][currx-1].touched || !currcell.west)) {
- valid_dirs.push(2);
- }
- if (!(currx >= maze_width-1 || maze[curry][currx+1].touched || !maze[curry][currx+1].west)) {
- valid_dirs.push(3);
- }
- // Are we stuck?
- if (valid_dirs.length == 0) {
- // Roll back
- currcell = currcell.back;
- if (currcell != null) {
- currx = currcell.x;
- curry = currcell.y;
- }
- continue;
- }
- // We're not stuck, move on
- var dir = valid_dirs[Math.floor(Math.random()*valid_dirs.length)];
- switch(dir) {
- case 0: // head north
- // knock out the north wall
- currcell.north = false;
- // move to the north cell
- curry--;
- maze[curry][currx].back = currcell;
- currcell = maze[curry][currx];
- break;
- case 1: // head south
- // knock out the south wall
- maze[curry+1][currx].north = false;
- // move to the south cell
- curry++;
- maze[curry][currx].back = currcell;
- currcell = maze[curry][currx];
- break;
- case 2: // head west
- // knock out the west wall
- currcell.west = false;
- // move to the west cell
- currx--;
- maze[curry][currx].back = currcell;
- currcell = maze[curry][currx];
- break;
- case 3: // head east
- // knock out the east wall
- maze[curry][currx+1].west = false;
- // move to the north cell
- currx++;
- maze[curry][currx].back = currcell;
- currcell = maze[curry][currx];
- break;
- } // switch (dir)
- } while (currcell != null); // This is when we know we've reached every cell.
- // Now we have to convert the maze data into a map for the raycaster
- // Build the base map - map boundaries and known internal corners only
- for (var i = 0; i < maze_map_height; i++) {
- for (var j = 0; j < maze_map_width; j++) {
- // Boundaries
- if (i == 0 || i == maze_map_height-1 || j == 0 || j == maze_map_width-1) {
- maze_map[i][j] = norm_char;
- }
- // Internal corners
- else if (i % 2 == 0 && j % 2 == 0) {
- maze_map[i][j] = norm_char;
- }
- else {
- maze_map[i][j] = "";
- }
- }
- }
- // Put in walls
- for (var i = 0; i < maze_height; i++) {
- for (var j = 0; j < maze_width; j++) {
- // North wall
- if (i > 0 && maze[i][j].north) {
- // Make it fake?
- if (Math.random() < 0.9) {
- maze_map[2*i][2*j+1] = norm_char;
- }
- else {
- maze_map[2*i][2*j+1] = "F";
- }
- }
- // West wall
- if (j > 0 && maze[i][j].west) {
- // Make it fake?
- if (Math.random() < 0.9) {
- maze_map[2*i+1][2*j] = norm_char;
- }
- else {
- maze_map[2*i+1][2*j] = "F";
- }
- }
- }
- }
- // Fill in start/end spaces
- var startx = 1;
- var starty = 1;
- var endx = maze_map_width-2;
- var endy = maze_map_height-2;
- for (var i = -1; i <= 1; i++) {
- for (var j = -1; j <= 1; j++) {
- if(maze_map[i+starty][j+startx]) {
- maze_map[i+starty][j+startx] = start_char;
- }
- if(maze_map[i+endy][j+endx]) {
- maze_map[i+endy][j+endx] = end_char;
- }
- }
- }
- // Draw out the map
- for (var i = 0; i < maze_map_height; i++) {
- for (var j = 0; j < maze_map_width; j++) {
- if (maze_map[i][j]) {
- mapdiv.innerHTML = mapdiv.innerHTML + maze_map[i][j];
- }
- else {
- mapdiv.innerHTML = mapdiv.innerHTML + " ";
- }
- }
- mapdiv.innerHTML = mapdiv.innerHTML + "<br/>";
- }
- // Set the map
- map = maze_map;
- playerx = 1.5*tile_size;
- playery = 1.5*tile_size;
- }
- ==== raycast.js ====
- var screen;
- var fpsdiv;
- var lines;
- var screen_width = 320;
- var screen_height = 100;
- var scanline_0;
- var coord_line;
- var map_coord_line;
- var floorc = ".";
- var ceilc = ".";
- /* This was for debugging the raycaster, it's no longer necessary
- var map = [
- ["#","#","#","#","#","#","#","#","#","#","#","#"], // ############
- ["#","" ,"" ,"@","" ,"" ,"" ,"" ,"" ,"" ,"" ,"#"], // # @ #
- ["#","@","" ,"" ,"" ,"$","" ,"!","!","!","" ,"#"], // #@ $ !!! #
- ["#","" ,"" ,"@","" ,"$","" ,"" ,"" ,"!","" ,"#"], // # @ $ ! #
- ["#","" ,"@","" ,"" ,"$","" ,"!","!","!","" ,"#"], // # @ $ !!! #
- ["#","#","#","#","#","#","#","#","#","#","#","#"] // ############
- ];
- */
- var map;
- var max_turn_rate = Math.PI/50;
- var turn_rate_accel = Math.PI/160;
- var max_move_rate = 9;
- var move_rate_accel = 1;
- var player_rad = 10; // need to keep player from walking right up to a wall, it causes issues
- var turn_rate = 0;
- var move_rate = 0;
- var strafe_rate = 0;
- var view_dist = 144;
- var view_width = 230;
- var view_height = 144;
- var view_angle = 2*Math.atan(0.5*view_width/view_dist);
- var tile_size = 100;
- var wall_height = 80;
- var playerx = 2.5*tile_size;
- var playery = 2.5*tile_size;
- var playerd = 0;
- var lastdrawx = -1;
- var lastdrawy = -1;
- var lastdrawc = "";
- var lastdir = -1;
- var HORIZ = 0;
- var VERT = 1;
- var keys = [false, false, false, false, false, false];
- var UP = 0;
- var DOWN = 1;
- var LEFT = 2;
- var RIGHT = 3;
- var KEY_A = 4;
- var KEY_S = 5;
- var must_redraw = true;
- var stop = false;
- var frames = 0;
- var msecs = 0;
- function raycaster_init() {
- // Get screen div
- screen = document.getElementById("divscreen");
- // Get fps div
- fpsdiv = document.getElementById("divfps");
- // Add a variety of text nodes
- var nodeidx = screen.childNodes.length;
- scanline_0 = nodeidx;
- for (var i = 0; i < screen_height + scanline_0; i++) {
- screen.appendChild(document.createTextNode("")); nodeidx++;
- screen.appendChild(document.createElement("br")); nodeidx++;
- }
- // Data lines
- coord_line = nodeidx;
- screen.appendChild(document.createTextNode("")); nodeidx++;
- screen.appendChild(document.createElement("br")); nodeidx++;
- map_coord_line = nodeidx;
- screen.appendChild(document.createTextNode("")); nodeidx++;
- screen.appendChild(document.createElement("br")); nodeidx++;
- // Set up lines array
- lines = new Array(screen_height);
- // Set up key event handling
- window.onkeydown = function(e) {
- var key = e.keyCode;
- if (key == 38) { keys[UP] = true; return false; }
- else if (key == 40) { keys[DOWN] = true; return false; }
- else if (key == 37) { keys[LEFT] = true; return false; }
- else if (key == 39) { keys[RIGHT] = true; return false; }
- else if (key == 65) { keys[KEY_A] = true; return false; }
- else if (key == 83) { keys[KEY_S] = true; return false; }
- else if (key == 81) { stop = true; return false; } // q
- }
- window.onkeyup = function(e) {
- var key = e.keyCode;
- if (key == 38) { keys[UP] = false; return false; }
- else if (key == 40) { keys[DOWN] = false; return false; }
- else if (key == 37) { keys[LEFT] = false; return false; }
- else if (key == 39) { keys[RIGHT] = false; return false; }
- else if (key == 65) { keys[KEY_A] = false; return false; }
- else if (key == 83) { keys[KEY_S] = false; return false; }
- }
- must_redraw = true;
- // Set up the update function
- window.setTimeout(update, 10);
- // Set up the counter function
- window.setInterval(timing, 200);
- }
- function timing() {
- msecs += 200;
- }
- function update() {
- frames += 1;
- var redraw = true; // Testing efficiency
- // Handle player movement
- with(Math) {
- // First, adjust turn rate
- if (keys[LEFT]) {
- turn_rate += turn_rate_accel;
- if (turn_rate > max_turn_rate) {
- turn_rate = max_turn_rate;
- }
- }
- else if (keys[RIGHT]) {
- turn_rate -= turn_rate_accel;
- if (turn_rate < -max_turn_rate) {
- turn_rate = -max_turn_rate;
- }
- }
- else {
- if (turn_rate < 0) {
- turn_rate += 2*turn_rate_accel;
- if (turn_rate > 0) {
- turn_rate = 0;
- }
- }
- else {
- turn_rate -= 2*turn_rate_accel;
- if (turn_rate < 0) {
- turn_rate = 0;
- }
- }
- }
- // Turn player
- if (turn_rate != 0) {
- playerd += turn_rate;
- if (playerd >= 2*PI) {
- playerd -= 2*PI;
- }
- else if (playerd < 0) {
- playerd += 2*PI;
- }
- redraw = true;
- }
- // Now, move the player forward/backward
- if (keys[UP]) {
- move_rate += move_rate_accel;
- if (move_rate > max_move_rate) {
- move_rate = max_move_rate;
- }
- }
- else if (keys[DOWN]) {
- move_rate -= move_rate_accel;
- if (move_rate < -max_move_rate) {
- move_rate = -max_move_rate;
- }
- }
- else {
- if (move_rate < 0) {
- move_rate += 2*move_rate_accel;
- if (move_rate > 0) {
- move_rate = 0;
- }
- }
- else {
- move_rate -= 2*move_rate_accel;
- if (move_rate < 0) {
- move_rate = 0;
- }
- }
- }
- // Move player
- if (move_rate != 0 ) {
- // Check for collision in the cheapest way possible
- var tempx = playerx + cos(playerd)*move_rate;
- var tempy = playery + sin(playerd)*move_rate;
- var collx = tempx + cos(playerd) * player_rad * (move_rate < 0 ? -1 : 1);
- var colly = tempy + sin(playerd) * player_rad * (move_rate < 0 ? -1 : 1);
- // Breaking a fake wall?
- if (map[floor(colly/tile_size)][floor(collx/tile_size)] == "F") {
- map[floor(colly/tile_size)][floor(collx/tile_size)] = "";
- }
- // Will we end up in blank space?
- if (!map[floor(colly/tile_size)][floor(collx/tile_size)]) {
- playerx = tempx;
- playery = tempy;
- redraw = true;
- }
- }
- // Strafe left/right
- if (keys[KEY_A]) {
- strafe_rate += move_rate_accel;
- if (strafe_rate > max_move_rate) {
- strafe_rate = max_move_rate;
- }
- }
- else if (keys[KEY_S]) {
- strafe_rate -= move_rate_accel;
- if (strafe_rate < -max_move_rate) {
- strafe_rate = -max_move_rate;
- }
- }
- else {
- if (strafe_rate < 0) {
- strafe_rate += 2*move_rate_accel;
- if (strafe_rate > 0) {
- strafe_rate = 0;
- }
- }
- else {
- strafe_rate -= 2*move_rate_accel;
- if (strafe_rate < 0) {
- strafe_rate = 0;
- }
- }
- }
- // Move player for strafing
- if (strafe_rate != 0) {
- // Check for collision in the cheapest way possible
- var tempx = playerx + cos(playerd+PI/2)*strafe_rate;
- var tempy = playery + sin(playerd+PI/2)*strafe_rate;
- var collx = tempx + cos(playerd+PI/2) * player_rad * (strafe_rate < 0 ? -1 : 1);
- var colly = tempy + sin(playerd+PI/2) * player_rad * (strafe_rate < 0 ? -1 : 1);
- // Breaking a fake wall?
- if (map[floor(colly/tile_size)][floor(collx/tile_size)] == "F") {
- map[floor(colly/tile_size)][floor(collx/tile_size)] = "";
- }
- // Will we end up in blank space?
- if (!map[floor(colly/tile_size)][floor(collx/tile_size)]) {
- playerx = tempx;
- playery = tempy;
- redraw = true;
- }
- }
- }
- // Must we redraw the screen?
- if (redraw || must_redraw) {
- // Clear screen
- for (var i = 0; i < screen_height; i++) {
- lines[i] = "";
- }
- // Reset outline information
- lastdrawx = -1;
- lastdrawy = -1;
- lastdir = -1;
- // Cast rays
- for (var i = 0; i < screen_width; i++) {
- raycast(i)
- }
- // Draw screen
- write_screen();
- // Turn off must_redraw, unless we were going to redraw anyway
- if (!redraw) {
- must_redraw = false;
- }
- }
- if (!stop) {
- window.setTimeout(update, 10);
- }
- }
- function raycast(n) {
- var dir = playerd - (n/screen_width - 0.5)*view_angle;
- var xbase = Math.cos(dir);
- var ybase = Math.sin(dir);
- var north = (ybase < 0 ? true : false);
- var east = (xbase > 0 ? true : false);
- var zerox = xbase == 0;
- var zeroy = ybase == 0;
- var currx = playerx;
- var curry = playery;
- var currmapx = Math.floor(playerx/tile_size);
- var currmapy = Math.floor(playery/tile_size);
- var nexthedgex, nexthedgey, nextvedgex, nextvedgey;
- var thisdir = -1;
- while(!map[currmapy][currmapx]) {
- // Calculate the next horizontal edge
- if (north) {
- nexthedgey = Math.ceil(curry/tile_size - 1) * tile_size;
- }
- else {
- nexthedgey = Math.floor(curry/tile_size + 1) * tile_size;
- }
- nexthedgex = currx + (xbase/ybase)*(nexthedgey-curry);
- // Calculate the next vertical edge
- if (east) {
- nextvedgex = Math.floor(currx/tile_size + 1) * tile_size;
- }
- else {
- nextvedgex = Math.ceil(currx/tile_size - 1) * tile_size;
- }
- nextvedgey = curry + (ybase/xbase)*(nextvedgex-currx);
- // Determine which is nearer
- if ((nexthedgex - currx)*(nexthedgex - currx) + (nexthedgey - curry)*(nexthedgey - curry) >
- (nextvedgex - currx)*(nextvedgex - currx) + (nextvedgey - curry)*(nextvedgey - curry)) {
- // Vertical edge is nearer
- currx = nextvedgex;
- curry = nextvedgey;
- // Get the next map cell
- if (east) {
- // Cell is to the right
- currmapx++;
- }
- else {
- // Cell is to the left
- currmapx--;
- }
- if (map[currmapy][currmapx]) {
- thisdir = VERT;
- }
- }
- else {
- // Horizontal edge is nearer
- currx = nexthedgex;
- curry = nexthedgey;
- // Get the next map cell
- if (north) {
- // Cell is northwards
- currmapy--;
- }
- else {
- // Cell is southwards
- currmapy++;
- }
- if (map[currmapy][currmapx]) {
- thisdir = HORIZ;
- }
- }
- }
- // We've found a wall! Now to get the distance
- var baddist = Math.sqrt((currx-playerx)*(currx-playerx)+(curry-playery)*(curry-playery));
- // Fisheye correction
- var dist = baddist * Math.cos(dir-playerd);
- // Calculate display height of wall
- var wall_view_height = wall_height * view_dist / dist;
- var dispheight = Math.ceil(wall_view_height / view_height * screen_height);
- // Detrmine number of ceiling, wall, and floor lines to use
- var ceilpx, wallpx, floorpx;
- // Wall taller than screen?
- if (dispheight > screen_height) {
- // Display only wall
- ceilpx = 0;
- wallpx = screen_height;
- floorpx = 0;
- }
- else {
- // Display wall at dispheight and floor/ceil at equal heights
- ceilpx = Math.ceil((screen_height - dispheight)/2);
- wallpx = dispheight;
- floorpx = Math.floor((screen_height - dispheight)/2);
- }
- // Where do I start drawing the wall and the floor?
- var wallloc = ceilpx;
- var floorloc = ceilpx + wallpx;
- // Should we draw a black outline?
- var outline = false;
- if (i == 0 || i == screen_width-1) {
- // Always draw the leftmost and rightmost columns
- }
- else if ((lastdrawx != currmapx && lastdrawy != currmapy) || lastdir != thisdir ||
- lastdrawc != map[currmapy][currmapx]) {
- // Draw an outline
- outline = true;
- }
- lastdrawx = currmapx;
- lastdrawy = currmapy;
- lastdrawc = map[currmapy][currmapx];
- lastdir = thisdir;
- // Now to draw to lines array
- for (var i = 0; i < screen_height; i++) {
- if (i >= floorloc) {
- lines[i] = lines[i] + floorc;
- }
- else if (i >= wallloc) {
- if (outline) {
- lines[i] = lines[i] + "\u00a0"; // non-breaking space
- }
- else if (map[currmapy][currmapx] == "F") {
- lines[i] = lines[i] + "#"; // fake wall
- }
- else {
- lines[i] = lines[i] + map[currmapy][currmapx];
- }
- }
- else {
- lines[i] = lines[i] + ceilc;
- }
- }
- } // function raycast
- function write_screen() {
- for (var i = 0; i < screen_height; i++) {
- // i*2 to skip over br elements
- screen.childNodes[scanline_0 + i*2].data = lines[i];
- }
- screen.childNodes[coord_line].data = playerx + ", " + playery + " (dir " + playerd + ")";
- screen.childNodes[map_coord_line].data = "Map coord: " + Math.floor(playerx/tile_size) + ", " + Math.floor(playery/tile_size);
- fpsdiv.innerHTML = (msecs == 0 ? "??" : Math.round(1000*frames/msecs));
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement