Raycasting Demo (like Wolfenstein3D, just veery basic)

Showcase your libraries, tools and other projects that help your fellow love users.
Post Reply
User avatar
Gerrit
Prole
Posts: 46
Joined: Wed Mar 25, 2009 7:40 pm

Raycasting Demo (like Wolfenstein3D, just veery basic)

Post by Gerrit »

Hey there.

I found a really simple raycasting demo today. It's a sample for CodeTanks "Brain Damage" ;) And because "Brain Damage" also uses lua it only took 5 minutes to port it to Löve :) It's not the next Wolfenstein 3D but it shows the basics of the raycasting technique. Have fun with this demo. Change some parameters, make a maze out of it or try to render textures to the walls :) If you're done with that, add enemies. And doors. And german shepherds :joker:

Image

Updated!
* Added FPS Counter
* Keydetection now in update() function -> walking smooth now :)
Attachments
LoveRaycastingDemo.love
(3.09 KiB) Downloaded 641 times
Last edited by Gerrit on Fri Apr 10, 2009 10:41 am, edited 1 time in total.
User avatar
appleide
Party member
Posts: 323
Joined: Fri Jun 27, 2008 2:50 pm

Re: Raycasting Demo (like Wolfenstein3D, just veery basic)

Post by appleide »

This is pretty awesome ;) nice work! :)
User avatar
Xcmd
Party member
Posts: 211
Joined: Fri Feb 13, 2009 10:45 pm

Re: Raycasting Demo (like Wolfenstein3D, just veery basic)

Post by Xcmd »

Good demo. I'd recommend moving the code out of function keypressed and into the update routine instead, so people don't have to keep tapping the buttons to go. Gives an idea of just how fast this engine really goes.
We don't borrow, we don't read, we don't rent, we don't lease, we take the minds!
User avatar
Gerrit
Prole
Posts: 46
Joined: Wed Mar 25, 2009 7:40 pm

Re: Raycasting Demo (like Wolfenstein3D, just veery basic)

Post by Gerrit »

Xcmd wrote:Good demo. I'd recommend moving the code out of function keypressed and into the update routine instead, so people don't have to keep tapping the buttons to go. Gives an idea of just how fast this engine really goes.
Ya, this was the last thing I did last night before bedtime ;) I updated it now. Runs smooth with 60fps most of the time if you're not drawing too many walls at once.
User avatar
osgeld
Party member
Posts: 303
Joined: Sun Nov 23, 2008 10:13 pm

Re: Raycasting Demo (like Wolfenstein3D, just veery basic)

Post by osgeld »

in this thread i posted basicly the same thing (im sure the script is organized different) but has some borked up texture mapping too

enjoy
User avatar
TechnoCat
Inner party member
Posts: 1611
Joined: Thu Jul 30, 2009 12:31 am
Location: Denver, CO
Contact:

Re: Raycasting Demo (like Wolfenstein3D, just veery basic)

Post by TechnoCat »

I mangled it to an ugly version of 0.6.1
Attachments
raycast.love
0.6.1
(2.43 KiB) Downloaded 252 times
User avatar
Jasoco
Inner party member
Posts: 3725
Joined: Mon Jun 22, 2009 9:35 am
Location: Pennsylvania, USA
Contact:

Re: Raycasting Demo (like Wolfenstein3D, just veery basic)

Post by Jasoco »

I'm sad it runs so slow if you try to up the "resolution". I guess all those math calculations really take their toll. Weird that the JavaScript/Canvas version of these Wolfenstein engines runs so much faster.
User avatar
Robin
The Omniscient
Posts: 6506
Joined: Fri Feb 20, 2009 4:29 pm
Location: The Netherlands
Contact:

Re: Raycasting Demo (like Wolfenstein3D, just veery basic)

Post by Robin »

Jasoco wrote:I'm sad it runs so slow if you try to up the "resolution". I guess all those math calculations really take their toll. Weird that the JavaScript/Canvas version of these Wolfenstein engines runs so much faster.
I guess they are much more optimized.
Help us help you: attach a .love.
User avatar
Jasoco
Inner party member
Posts: 3725
Joined: Mon Jun 22, 2009 9:35 am
Location: Pennsylvania, USA
Contact:

Re: Raycasting Demo (like Wolfenstein3D, just veery basic)

Post by Jasoco »

Robin wrote:
Jasoco wrote:I'm sad it runs so slow if you try to up the "resolution". I guess all those math calculations really take their toll. Weird that the JavaScript/Canvas version of these Wolfenstein engines runs so much faster.
I guess they are much more optimized.
Nope. They work the same basic way. Raycasting at a certain number of columns per frame. Somehow the JavaScript version runs faster, yet Löve is faster than JavaScript at pretty much everything else. Could it be that Lua/Löve has slightly slower math calculations that don't really show up as a difference until you're calling them many many times?

Compare the code for both engines:
JavaScript:

Code: Select all

var pixelWidth=2;

function changeResolution(){
	var detail = document.getElementById('hilow').options.selectedIndex;
	pixelWidth = [8,4,2,1][detail];
	samples = [50,100,200,400][detail];
    drawCanvas();
}

var map;
var canvas;
var overlay;
//variables initiated at the bottom of the code...

var pic=[];
for (var i=0; i<4; i++) pic[i]=new Image(), pic[i].src='wall'+i+'.gif';


var pi=Math.PI;

var total=0;

Number.prototype.range=function(){
	return (this+2*pi)%(2*pi);
}
Number.prototype.roundC=function(){
	return Math.round(this*100)/100;
}

var total=0;

var samples=200;


var arena=[];
arena[0]=[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
arena[1]=[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] 
arena[2]=[1,1,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,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
arena[3]=[1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] 
arena[4]=[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,0,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
arena[5]=[1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] 
arena[6]=[1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,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]
arena[7]=[1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,1,1,1,1,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] 
arena[8]=[1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
arena[9]=[1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] 
arena[10]=[1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
arena[11]=[1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] 
arena[12]=[1,1,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,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
arena[13]=[1,1,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,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] 
arena[14]=[1,1,1,1,1,1,0,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,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
arena[15]=[1,1,1,1,1,0,0,0,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,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] 
arena[16]=[1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
arena[17]=[1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] 
arena[18]=[1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
arena[19]=[1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] 
arena[20]=[1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
arena[21]=[1,1,1,1,1,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,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] 
arena[22]=[1,1,1,1,1,1,0,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,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
arena[23]=[1,1,0,0,0,0,0,0,0,0,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,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] 
arena[24]=[1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,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]
arena[25]=[1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,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] 
arena[26]=[1,1,0,0,0,0,0,0,0,0,0,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,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1]
arena[27]=[1,1,0,0,0,0,0,0,0,0,0,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,1,1,1,1,1,1,1,1,1] 
arena[28]=[1,1,0,0,0,0,0,0,0,0,0,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,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1]
arena[29]=[1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1] 
arena[30]=[1,1,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,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1]
arena[31]=[1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,0,0,0,1,1,0,1,1,1,1,1,1,1] 
arena[32]=[1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,0,0,0,1,0,0,0,0,0,0,0,0,1]
arena[33]=[1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1] 
arena[34]=[1,0,0,0,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,0,0,0,1,0,0,0,0,0,0,0,0,1]
arena[35]=[1,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,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1] 
arena[36]=[1,0,0,0,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
arena[37]=[1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] 
arena[38]=[1,1,1,1,1,0,0,0,0,0,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
arena[39]=[1,1,1,1,1,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,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] 
arena[40]=[1,1,1,1,1,0,0,0,0,0,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
arena[41]=[1,1,1,1,1,1,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,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] 
arena[42]=[1,1,1,1,1,1,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,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
arena[43]=[1,1,1,1,1,1,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,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] 
arena[44]=[1,1,1,1,1,1,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,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
arena[45]=[1,1,1,1,1,1,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,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] 
arena[46]=[1,1,1,1,1,1,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,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
arena[47]=[1,1,1,1,1,1,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,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] 
arena[48]=[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
arena[49]=[1,1,1,1,1,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,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] 
arena[50]=[1,1,1,1,1,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,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
arena[51]=[1,1,1,1,1,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,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] 
arena[52]=[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]

var playerPos=[51,30.5]; // x,y (from top left)
var playerDir=Math.PI; // theta, facing right=0=2pi
var playerPosZ=1;
var key=[0,0,0,0,0]; // left, right, up, down

var playerVelY=0;

var face=[];
var bit=[];

function wallDistance(theta){

	var dist=[];

	var x = playerPos[0], y = playerPos[1];
	var deltaX, deltaY;
	var distX, distY;
	var stepX, stepY;
	var mapX, mapY
	
	var atX=Math.floor(x), atY=Math.floor(y);


	for (var i=0; i<samples; i++) {
		theta+=pi/(3*samples)+2*pi;
		theta%=2*pi;

		mapX = atX, mapY = atY;

		deltaX=1/Math.cos(theta);
		deltaY=1/Math.sin(theta);

		if (deltaX>0) {
			stepX = 1;
			distX = (mapX + 1 - x) * deltaX;
		}
		else {
			stepX = -1;
			distX = (x - mapX) * (deltaX*=-1);		
		}
		if (deltaY>0) {
			stepY = 1;
			distY = (mapY + 1 - y) * deltaY;
		}
		else {
			stepY = -1;
			distY = (y - mapY) * (deltaY*=-1);
		}
		

		while (true) {
			if (distX < distY) {
				mapX += stepX;
				if (arena[mapX][mapY]) {
					dist[i]=distX;
					face[i]=2+stepX;
					bit[i]=(y+distX/deltaY*stepY)%1 || 0;
					break;
				}
				distX += deltaX;
			}
			else {
				mapY += stepY;
				if (arena[mapX][mapY]) {
					dist[i]=distY;
					face[i]=1+stepY;
					bit[i]=(x+distY/deltaX*stepX)%1 || 0;
					break;
				}
				distY += deltaY;
			}
		}
	}
	
	return dist;
}


function drawCanvas(){

	map.clearRect(0,0,106,106);
	map.fillStyle="#3366CC";
	map.arc(playerPos[0]*2, playerPos[1]*2, 2, 0, 2*pi, true);
	map.fill();
	map.beginPath();
	map.moveTo(2*playerPos[0], 2*playerPos[1]);

	canvas.clearRect(0,0, 400, 300);
	
	var theta = playerDir-pi/6;

	var dist=wallDistance(theta);
	

	var c;	


	for (var i=0; i<samples; i++) {
		theta+=pi/(3*samples);

		var d2=dist[i];
		var d=d2*Math.cos(theta-playerDir);
		
		var z=1-playerPosZ/2;
		
		var h=300/d;

		canvas.drawImage(pic[face[i]], bit[i]*63, 0, 1, 64, i*pixelWidth, 150-h*z, pixelWidth, h)
	
		map.lineTo(playerPos[0]*2+Math.cos(theta)*d2*2, playerPos[1]*2+Math.sin(theta)*d2*2);
	
	}
	map.fillStyle="#FF0000"
	map.fill();
}

function nearWall(x,y){
	var xx,yy;
	if (isNaN(x)) x=playerPos[0];
	if (isNaN(y)) y=playerPos[1];
	for (var i=-0.1; i<=0.1; i+=0.2) {
		xx=Math.floor(x+i)
		for (var j=-0.1; j<=0.1; j+=0.2) {
			yy=Math.floor(y+j);
			if (arena[xx][yy]) return true;
		}
	}
	return false;
}

function wobbleGun(){
	var mag=playerVelY;
	overlay.style.backgroundPosition=(10+Math.cos(total/6.23)*mag*50)+"px "+(10+Math.cos(total/5)*mag*50)+"px";
}


var jumpCycle=0;


function update(){

	total++;

	var change=false;

	if (jumpCycle) {
		jumpCycle--;
		change=true;
		playerPosZ = 1 + jumpCycle*(20-jumpCycle)/110;
	}
	else if (key[4]) jumpCycle=20;
	
	if (key[0]) {
		if (!key[1]) {
			playerDir-=0.07; //left
			change=true;
		}
	}
	else if (key[1]) {
		playerDir+=0.07; //right
		change=true;
	}

	if (change) {
		playerDir+=2*pi;
		playerDir%=2*pi;
		document.getElementById("sky").style.backgroundPosition=Math.floor(-playerDir/(2*pi)*2400)+"px 0";
	}

	if (key[2] && !key[3]) {
		if (playerVelY<0.2) playerVelY += 0.04;
	}
	else if (key[3] && !key[2]) {
		if (playerVelY>-0.2) playerVelY -= 0.04;
	}
	else {
		if (playerVelY<-0.02) playerVelY += 0.015;
		else if (playerVelY>0.02) playerVelY -= 0.015;
		else playerVelY=0;
	}
	
	
	if (playerVelY!=0) {

		var oldX=playerPos[0];;
		var oldY=playerPos[1];		
		var newX=oldX+Math.cos(playerDir)*playerVelY;
		var newY=oldY+Math.sin(playerDir)*playerVelY;

		if (!nearWall(newX, oldY)) {
			playerPos[0]=newX;
			oldX=newX;
			change=true;
		}
		if (!nearWall(oldX, newY)) {
			playerPos[1]=newY;
			change=true;
		}

	}
	
	if (playerVelY) wobbleGun();
	if (change) drawCanvas();
}


function changeKey(which, to){
	switch (which){
		case 65:case 37: key[0]=to; break; // left
		case 87: case 38: key[2]=to; break; // up
		case 68: case 39: key[1]=to; break; // right
		case 83: case 40: key[3]=to; break;// down
		case 32: key[4]=to; break; // space bar;
		case 17: key[5]=to; break; // ctrl
	}
}
document.onkeydown=function(e){changeKey((e||window.event).keyCode, 1);}
document.onkeyup=function(e){changeKey((e||window.event).keyCode, 0);}



function initUnderMap(){
	var underMap=document.getElementById("underMap").getContext("2d");
	underMap.fillStyle="#FFF";
	underMap.fillRect(0,0, 200, 200);
	underMap.fillStyle="#444";
	for (var i=0; i<arena.length; i++) {
		for (var j=0; j<arena[i].length; j++) {
			if (arena[i][j]) underMap.fillRect(i*2, j*2, 2, 2);
		}	
	}
}

window.onload=function(){
	map=document.getElementById("map").getContext("2d");
	canvas=document.getElementById("canvas").getContext("2d");
	overlay=document.getElementById("overlay");
	document.getElementById("sky").style.backgroundPosition=Math.floor(-playerDir/(2*pi)*2400)+"px 0";
	drawCanvas();
	initUnderMap();
	setInterval(update, 35);
}
Lua/Löve:

Code: Select all

function love.load()
love.graphics.setMode( 640, 480, false, true, 0 )

config = { }
config.width = 640 -- width of window
config.height = 480 -- height of window
config.fov = 60 -- field of view (degrees)
config.sliver_width = 4 -- thickness of each wall sliver (pixels)
config.ray_resolution = 0.02 -- lower number = higher resolution = more accurate = more CPU
config.wall_zoom = 320 -- how much to zoom the walls, play with to see results
config.player_speed = 0.1 -- how fast the player moves forward
config.player_turn = 2 -- how fast the player turns
config.fish_eye = false -- should the engine remove the "fish eye" side effect?

-- don't change this stuff
config.sliver_width = math.floor(config.sliver_width + 0.5)
config.slivers_per_screen = math.ceil(config.width / config.sliver_width)
config.ang_per_sliver = config.fov / config.slivers_per_screen
config.cy = math.floor(config.height / 2)

map = {
    width = 11, -- width of map
    height = 11, -- height of map
    start = {2.5, 2.5}, -- x, y cell to start in
    startang = 90, -- what direction to face in

    -- map data... 1-9 are colored walls, 0 is no wall
    {1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1},
    {3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4},
    {4, 0, 0, 0, 0, 0, 7, 8, 7, 0, 3},
    {3, 0, 5, 0, 0, 0, 8, 9, 8, 0, 4},
    {4, 0, 6, 0, 0, 0, 7, 0, 7, 0, 3},
    {3, 0, 5, 0, 0, 0, 8, 0, 8, 0, 4},
    {4, 0, 6, 5, 6, 0, 7, 0, 7, 0, 3},
    {3, 0, 0, 0, 0, 0, 8, 0, 0, 0, 4},
    {4, 0, 6, 0, 3, 0, 7, 8, 7, 0, 3},
    {3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4},
    {1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1},
}

color = { }

player = { }
player.x = map.start[1]
player.y = map.start[2]
player.ang = map.startang

end


function love.draw()
local sx = 0
local ang = player.ang - (config.fov / 2)
local ray_len
local col
local height
local start_wall

    -- start from the left side, and cast a ray for each sliver
    for s = 0, config.slivers_per_screen do

        -- cast the ray
        ray_len, col = cast_ray(player.x, player.y, ang)
        if (not config.fish_eye) then
            -- remove the fish eye side effect
            ray_len = ray_len * math.cos((player.ang - ang) * math.pi / 180)
        end

        -- calculate height of wall
        height = math.floor(config.wall_zoom / ray_len)
        start_wall = math.floor(config.cy - height / 2)

        -- clip wall to screen
        if (start_wall < 0) then
            height = height + start_wall
            start_wall = 0
        else
            -- wall isn't too high, so draw sky
            love.graphics.setColor( 200, 200, 255 )
            love.graphics.rectangle( "fill", sx, 0, config.sliver_width, start_wall )
        end

        if (start_wall + height > config.height) then
            height = config.height - start_wall
        else
            -- wall isn't too long, so draw floor
            love.graphics.setColor( 63, 63, 0 )
            love.graphics.rectangle( "fill", sx, start_wall + height, config.sliver_width, config.height - start_wall - height )
        end

        -- draw the wall
        love.graphics.setColor( 255,   0, 255 )
        love.graphics.rectangle( "fill", sx, start_wall, config.sliver_width, height )

        -- increase our sliver position
        sx = sx + config.sliver_width

        -- increase our sliver angle
        ang = ang + config.ang_per_sliver

    end
    love.graphics.setColor( 0, 0, 0 )
    love.graphics.print("FPS: " .. love.timer.getFPS(), 50, 50)

end


function love.update( dt )
local ox = player.x
local oy = player.y

    if ( love.keyboard.isDown( "up" ) ) then
	-- move player forward
	player.x = player.x + math.cos(player.ang * math.pi / 180) * config.player_speed
	player.y = player.y + math.sin(player.ang * math.pi / 180) * config.player_speed
    elseif ( love.keyboard.isDown( "down" ) ) then
	-- move player backwards
	player.x = player.x - math.cos(player.ang * math.pi / 180) * config.player_speed
	player.y = player.y - math.sin(player.ang * math.pi / 180) * config.player_speed
    end
	 if ( love.keyboard.isDown( "right" ) ) then
	player.ang = (player.ang + config.player_turn) % 360
    elseif ( love.keyboard.isDown( "left" ) ) then
	player.ang = (player.ang + 360 - config.player_turn) % 360
    end

    -- clip the player to the walls
    if (get_map(player.x, player.y) ~= 0) then
	player.x = ox
	player.y = oy
    end
end

-- gets the wall color of the map, with bounds checking
function get_map(x, y)
x = math.floor(x)
y = math.floor(y)

    if (x < 1 or y < 1 or x > map.width or y > map.height) then
	return 1
    end

    return map[y][x]
end

-- casts a ray starting at (x, y), pointing at ang direction...
-- returns the distance the ray travelled until it hit a wall, and the wall's color
function cast_ray(x, y, ang)
local dist = 0
local col = 1
local dx = math.cos(ang * math.pi / 180) * config.ray_resolution
local dy = math.sin(ang * math.pi / 180) * config.ray_resolution

    repeat
	x = x + dx -- travel
	y = y + dy
	dist = dist + config.ray_resolution -- track distance travelled
	col = get_map(x, y)

	if (col ~= 0) then -- is map location still blank?
	    break -- no? so we hit a wall... stop casting
	end
    until (false)

    -- return the distance travelled, and the color of the wall that finally stopped our ray
    return dist, col
end
The Löve version is so much shorter and simpler. Yet slower.

I mean look at the DOOM-style engine clone I posted in the other thread which does even more than the much more simple Wolfenstein-style engine (i.e. different height floors and ceilings, textures on the floors, sprites, doors...) and is still super fast.

It's so weird really. Something is causing the bottleneck slowdown.
User avatar
Robin
The Omniscient
Posts: 6506
Joined: Fri Feb 20, 2009 4:29 pm
Location: The Netherlands
Contact:

Re: Raycasting Demo (like Wolfenstein3D, just veery basic)

Post by Robin »

Perhaps the double table lookups for math functions?

math.cos translates to: getglobal 'math', getdotted 'cos' while cos translates to: getlocal 0 (or something like that, and assuming cos is a local)

Also repeat ... until was slow, wasn't it?
Help us help you: attach a .love.
Post Reply

Who is online

Users browsing this forum: No registered users and 49 guests