Alustame piltide ja helide (võib esialgu ära jätta) laadimisega:
function loadImages(sources, callback) {
var images = {};
var loadedImages = 0;
var numImages = 0;
// get num of sources
for(var src in sources) {
numImages++;
}
for(var src in sources) {
images[src] = new Image();
images[src].onload = function() {
if(++loadedImages >= numImages) {
callback(images);
}
};
images[src].src = sources[src];
}
}
window.onload = function() {
var canvas = document.getElementById("gameCanvas");
ctx = canvas.getContext("2d");
var sources = {
treesprite: "./images/winter/puud_lumes.gif",
pakid: "./images/winter/pakid.png",
santa_ani: "images/winter/santad1.png",
flake: "images/winter/flake.gif",
lamp: "images/winter/lamp64.png"
};
loadImages(sources, initGame);
}
Kui kõik on laetud käivitub mängu initsialiseerimisfunktsioon:
var initGame = function(images){
wt = 64; // module - width of a tree tile
ht = 64; //kui ebasümmeetrilised
var k = 2; // how many tiles per side
var n = 6; // how many squares in row/column on screen
var vl = n+10; //number of columns in (virtual) labyrinth
var rl = 20; //number of rows in (virtual) labyrinth
d = k * wt; // width of one labyrinth square
wc = n * d+wt; //canvas width
hc = (n-1) * d+ht; //canvas height
w0 = vl*d+wc; //width of virtual canvas
h0 = rl*d+wt; //height of virtual canvas
xl = 0; //start position of labyrinth on screen
yl = 0; //start position of labyrinth
ctx.canvas.width = wc;
ctx.canvas.height = hc;
canvas0 = document.createElement('canvas');
ctx0 = canvas0.getContext('2d');
//canvas0.id = "VirtualCanvas";
canvas0.width = w0;
canvas0.height = h0;
pick_snd = document.getElementById("pick");
fanfare_snd = document.getElementById("fanfare");
trees = new spriteSheet(images.treesprite,[[0,0,64,64],[65,0,64,64],[128,0,64,64],[192,0,64,64]]);
pakid = new spriteSheet(images.pakid,[[0,0,64,64],[64, 0, 64, 64],[128,0,64, 64],[192, 0, 64, 64],[0,64,64,64],[64, 64, 64, 64],[128,64,64, 64],[192, 64, 64, 64],[0,128,64,64],[64, 128, 64, 64],[128,128,64, 64],[192, 128, 64, 64],[0,192,64,64],[64, 192, 64, 64],[128,192,64, 64],[191, 192, 64, 64]]);
rooms = create(vl,rl,n,wt,ht,d,w0,h0,k);
map = draw_labyrinth(rooms,trees,d,k,vl,rl,wt,ht,w0,h0);
labyr = ctx0.getImageData(0, 0, w0, h0);
ctx.putImageData(labyr, xl,yl,-xl, -yl,wc,hc)
}
Nüüd võib juba testida, näit käsuga ctx.drawImage(images.treesprite,0,0) peaks kanvaale ilmuma puude spriit.
Spriidilehe põhjal loodud animeerimata staatilised objektid - puud (ja pakid) luuakse juba tuttava klassiga :
function spriteSheet(img,coords){
this.coords = coords;
//coords = [[x0,y0,w0,h0],...[xn,yn,wn,hn]]
this.sheet = img;
this.draw = function(nr,x,y){
ctx.drawImage(img,this.coords[nr][0],this.coords[nr][1],this.coords[nr][2],this.coords[nr][3],x,y,this.coords[nr][2],this.coords[nr][3]);
}
this.draw0 = function(nr,x,y){
ctx0.drawImage(img,this.coords[nr][0],this.coords[nr][1],this.coords[nr][2],this.coords[nr][3],x,y,this.coords[nr][2],this.coords[nr][3]);
}
}
Animatsiooniga objekt jõuluvana luuakse klassiga animated; jõuluvanal on 4 animatsiooni:
function animated(img,dx,dy,states, n_fr,state,pos,sp){ //states - array of coordinates
this.image = img;
//console.log('santa: '+this.image.height);
this.width = dx;
this.height = dy;
this.states = states;
this.dx = dx; //this.width;
this.dy = dy; //this.height/3; //32
this.n_frames = n_fr; //number of frames
this.actualFrame = 0;
this.speed = sp; //how many *game* frames a frame is shown
this.step = 0;
this.y = 0;
this.x = 0;
this.state = state;
this.pos = pos;
this.setPosition = function(X, Y){
this.x = X;
this.y = Y;
}
this.draw = function(cx,cy){
var cx=cx||0;
var cy=cy||0;
if (this.pos == 0) //still
ctx.drawImage(this.image,santa.states[santa.state][0],santa.states[santa.state][1],this.dx,this.dy,this.x+xl,this.y+yl,this.dx,this.dy);
else { // walking
ctx.drawImage(this.image,santa.states[santa.state][0],santa.states[santa.state][1] + this.dy*this.actualFrame,this.dx,this.dy,this.x+xl,this.y+yl,this.dx,this.dy);
this.step ++;
if (this.step == this.speed) {
this.actualFrame++;
//this.actualFrame = this.actualFrame%this.n_frames 1;
if (this.actualFrame == this.n_frames)
this.actualFrame = 1;
this.step = 0;
}
}
}
}
Labyrint luuakse nn 'uuristamismeetodiga' - algul on labyrindiga ühendatud vaid sissepääs (vasak ülanurk); igal sammul liidetakse uus veel ühendamata ruum, kui selle kõrval on juba ühendatud ruum (luuakse nende vahele uks):
function create(vl,rl,n,wt,ht,d,w0,h0,k){
var ruudud = [];
var connected = [];
var free = [];
var dirs = [0, 1, 2, 3];
for (i = 0; i < rl; i++) {
for (var j = 0; j < vl; j++) {
ruudud.push(new ruut(i, j));
}
}
var i0 = random(0,ruudud.length);
connected.push(ruudud[i0]);
//esimene ruum labyrindis
free = ruudud.slice(0);
free.splice(i0, 1);
//Next line starts Firebug debugger!
//debugger;
while (free.length > 0) {
var found = 0;
while (found == 0) {
i0 = random(0, connected.length);
var r = connected[i0];
//kandidaat!
var dirs = [0, 1, 2, 3];
for (i = 0; i < 4; i++) {
if (r.doors.indexOf(dirs[i]) > 0)
dirs.splice(i, 1);
}
while (dirs.length > 0) {
var j0 = random(0, dirs.length);
var d1 = dirs[j0];
var i1 = next(r, d1,free);
if (i1 >= 0) {
var r1 = free[i1];
found = 1;
r.doors.push(d1);
var d2 = reverse(d1);
r1.doors.push(d2);
connected.push(r1);
free.splice(i1, 1);
break;
} else {
dirs.splice(j0, 1);
}
}
}
}
return connected;
}
function draw_labyrinth(connected,trees,d,k,vl,rl,wt,ht,w0,h0){
map = [];
for(var i=0;i < vl*k+1;i++) //colums in virtual
map[i]=[];
for(var i=0;i < vl*k+1;i++)
for(var j=0;j < rl*k+1;j++)
map[i][j]=0;
for (var i = 0; i < connected.length; i++) {
var xx = connected[i].m;
var yy = connected[i].n;
if (connected[i].doors.indexOf(0) == -1){
treeline((xx + 1) * d, yy * d, (xx + 1) * d, (yy + 1) * d, wt,ht, trees);
for(var i1=yy*k;i1 < (yy+1)*k;i1++)
map[(xx+1)*k][i1]=1; }
if (connected[i].doors.indexOf(1) == -1){
treeline(xx * d, (yy + 1) * d, (xx + 1) * d+1, (yy + 1) * d, wt,ht, trees);
for(var i1=xx*k;i1 < =(xx+1)*k;i1++)
map[i1][(yy+1)*k]=1; }
if (xx == 0 && connected[i].doors.indexOf(2) == -1){
treeline(xx * d, yy * d, xx * d, (yy + 1) * d, wt,ht, trees);
for(var i1=yy*k;i1 < (yy+1)*k;i1++)
map[xx*k][i1]=1; }
if (yy == 0 && connected[i].doors.indexOf(3) == -1){
treeline(xx * d, yy * d, (xx + 1) * d, yy * d, wt,ht, trees);
for(var i1=xx*k;i1 < (xx+1)*k;i1++)
map[i1][yy*k]=1; }
}
ctx0.clearRect(w0-wt,h0-2*ht,wt,ht); // door out!
map[vl*k][rl*k-1]=0;
return map;
}
function next(r, dir,free) {
var nn1;
var mm1;
switch (dir) {
case 0:
nn1 = r.n;
mm1 = r.m + 1;
break;
case 1:
nn1 = r.n + 1;
mm1 = r.m;
break;
case 2:
nn1 = r.n;
mm1 = r.m - 1;
break;
case 3:
nn1 = r.n - 1;
mm1 = r.m;
break;
default:
nn1 = r.n;
mm1 = r.m;
}
for (var i = 0; i < free.length; i++) {
if ((free[i].n == nn1) && (free[i].m == mm1)) {
return i;
break;
}
}
return -1;
}
function connection(n, m, direction) {
//direction=1 - right
//direction=2 - down
//direction=3 - left
//direction=4 - up
switch (direction) {
case 0:
this.n1 = n;
this.m1 = m;
this.n2 = n + 1;
this.m2 = m;
this.dir = 0;
break;
case 1:
this.n1 = n;
this.m1 = m;
this.n2 = n;
this.m2 = m + 1;
this.dir = 1;
break;
case 2:
this.n1 = n;
this.m1 = m;
this.n2 = n - 1;
this.m2 = m;
this.dir = 2;
break;
case 3:
this.n1 = n;
this.m1 = m;
this.n2 = n;
this.m2 = m - 1;
this.dir = 3;
break;
default:
this.n1 = n;
this.m1 = m;
this.n2 = n;
this.m2 = m;
this.dir = 0;
//no connection
}
}
function reverse(dir) {
return (dir + 2) % 4;
}
function ruut(n, m) {
this.n = n;
this.m = m;
this.doors = [];
}
function treeline(x0,y0,x1,y1,wt,ht, trees){
var nx = (x1 - x0)/wt;
var ny = (y1 - y0)/ht;
var n = Math.max(nx,ny);
var dx = (x1 - x0)/n;
var dy = (y1 - y0)/n;
for(var i=0;i < n;i++){
trees.draw0(Math.floor(Math.random()*trees.coords.length),x0 + i*dx, y0 + i*dy);
}
}
Kui need klassid on lisatud, võib juba kontrollida: lisada initsialiseerimisfunktsiooni lõppu metsa ja jõuluvana joonistamise:
xb = wt/2+(d-santa.width)/2; //santai (bogy) (alg)asend virtuaalses labyrindis
yb = ht/2 + (d-santa.height)/2;
santa.setPosition(xb,yb);
santa.draw();
rooms = create(vl,rl,n,wt,ht,d,w0,h0,k);
map = draw_labyrinth(rooms,trees,d,k,vl,rl,wt,ht,w0,h0);
labyr = ctx0.getImageData(0, 0, w0, h0);
ctx.putImageData(labyr, xl,yl,-xl, -yl,wc,hc)
-ekraanile peaks ilmuma mets ja jõuluvana.
Pakid jagab metsa laiali funktdsioon placepakid, nad joonistab 8lisab metsale) drawpakid:
function placepakid(v,r,d,n){
//select random squares for mushrooms
var indexis=[];
var pakikohad1 = []; //ajutine massiiv otsimiseks
for(var i = 0; i < v*r-1; i++) indexis[i] = i+1; //ruudud alates teisest - esimeses on
var i0 = Math.floor(Math.random()*indexis.length);
pakikohad1.push(indexis[i0]);
indexis.splice(i0,1); //korduste vältimine !
i0 = Math.floor(Math.random()*indexis.length); //next!
while(pakikohad1.length < pakke && pakikohad1.indexOf(indexis[i0])==-1){
pakikohad1.push(indexis[i0]);
indexis.splice(i0,1);
i0 = Math.floor(Math.random()*indexis.length);
}
//console.log(pakikohad);
for (var i = 0; i < pakke; i++) {
var v1 = pakikohad1[i]%v;
var r1 = Math.floor(pakikohad1[i]/v);
var sx = v1*d + d/2;
var sy = r1*d + d/2;
pakikohad.push({r:indexis[i0],x:sx,y:sy, seen:Math.floor(Math.random()*n)});
}
return pakikohad;
}
function drawpakid() {
ctx0.clearRect(0,0,w0,h0);
ctx0.putImageData(labyr, 0,0);
for(var i=0;i < pakikohad.length; i++) {
pakid.draw0(pakikohad[i].seen,pakikohad[i].x, pakikohad[i].y );
}
Pakkide lisamiseks lisame vastavate funktsioonide käivitamise ka mängu initsialiseerimisfunktsioonile:
pakke = 40; //kui palju lisatakse mängu (peab olema < v*r-1 - esimene ruut santa jaoks!) pakke = Math.min(vl*rl-1,pakke); pakikohad = []; //ruudud, kus pakid paiknevad korjatud = 0; pakikohad = placepakid(vl,rl,d,pakid.coords.length); drawpakid(); pakimets = ctx0.getImageData(0, 0, w0, h0); ctx.putImageData(pakimets, xl,yl,-xl, -yl,wc,hc)
- viimase reaga peaks ekraanile ilmuma juba pakke täis mets.
Jõuluvana juhitakse klaviatuuriga, lisame klahvide seisu salvestamise:
keysDown = []; keysDown[37]=keysDown[38]=keysDown[39]=keysDown[40]=false;
Jõuluvana juhtimise testimiseks käivitame mängu kaadrivahetuse: lisame initsialiseerimisfunktsiooni lõppu
GameLoop();
Funktsioon GameLoop juhib kogu mängu; igas kaadris kontrollib funktsioon update jõuluvana liikumist (kasutab kokkupõrkefunktsioone)
function GameLoop(){
update(speed); //speed - mitu pikslit kaadris, tuleb anda initsialiseerimisel
if (santa.x+xl > wc/2+d && wc - xl < w0)
{xl -= speed; //metsa skrollimine
}
else if (santa.x+xl < wc/2 && xl > 0)
{xl += speed;
}
if (santa.y+yl > hc/2 && hc - yl < h0)
{yl -= speed; }
else if (santa.y+yl < hc/2-d && yl < 0)
{yl += speed;}
ctx.clearRect(0,0,wc,hc);
ctx.drawImage(canvas0, -xl,-yl,wc,hc,0,0,wc,hc); //pakimets
santa.draw();
//lumehelbed ja valgus - kui veel pole, tuleb välja kommenteerida
/*
ctx.globalAlpha=0.4;
ctx.globalCompositeOperation = "source-over";
ctx.globalCompositeOperation = "lighter";
ctx.drawImage(light,0,0,64,64,0,0,wc,hc);
for(var i=0;i < nrOfFlakes;i++)
{flakes[i].update();}
ctx.globalAlpha = 1;
*/
setTimeout(GameLoop, 30);
}
function update(sp){
var dx = 0;
var dy = 0;
if (keysDown[37]) { // left arrow
dx = -sp;
santa.state = "vasakule";
santa.pos = 1;
}
else if (keysDown[38]) { // up arrow
dy = -sp;
santa.state = "sinna";
santa.pos = 1;
}
else if (keysDown[39]) { // right arrow
dx = sp;
santa.state = "paremale";
santa.pos = 1;
}
else if (keysDown[40]) { // down arrow
dy = sp;
santa.state = "siia";
santa.pos = 1;
}
else
santa.pos = 0;
if(dx !=0 || dy !=0) {
santa.pos = 1; //walking
check_collision1(dx,dy,santa);
}
else
santa.pos = 0; //still
};
function collision(x1,y1,w1,h1,x2,y2,w2,h2){
return x1 < x2 + w2 && x2 < x1 + w1 &&
y1 < y2 + h2 && y2 < y1 + h1
}
function check_collision(dx,dy,obj1,obj2){ //obj1 is moving, obj2 is bitmapdata to check
var x = obj1.x;
var y = obj1.y;
var wb = obj1.width;
var hb = obj1.height;
var points1;
if (dx < 0) {
if (dy == 0)
points1 = [[x,y],[x,y+hb]];
else if (dy > 0)
points1 = [[x,y],[x,y+hb],[x+wb,y+hb]];
else if (dy < 0)
points1 = [[x+wb,y],[x,y],[x,y+hb]];
};
if (dx == 0) {
if (dy > 0)
points1 = [[x,y+hb],[x+wb,y+hb]];
else if (dy < 0)
points1 = [[x+wb,y],[x,y]];
};
if (dx > 0) {
if (dy == 0)
points1 = [[x+wb,y+hb],[x+wb,y]];
else if (dy > 0)
points1 = [[x,y+hb],[x+wb,y+hb],[x+wb,y]];
else if (dy < 0)
points1 = [[x+wb,y+hb],[x+wb,y],[x,y]];
};
var t = 0;
var j = Math.max(Math.abs(dx),Math.abs(dy)); //how many steps to check
var ddx = dx/j;
var ddy = dy/j;
found: do{
t++;
var i = 0;
do{
var x1 = points1[i][0]; //coordinate of the faremost corner
var y1 = points1[i][1];
var xx1 = (x1+t*ddx);
var yy1 = (y1+t*ddy);
var p = 4*(xx1 + w0*yy1);
if (obj2.data[p+3]!=0 ) // not transparent!
{ break found; };
i++;
} while (i < points1.length);
} while (t < j) ;
xb = Math.floor(xb + (t-1)*ddx); //move obj1 !
yb = Math.floor(yb + (t-1)*ddy);
santa.setPosition(xb,yb);
korja();
}
function check_collision1(vx,vy,obj1){
//vx,vy - move of obj1 on next step
// collision is based on map
var x = obj1.x;
var y = obj1.y;
var wb = obj1.width;
var hb = obj1.height;
var points1;
if (vx < 0) {
if (vy == 0)
points1 = [[x,y],[x,y+hb]];
else if (vy > 0)
points1 = [[x,y],[x,y+hb],[x+wb,y+hb]];
else if (vy < 0)
points1 = [[x+wb,y],[x,y],[x,y+hb]];
};
if (vx == 0) {
if (vy > 0)
points1 = [[x,y+hb],[x+wb,y+hb]];
else if (vy < 0)
points1 = [[x+wb,y],[x,y]];
};
if (vx > 0) {
if (vy == 0)
points1 = [[x+wb,y+hb],[x+wb,y]];
else if (vy > 0)
points1 = [[x,y+hb],[x+wb,y+hb],[x+wb,y]];
else if (vy < 0)
points1 = [[x+wb,y+hb],[x+wb,y],[x,y]];
};
var t = 0;
var j = Math.max(Math.abs(vx),Math.abs(vy)); //how many steps to check
var dvx = vx/j;
var dvy = vy/j;
found: do{
t++;
var i = 0;
do{
var x1 = points1[i][0]; //coordinate of the faremost corner
var y1 = points1[i][1];
var xx1 = (x1+t*dvx);
var yy1 = (y1+t*dvy);
var v = Math.floor(xx1/wt);
var r = Math.floor(yy1/ht);
if (map[v][r]!=0 ) // not transparent!
{
break found; };
i++;
} while (i < points1.length);
} while (t < j) ;
xb = Math.floor(xb + (t-1)*dvx); //move obj1 !
yb = Math.floor(yb + (t-1)*dvy);
obj1.setPosition(xb,yb);
korja();
}
Järgnev funktsioon on pakkide korjamiseks:
function korja(){
// kontrollime ruudu [xb,yb,32,32] lõikumist
// ruuduga [pakikohad[i].x, pakikohad[i].y, 32,32]
var i = 0;
while (i < pakikohad.length){
if (collision(xb,yb,wt,ht,pakikohad[i].x,pakikohad[i].y,wt,ht))
break;
else
i++;}
if (i < pakikohad.length) //oli seen!
{
korjatud ++;
pick_snd.cloneNode(true).play();
inf.innerHTML = 'Korjatud '+korjatud + ' pakki!';
pakikohad.splice(i,1);
drawpakid(); //uued!
}
}
Helbed luuakse klassidega
function snowFlake(img, xPos, yPos) {
// set initial snowflake properties
this.img = img;
this.w = img.width;
this.h = img.height;
//this.r = r;
this.v = 1+4*Math.random();
this.xPos = xPos;
this.yPos = yPos;
this.suund = (1+2*Math.random())*((Math.random()>0.5)?1:-1);
// declare variables used for snowflake's motion
//this.counter = 0;
this.sign = (Math.random()>0.5)?1:-1; ////Math.floor(Math.random() * 2);
// setting an initial opacity and size for our snowflake
this.opacity = 0.1+Math.random();
this.size = 0.5 + Math.random(); //0.5 .. 1.5
// the function responsible for actually moving our snowflake
this.update = function () {
this.xPos += this.suund; //this.sign*this.v*Math.cos(this.counter)/20;
this.yPos += this.v;
if (Math.random() < 0.01)
this.suund = (1+2*Math.random())*((Math.random()>0.5)?1:-1);
// setting our snowflake's position
ctx.globalAlpha = this.opacity;
ctx.drawImage(this.img,this.xPos,this.yPos);
// if snowflake goes below the browser window, move it back to the top
if (this.yPos > hc) {
this.yPos = -50;
}
}
}
function createFlakes(img,nr){
for (var i = 0; i < nr; i++) {
// create new instance
var x0 = 10 + (w0-20)*Math.random();
var y0 = (h0-20)*math.random();
var flake = new SnowFlake(img,x0,y0);
flakes.push(flake);
}
return flakes;
}
Helbed salvestatakse massiivis, mis luuakse mängu initsialiseerimisel:
nrOfFlakes = 25;
flakes = [];
for (var i = 0; i < nrOfFlakes; i++) {
// create new instance
var x0 = 10 + Math.floor((wc-20)*Math.random());
var y0 = Math.floor((hc-20)*Math.random());
//console.log(x0+','+y0);
var flake = new snowFlake(images.flake,x0,y0);
flakes.push(flake);
}
Valgustus on vaid üks spriit, mis mängu initsialiseerimisel salvestatakse eraldi kanvaal
light = document.createElement('canvas');
ctxL = light.getContext('2d');
ctxL.drawImage(images.lamp,0,0);
Mängu ajal) funktsioonis GameLoop) venitatakse see üle kogu kanvaa (ülal välja kommenteeritud).
Ülesandid
1. Modifitseeri helveste skripti, nii et tekiks erineva suurusega helbeid.
2. Modifitseeri valgustust - praegu ripub lamp mänguvälja keskel - tee nii, et see valgustaks jõuluvana ja liiguks koos temaga.