Raycasting from scratch (JS)

Started by
0 comments, last by Krypt0n 10 years, 5 months ago

Hi all,

I don't usually post on places like this but I'm having some real trouble getting my engine to behave correctly and I've been trying to fix this for several weeks now.

I've been building a raycasting / pseudo-3d (Think Wolfenstein, Doom etc.) engine from scratch, in JavaScript. I have a very basic map rendering, with different coloured walls and there is player movement and collision. I'm having two major issues:

  • The camera does not appear to correctly center with the screen - If the player moves, the view appears to move at a slight angle
  • I'm unable to render each slice of my texture without significant fluttering at most angles - Sometimes it draws perfectly and stays still, most of the time is scrolls horizontally for no reason

7FarDP2.png

4PdReGY.png

I've just made my repo public, to make sharing the code easier:

https://github.com/devseer/dust

The main code for rendering is in world.js

(Arrow keys to control movement)

I would greatly appreciate any help anyone has to offer. However, I'm really not interested in using any frameworks, as I'm really doing this for the learning experience. So please do not suggest any frameworks or libraries.

Thanks for reading

ps. Added code for those who don't want to go to Github:


var World = function() {
	this.viewDistance = 0;
	this.wall = new Image();

	this.map = [
	[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
	[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1],
	[1,0,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1],
	[1,0,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1],
	[1,0,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1],
	[1,0,0,3,3,0,0,0,0,0,4,4,0,0,0,0,0,0,1,0,0,1],
	[1,0,0,0,0,0,0,0,0,0,4,4,0,0,0,0,0,0,0,0,0,1],
	[1,0,0,0,0,0,0,0,0,0,4,4,0,0,0,0,0,0,0,0,0,1],
	[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
	[1,2,2,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
	[1,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
	[1,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
	[1,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
	[1,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
	[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
	];

	this.init = function() {
		var fieldOfView = 60 * Math.PI / 180;
		this.viewDistance = 256 / Math.tan((fieldOfView / 2));
		this.wall.src = './img/wall.png';
	};

	this.inc = 0;

	this.drawRay = function(context, x, distance, texture) {
		var width = 8;
		var height = Math.round(this.viewDistance / distance);
		var y = Math.round((512 - height) / 2);

		switch (texture) {
			case 1: context.fillStyle = 'rgb(100, 100, 100)';
				break;
			case 2: context.fillStyle = 'rgb(128, 64, 64)';
				break;
			case 3: context.fillStyle = 'rgb(64, 64, 128)';
				break;
		}

		//if (texture == 1 || texture == 2 || texture == 3)
			context.fillRect(x, y, width, height);

		if (texture == 4) {
			context.drawImage(this.wall, this.inc % 16, 0, 1, 16, x, Math.round((512 - height) / 2), 8, height);
			this.inc++;
		}
	};

	this.castRay = function(rotation, direction, sourcex, sourcey) {
		var x = sourcex;
		var y = sourcey;
		var addx =  Math.cos(direction) * 0.01;
		var addy = Math.sin(direction) * 0.01;
		var outx = Math.floor(x);
		var outy = Math.floor(y);
		var type = 0;


		while (type == 0) {
			type = this.map[outy][outx];

			x += addx;
			y += addy;

			outx = Math.floor(x);
			outy = Math.floor(y);

			if (outx < 1 || outx > 21 || outy < 1 || outy > 14) break;
		}

		return {
			texture: type,
			distance: this.distance(sourcex, sourcey, x, y) * Math.cos(rotation - direction)
		};
	};

	this.distance = function(ax, ay, bx, by) {
		var nx = ax - bx;
		var ny = ay - by;

		return Math.sqrt(nx * nx + ny * ny);
	};

	this.angle = function(rotation, increment) {
		return rotation + Math.asin(increment / Math.sqrt(increment * increment + this.viewDistance * this.viewDistance));
	};

	this.draw = function(context, player) {
		var i, j;

		// Ceiling
		context.fillStyle = 'rgb(150, 150, 180)';
		context.fillRect(0, 0, 512, 256);

		// Floor
		context.fillStyle = 'rgb(32, 64, 32)';
		context.fillRect(0, 256, 512, 256);

		for (i = 0; i < 512; i+= 8) {
			context.fillStyle = 'rgb(100, 100, 100)';
			var surface = this.castRay(player.rot, this.angle(player.rot, i), player.pos.x, player.pos.y);
			this.drawRay(context, i, surface.distance, surface.texture);
		}

		// Mini-map
		for (i = 0; i < 22; i++) {
			for (j = 0; j < 15; j++) {
				if (this.map[j][i] > 0) {
					context.fillStyle = 'rgb(255, 255, 255)';
					context.fillRect(i*8, j*8, 8, 8);
				}
			}
		}
	};
};
Advertisement

has been done tons of time before... but I still love it! :)

This topic is closed to new replies.

Advertisement