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
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);
}
}
}
};
};