Posts Tagged ‘js1k’


A while ago, maybe a few months, I made a small racing style game 3d engine for the js1k competition. Unfortunately there was a submission error after I submitted it and I didn’t get a chance to resubmit. too bad, because I tested it in 5 different browsers to make sure it would pass everything… anyway I thought I would share my code for anyone interested.

The engine is programmed in JavaScript so it can simply run inside of a standard web browser. It isn’t a regular 3d-engine, it’s actually pseudo-3d. It takes a 2d map and copies pixels from the map in a 3d-like manner, then translating those pixels onto the screen. I believe this is called a mode7 engine, originally used on the super Nintendo for Mario Kart games. It is a quick and efficient way of rendering from a 2d-map to the screen.  Below is a screenshot of the program in action.

This was renderered off a 2d map shown below, can you guess where the viewer is and in what direction they are looking?

Below is the code I used for the entire demonstration, for fun I am also putting in the code I submitted for the js1k competition as well. Basically it is just the same code, but compressed into 1024 bytes or 1kb.

This can be done by using tricks such as;

  • Placing all code on one line – new lines take up space.
  • Renaming functions to smaller names – such a R instead of Redraw
  • Placing re-usable/re-used identifiers into variables – such as m=Math; so we can reference the Math path with just an m.
  • Using g=’#ccc’; instead of g=’grey’ – we can save one character

There are many other tricks to reduce code length size but here is the code already:

function G(){

 C.fillStyle=g;
 C.fillRect(0,0,w,h);
 r=C.getImageData(0,0,w,h);
 C.fillStyle='#0b0';
 C.strokeStyle='#000';
 C.lineWidth=14;
 C.moveTo(f,u); 
 C.quadraticCurveTo(u,u,u,f);
 C.quadraticCurveTo(u,320,f,320);
 C.quadraticCurveTo(h,320,h,f);
 C.quadraticCurveTo(h,u,f,u);
 C.fill();
 C.stroke();
 C.font="16px Calibri"; 
 C.fillStyle='red'; 
 C.fillText("JS1K",200,85);
 I=C.getImageData(0,0,w,h);

}

function s(u,v){return x+(u-x)*m.cos(d)-(v-y)*m.sin(d)}

function t(u,v){return y+(u-x)*m.sin(d)+(v-y)*m.cos(d)}

function R(){
x=s(x,y-3);
y=t(x,y-3);

for(v=f;v<h;v++){
for(u=0;u<w;u++){

 e=(v-f)/f;
 a=f-f*e;

 i=x-a/2+a*u/w;
 j=y-a+a*e;

 o=parseInt(t(i,j));
 p=parseInt(s(i,j));

 nn=4*((v*w)+u);
 if(o<0||p<0||o>h||p>w){r.data[nn]=r.data[nn+1]=r.data[nn+2]=204;r.data[nn+3]=255;}else{
 n=4*((o*w)+p);
 r.data[nn]=I.data[n];
 r.data[nn+1]=I.data[n+1];
 r.data[nn+2]=I.data[n+2];
 r.data[nn+3]=255;

 }

 }
}
C.putImageData(r,0,0);
}

D=document;
D.addEventListener('keyup',function(e){k=e.keyCode;if(k==65){d-=.1}if(k==68){d+=.1}R()},true);
C=D.getElementById("myCanvas").getContext("2d");
m=Math;u=80;g='#ccc';
w=600;h=400;f=h/2;
x=150;y=85;d=1.5;r=0;
setInterval('R()',90);
G();
 

You may notice that I have used putImageData(); to write pixel data to the screen. This was faster than any other method I was experimenting in, in terms of messing around with one canvas and drawing to another on a pixel-by-pixel basis.

And here is the 1024 bytes long code for the engine.

function s(u,v){return x+(u-x)*m.cos(d)-(v-y)*m.sin(d)}
function t(u,v){return y+(u-x)*m.sin(d)+(v-y)*m.cos(d)}
function R(){x=s(x,y-3);y=t(x,y-3);for(v=f;v<h;v++){for(u=0;u<w;u++){e=(v-f)/f;a=f-f*e;i=x-a/2+a*u/w;j=y-a+a*e;o=parseInt(t(i,j));p=parseInt(s(i,j));z=4*((v*w)+u);if(o<0||p<0||o>=h||p>w){for(n=0;n<3;n++){r.data[z+n]=204*e+204*(1-e)}}else{for(n=0;n<3;n++){r.data[z+n]=I.data[4*((o*w)+p)+n]*e+204*(1-e)}}}}C.putImageData(r,0,0);}
D=document;D.addEventListener('keyup',function(e){k=e.keyCode;if(k==65){d-=.1}if(k==68){d+=.1}R()},true);
C=D.getElementById("myCanvas").getContext("2d");m=Math;g='#ccc';w=600;h=400;f=h/2;x=150;y=85;d=1.5;u=80;
C.fillStyle=g;C.fillRect(0,0,w,h);r=C.getImageData(0,0,w,h);C.fillStyle='#0b0';C.lineWidth=14;C.moveTo(f,u);C.quadraticCurveTo(u,u,u,f);C.quadraticCurveTo(u,320,f,320);C.quadraticCurveTo(h,320,h,f);C.quadraticCurveTo(h,u,f,u);C.fill();C.stroke();C.font="16px Calibri";C.fillStyle='red';C.fillText("JS1K",200,85);I=C.getImageData(0,0,w,h);
setInterval('R()',1);

More 2 come, when I get around to revising/editing and adding to this post!