Sign in to follow this  
devseer

Raycasting from scratch (JS)

Recommended Posts

devseer    115

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);
				}
			}
		}
	};
};
Edited by devseer

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this