Koitan tehdä Space Invaders-tyylistä peliä ja tarvitsisin apuja koodin kanssa. Miksi kuvat välkkyy satunnaisesti? Miten viholliseen saa liikettä? Varmaankin SIN funktiolla saa sopivan liikeradan. En halua että vihollinen liikkuu niin kaavamaisesti kun alkuperäsessä.
<html> <head> <title>Space Invaders</title> <style type="text/css"> #canvas { background-color: #fff; } </style> </head> <body bgcolor="red"> <canvas id="canvas" width="400" height="400"></canvas> <script type="text/javascript"> var canvas; var ctx; var dx = 5; var x = 200; var y = 370; var enemyx = 200; var enemyy = 10 var WIDTH = 400; var HEIGHT = 400; var img; function hero(x,y) { ctx = document.getElementById('canvas').getContext('2d'); var img = new Image(); img.onload = function(){ ctx.drawImage(img,x,y); } img.src = "img/hero.png"; } function enemy(enemyx,enemyy) { ctx = document.getElementById('canvas').getContext('2d'); var img = new Image(); img.onload = function(){ ctx.drawImage(img,enemyx,enemyy); } img.src = "img/enemy.png"; } function rect(x,y,w,h) { ctx.beginPath(); ctx.rect(x,y,w,h); ctx.closePath(); ctx.fill(); ctx.stroke(); } function clear() { ctx.clearRect(0, 0, WIDTH, HEIGHT); } function init() { canvas = document.getElementById("canvas"); ctx = canvas.getContext("2d"); return setInterval(draw, 25); } function doKeyDown(evt){ switch (evt.keyCode) { case 37: if (x - dx > 0){ x -= dx; } break; case 39: if (x + dx < WIDTH){ x += dx; } break; } } function draw() { clear(); ctx.fillStyle = "white"; ctx.strokeStyle = "black"; rect(0,0,WIDTH,HEIGHT); ctx.fillStyle = "purple"; hero(x, y); enemy(enemyx, enemyy) } init(); window.addEventListener('keydown',doKeyDown,true); </script> </body> </html>
Lisäys: Vielä yksi kysymys. Miten saa näppäinpainalluksesta viiveen pois? Nyt ohjattava alus liikkuu yhden askeleen ja pysähtyy ennen kuin se liikkuu sivulle.
Lisäys:
Päätinkin piirtää alukset kankaalle, mutta en saa vihollista liikkeelle.
function enemy(enemyx,enemyy) { ctx.save(); ctx.strokeStyle = "red"; ctx.beginPath(); ctx.moveTo(enemyx+0,enemyy+0); ctx.lineTo(enemyx+10,enemyy+0); ctx.lineTo(enemyx+5,enemyy+10); ctx.lineTo(enemyx+0,enemyy+0); ctx.closePath(); ctx.stroke(); ctx.restore(); } function enemyAnimation(enemyx,enemyy) { canvas.width = canvas.width; enemy(); enemyx += 5; if (enemyx > 400) enemyx = 0; setTimeout(enemyAnimation, 33); }
Tuo enemyanimation homma oli vähän huonosti tuossa, ko se toimi satunnaiseen aikaan verrattuna pää draw metodiin. Postin vähän turhia muuttujia ja rivejä tosta nii nyt se varmaa toimii silleen miten halusit:
<html> <head> <title>Space Invaders</title> <style type="text/css"> #canvas { background-color: #fff; } </style> </head> <body bgcolor="red"> <canvas id="canvas" width="400" height="400"></canvas> <script type="text/javascript"> var canvas; var ctx; var dx = 5; var x = 200; var y = 370; var enemyx = 200; var enemyy = 10; var WIDTH = 400; var HEIGHT = 400; var img; function hero(x,y) { ctx = document.getElementById('canvas').getContext('2d'); var img = new Image(); img.onload = function(){ ctx.drawImage(img,x,y); } img.src = "img/hero.png"; } function enemy(enemyx,enemyy) { ctx.save(); ctx.strokeStyle = "red"; ctx.beginPath(); ctx.moveTo(enemyx+0,enemyy+0); ctx.lineTo(enemyx+10,enemyy+0); ctx.lineTo(enemyx+5,enemyy+10); ctx.lineTo(enemyx+0,enemyy+0); ctx.closePath(); ctx.stroke(); ctx.restore(); } function enemyAnimation() { enemyx += 5; if (enemyx > 400) enemyx = 0; enemy(enemyx,enemyy); } function rect(x,y,w,h) { ctx.beginPath(); ctx.rect(x,y,w,h); ctx.closePath(); ctx.fill(); ctx.stroke(); } function clear() { ctx.clearRect(0, 0, WIDTH, HEIGHT); } function init() { canvas = document.getElementById("canvas"); ctx = canvas.getContext("2d"); return setInterval(draw, 25); } function doKeyDown(evt){ switch (evt.keyCode) { case 37: if (x - dx > 0){ x -= dx; } break; case 39: if (x + dx < WIDTH){ x += dx; } break; } } function draw() { clear(); ctx.fillStyle = "white"; ctx.strokeStyle = "black"; rect(0,0,WIDTH,HEIGHT); ctx.fillStyle = "purple"; hero(x, y); enemyAnimation(); } init(); window.addEventListener('keydown',doKeyDown,true); </script> </body> </html>
Löytyy myös linkistä: http://q-q.name/spaceinv/
Tosiaan varmaaan enemmän nuita vihollisia tuohon haluat. Se varmaan yksinkertaista alkuun toteuttaa enemyx ja enemyy taulukoilla jossa vaikka liikutat kaikkia aina samanverran eteenpäin samaan tapaan kuin tuota yhtä.
Muuttuja määritykset siis:
var enemyx = new Array(); var enemyy = new Array();
initissä:
for (var i=0;i<10;i++) { enemyx[i]=i*20; enemyy[i]=10; }
sit enemyanimationissa:
for (var i=0;i<10;i++) { enemyx[i] += 5; if (enemyx[i] > 400) enemyx[i] = 0; enemy(enemyx[i],enemyy[i]); }
Paras se varmaan olisi tehdä objekteja nuista vihollisista niin niille saisi helpolla muitaki muuttujia kuten elämän.
Kiitos! Tästä oli paljon apua.
Vielä pari ongelmaa tuli vastaan. Miksi viholliset lähtevät liikkumaan miten sattuu ja miksi ammuksesta jää jälki perään? Kuitenkaan aluksista ei jää jälkeä.
<html> <head> <title>Space Invaders</title> <style type="text/css"> #canvas { background-color: #fff; } </style> </head> <body bgcolor="gray"> <canvas id="canvas" width="400" height="400"></canvas> <script type="text/javascript"> var canvas; var ctx; var x = 200; var y = 370; var bulletx = x; var bullety = y; var enemyx = new Array(); var enemyy = new Array(); var ex = 5; var ey = 1; var dx = 5; var dy = 5; var WIDTH = 400; var HEIGHT = 400; var img; function hero(x,y) { ctx.beginPath(); ctx.moveTo(x+0,380); ctx.lineTo(x+10,380); ctx.lineTo(x+5,370); ctx.lineTo(x+0,380); ctx.closePath(); ctx.stroke(); } function heroShoot(x,bullety) { ctx.save(); ctx.fillStyle = "rgb(0,0,0)" ctx.beginPath(); ctx.rect(x+3,bullety,3,3); ctx.closePath(); ctx.fill(); ctx.restore(); } function shootAnimiation() { for(bullety=y; bullety > 0; bullety --) { heroShoot(x,bullety); } } function enemy(enemyx,enemyy) { ctx.save(); ctx.strokeStyle = "red"; ctx.beginPath(); ctx.moveTo(enemyx+0,enemyy+0); ctx.lineTo(enemyx+10,enemyy+0); ctx.lineTo(enemyx+5,enemyy+10); ctx.lineTo(enemyx+0,enemyy+0); ctx.closePath(); ctx.stroke(); ctx.restore(); } function enemyAnimation() { for (var i=0;i<10;i++) { enemyx[i] += ex; enemyy[i] += ey; if ((enemyx[i] > 400) || (enemyx[i] < 0)) ex = -ex; if (enemyy[i] > 400) enemyy[i] = 0; enemy(enemyx[i],enemyy[i]); } } function rect(x,y,w,h) { ctx.beginPath(); ctx.rect(x,y,w,h); ctx.closePath(); ctx.fill(); ctx.stroke(); } function clear() { ctx.clearRect(0, 0, WIDTH, HEIGHT); } function init() { canvas = document.getElementById("canvas"); ctx = canvas.getContext("2d"); for (var i=0;i<10;i++) { enemyx[i]=i*20; enemyy[i]=10; } return setInterval(draw, 33); } function doKeyDown(evt){ switch (evt.keyCode) { case 37: /* Left arrow was pressed */ if (x - dx > 0){ x -= dx; } break; case 39: /* Right arrow was pressed */ if (x + dx < WIDTH){ x += dx; } break; case 38: /* Up arrow was pressed */ shootAnimiation(); break; } } function draw() { clear(); ctx.fillStyle = "white"; ctx.strokeStyle = "black"; rect(0,0,WIDTH,HEIGHT); ctx.fillStyle = "purple"; hero(x, y); enemyAnimation(); } init(); window.addEventListener('keydown',doKeyDown,true); </script> </body> </html>
Se miksi ne viholliset käyttäytyy erikoisesti johtuu siitä, että tuo ex kun tuossa kääntyy ympäri 0:n tai 400:n ylityksen tapahtuessa, niin osalle vihollisista käytetään eri ex arvoa saman for loopin sisällä.
No muutin tuota koodia vähän niin se lähti toimimaan, tein myös vähä helpommin itelle ymmärrettävän tuosta enemyanimationista. Ja lisäsin tuon ampumisen perus draw looppiin. Se miksi se viimeksi piirsi vain viivan, johtui siistä että se toimi vaan yhden framen ajan ja koko ampuminen tapahtui yhdessä framessa. En kyllä tiedä tarkasti minkälaisen tuosta ampumisesta haluaisit. Mutta varmaan kumminkin jotain mikä ei tuolleen yhden framen ajan vaan toimi.
On taas tuollakin: http://q-q.name/spaceinv/
<html> <head> <title>Space Invaders</title> <style type="text/css"> #canvas { background-color: #fff; } </style> </head> <body bgcolor="gray"> <canvas id="canvas" width="400" height="400"></canvas> <script type="text/javascript"> var canvas; var ctx; var x = 200; var y = 370; var bulletx = x; var bullety = -1; var enemyx = new Array(); var enemyy = new Array(); var direction="right"; var dx = 5; var dy = 5; var WIDTH = 400; var HEIGHT = 400; var img; function hero(x,y) { ctx.beginPath(); ctx.moveTo(x+0,380); ctx.lineTo(x+10,380); ctx.lineTo(x+5,370); ctx.lineTo(x+0,380); ctx.closePath(); ctx.stroke(); } function heroShoot(x,bullety) { ctx.save(); ctx.fillStyle = "rgb(0,0,0)" ctx.beginPath(); ctx.rect(x+3,bullety,3,3); ctx.closePath(); ctx.fill(); ctx.restore(); } function shootAnimiation() { //for(bullety=y; bullety > 0; ) { bullety -=9; heroShoot(bulletx,bullety); } function enemy(enemyx,enemyy) { ctx.save(); ctx.strokeStyle = "red"; ctx.beginPath(); ctx.moveTo(enemyx+0,enemyy+0); ctx.lineTo(enemyx+10,enemyy+0); ctx.lineTo(enemyx+5,enemyy+10); ctx.lineTo(enemyx+0,enemyy+0); ctx.closePath(); ctx.stroke(); ctx.restore(); } function enemyAnimation() { var changedir=false; for (var i=0;i<10;i++) { if (direction=="right") enemyx[i] += 5; else if (direction=="left") enemyx[i] -= 5; enemyy[i]+=1; if (enemyx[i] > 400) changedir=true; else if (enemyx[i] < 0) changedir=true; if (enemyy[i] > 400) enemyy[i] = 0; enemy(enemyx[i],enemyy[i]); } if (changedir && direction=="right") direction="left" else if (changedir && direction=="left") direction="right"; } function rect(x,y,w,h) { ctx.beginPath(); ctx.rect(x,y,w,h); ctx.closePath(); ctx.fill(); ctx.stroke(); } function clear() { ctx.clearRect(0, 0, WIDTH, HEIGHT); } function init() { canvas = document.getElementById("canvas"); ctx = canvas.getContext("2d"); for (var i=0;i<10;i++) { enemyx[i]=i*20; enemyy[i]=10; } return setInterval(draw, 33); } function doKeyDown(evt){ switch (evt.keyCode) { case 37: /* Left arrow was pressed */ if (x - dx > 0){ x -= dx; } break; case 39: /* Right arrow was pressed */ if (x + dx < WIDTH){ x += dx; } break; case 38: /* Up arrow was pressed */ if (bullety<0) { bulletx=x; bullety=y; } break; } } function draw() { clear(); ctx.fillStyle = "white"; ctx.strokeStyle = "black"; rect(0,0,WIDTH,HEIGHT); ctx.fillStyle = "purple"; hero(x, y); enemyAnimation(); shootAnimiation(); } init(); window.addEventListener('keydown',doKeyDown,true); </script> </body> </html>
Kiitoksia taas! Olenko ymmärtänyt nyt oikein, että kun haluan tehdä objektin vihollisista ja ammuksista, tarvitsen molemmille kolme funktitota? Yhden itse objektille, yhden sen piirtämiselle ja yhden liikuttamaan sitä.
Suurinpiirtein nuin, mutta objektissa muut funktiot lähinnä määritellään kyseisen objektin sisälle. Muutin tuossa nuo viholliset objekteiksi, niin samaan tapaan voisi tehdä panoksille, jos haluaisi voida ampua useamman panoksen kerralla. Varmaan vihollisten ammuksille voi tehdä kokonaan oman objekti taulukon myös.
<html> <head> <title>Space Invaders</title> <style type="text/css"> #canvas { background-color: #fff; } </style> </head> <body bgcolor="gray"> <canvas id="canvas" width="400" height="400"></canvas> <script type="text/javascript"> function enemy_object() { this.x=0; this.y=0; this.health=0; this.draw = function() { ctx.save(); ctx.strokeStyle = "red"; ctx.beginPath(); ctx.moveTo(this.x+0,this.y+0); ctx.lineTo(this.x+10,this.y+0); ctx.lineTo(this.x+5,this.y+10); ctx.lineTo(this.x+0,this.y+0); ctx.closePath(); ctx.stroke(); ctx.restore(); } this.moveStep = function() { if (direction=="right") this.x += 5; else if (direction=="left") this.x -= 5; this.y+=1; if (this.x > 400) changedir=true; else if (this.x < 0) changedir=true; if (this.y > 400) this.y = 0; } } var enemy; var canvas; var ctx; var x = 200; var y = 370; var bulletx = x; var bullety = -1; var direction="right"; var changedir; var dx = 5; var dy = 5; var WIDTH = 400; var HEIGHT = 400; var img; function hero(x,y) { ctx.beginPath(); ctx.moveTo(x+0,380); ctx.lineTo(x+10,380); ctx.lineTo(x+5,370); ctx.lineTo(x+0,380); ctx.closePath(); ctx.stroke(); } function heroShoot(x,bullety) { ctx.save(); ctx.fillStyle = "rgb(0,0,0)" ctx.beginPath(); ctx.rect(x+3,bullety,3,3); ctx.closePath(); ctx.fill(); ctx.restore(); } function shootAnimiation() { bullety -=9; heroShoot(bulletx,bullety); } function enemyAnimation() { changedir=false; for (var i=0;i<enemy.length;i++) { if (enemy[i].health>0) { enemy[i].moveStep(); enemy[i].draw(); } } if (changedir && direction=="right") direction="left" else if (changedir && direction=="left") direction="right"; } function rect(x,y,w,h) { ctx.beginPath(); ctx.rect(x,y,w,h); ctx.closePath(); ctx.fill(); ctx.stroke(); } function clear() { ctx.clearRect(0, 0, WIDTH, HEIGHT); } function init() { canvas = document.getElementById("canvas"); ctx = canvas.getContext("2d"); enemy=new Array(); for (var i=0;i<10;i++) { enemy[i]=new enemy_object(); enemy[i].x=i*20; enemy[i].y=10; enemy[i].health=100; } return setInterval(draw, 33); } function doKeyDown(evt){ switch (evt.keyCode) { case 37: /* Left arrow was pressed */ if (x - dx > 0){ x -= dx; } break; case 39: /* Right arrow was pressed */ if (x + dx < WIDTH){ x += dx; } break; case 38: /* Up arrow was pressed */ if (bullety<0) { bulletx=x; bullety=y; } break; } } function draw() { clear(); ctx.fillStyle = "white"; ctx.strokeStyle = "black"; rect(0,0,WIDTH,HEIGHT); ctx.fillStyle = "purple"; hero(x, y); enemyAnimation(); shootAnimiation(); } init(); window.addEventListener('keydown',doKeyDown,true); </script> </body> </html>
Liikkuu helpommin:
<html> <head> <title>Space Invaders</title> <style type="text/css"> #canvas { background-color: #fff; } </style> </head> <body bgcolor="gray"> <canvas id="canvas" width="400" height="400"></canvas> <script type="text/javascript"> function enemy_object() { this.x=0; this.y=0; this.health=0; this.draw = function() { ctx.save(); ctx.strokeStyle = "red"; ctx.beginPath(); ctx.moveTo(this.x+0,this.y+0); ctx.lineTo(this.x+10,this.y+0); ctx.lineTo(this.x+5,this.y+10); ctx.lineTo(this.x+0,this.y+0); ctx.closePath(); ctx.stroke(); ctx.restore(); } this.moveStep = function() { if (direction=="right") this.x += 5; else if (direction=="left") this.x -= 5; this.y+=1; if (this.x > 400) changedir=true; else if (this.x < 0) changedir=true; if (this.y > 400) this.y = 0; } } var enemy; var canvas; var ctx; var x = 200; var y = 370; var bulletx = x; var bullety = -1; var direction="right"; var changedir; var dx = 5; var dy = 5; var WIDTH = 400; var HEIGHT = 400; var img; var RIGHT_MOVE = false; var LEFT_MOVE = false; function hero(x,y) { ctx.beginPath(); ctx.moveTo(x+0,380); ctx.lineTo(x+10,380); ctx.lineTo(x+5,370); ctx.lineTo(x+0,380); ctx.closePath(); ctx.stroke(); } function heroShoot(x,bullety) { ctx.save(); ctx.fillStyle = "rgb(0,0,0)" ctx.beginPath(); ctx.rect(x+3,bullety,3,3); Move(); ctx.closePath(); ctx.fill(); ctx.restore(); } function shootAnimiation() { bullety -=9; heroShoot(bulletx,bullety); } function enemyAnimation() { changedir=false; for (var i=0;i<enemy.length;i++) { if (enemy[i].health>0) { enemy[i].moveStep(); enemy[i].draw(); } } if (changedir && direction=="right") direction="left" else if (changedir && direction=="left") direction="right"; } function rect(x,y,w,h) { ctx.beginPath(); ctx.rect(x,y,w,h); ctx.closePath(); ctx.fill(); ctx.stroke(); } function clear() { ctx.clearRect(0, 0, WIDTH, HEIGHT); } function init() { canvas = document.getElementById("canvas"); ctx = canvas.getContext("2d"); enemy=new Array(); for (var i=0;i<10;i++) { enemy[i]=new enemy_object(); enemy[i].x=i*20; enemy[i].y=10; enemy[i].health=100; } return setInterval(draw, 33); } function Move() { if ( RIGHT_MOVE ) if (x + dx < WIDTH) x += dx; if ( LEFT_MOVE ) if (x - dx > 0) x -= dx; } function doKeyDown(evt){ switch (evt.keyCode) { case 37: /* Left arrow was pressed */ LEFT_MOVE = true; break; case 39: /* Right arrow was pressed */ RIGHT_MOVE = true; break; case 38: /* Up arrow was pressed */ if (bullety<0) { bulletx=x; bullety=y; } break; } } function doKeyUp(evt){ switch (evt.keyCode) { case 37: /* Left arrow was pressed */ LEFT_MOVE = false; break; case 39: /* Right arrow was pressed */ RIGHT_MOVE = false; break; case 38: /* Up arrow was pressed */ if (bullety<0) { bulletx=x; bullety=y; } break; } } function draw() { clear(); ctx.fillStyle = "white"; ctx.strokeStyle = "black"; rect(0,0,WIDTH,HEIGHT); ctx.fillStyle = "purple"; hero(x, y); enemyAnimation(); shootAnimiation(); } init(); window.addEventListener('keydown',doKeyDown,true); window.addEventListener('keyup',doKeyUp,true); </script> </body> </html>
Onko tämä edes sinne päin? Ei ainakaan toimi. Pitäisikö uuden bullet_objectin luonnille tehdä oma funktio? Mitä määrityksiä ylänuolinäppäimelle tulee?
function bullet_object(x, bullety) { this.bulletx=x; this.bullety=-1; this.draw = function() { ctx.save(); ctx.fillStyle = "rgb(0,0,0)" ctx.beginPath(); ctx.rect(this.bulletx+3,this.bullety,3,3); ctx.closePath(); ctx.fill(); ctx.restore(); } this.bulletStep = function() { this.bullety -= 9; bullet_object(bullety) } }
Ei mitenkään kaunista:
<html> <head> <title>Space Invaders</title> <style type="text/css"> #canvas { background-color: #fff; } </style> </head> <body bgcolor="gray"> <canvas id="canvas" width="400" height="400"></canvas> <script type="text/javascript"> function bullet_object() { this.bulletx=0 this.bullety=-1; this.shot = false; this.shoot = shoot; function shoot( x,y ) { if ( !this.shot ) { this.bulletx=x; this.bullety=y; this.shot = true; } } this.draw = function() { if ( this.shot ) { ctx.save(); ctx.fillStyle = "rgb(0,0,0)" ctx.beginPath(); ctx.rect(this.bulletx+3,this.bullety,3,3); ctx.closePath(); ctx.fill(); ctx.restore(); } } this.bulletStep = function() { if ( this.shot ) this.bullety -= 9; if ( this.bullety<0 ) this.shot = false; } } function Gun( magSize,frequency ) { this.magSize = magSize; this.bullets = new Array(this.magSize); for ( var i=0; i<this.magSize; i++ ) this.bullets[ i ] = new bullet_object(); this.shoot = shoot; this.GunLoop = GunLoop; this.ShootingFreq = frequency; this.Gap = 0; function shoot( x,y ) { if ( this.Gap == 0 ) { for ( var i=0; i<this.magSize; i++ ) if ( !this.bullets[ i ].shot ) { this.bullets[ i ].shoot( x,y ); break; } this.Gap = this.ShootingFreq; } } function GunLoop() { if ( this.Gap>0 ) this.Gap--; for ( var i=0; i<this.magSize; i++ ) { this.bullets[ i ].bulletStep(); this.bullets[ i ].draw(); } } } function enemy_object() { this.x=0; this.y=0; this.health=0; this.draw = function() { ctx.save(); ctx.strokeStyle = "red"; ctx.beginPath(); ctx.moveTo(this.x+0,this.y+0); ctx.lineTo(this.x+10,this.y+0); ctx.lineTo(this.x+5,this.y+10); ctx.lineTo(this.x+0,this.y+0); ctx.closePath(); ctx.stroke(); ctx.restore(); } this.moveStep = function() { if (direction=="right") this.x += 5; else if (direction=="left") this.x -= 5; this.y+=1; if (this.x > 400) changedir=true; else if (this.x < 0) changedir=true; if (this.y > 400) this.y = 0; } } var enemy; var canvas; var ctx; var x = 200; var y = 370; var MagSize = 100; var ShootingFrequency = 10; var MyGun = new Gun( MagSize,ShootingFrequency ); var direction="right"; var changedir; var dx = 5; var dy = 5; var WIDTH = 400; var HEIGHT = 400; var img; var RIGHT_MOVE = false; var LEFT_MOVE = false; var TRIGGER_DOWN = false; function hero(x,y) { ctx.beginPath(); ctx.moveTo(x+0,380); ctx.lineTo(x+10,380); ctx.lineTo(x+5,370); ctx.lineTo(x+0,380); ctx.closePath(); ctx.stroke(); } function heroShoot( ) { MyGun.shoot( x,y ) } function shootAnimiation() { if ( TRIGGER_DOWN ) heroShoot(); } function enemyAnimation() { changedir=false; for (var i=0;i<enemy.length;i++) { if (enemy[i].health>0) { enemy[i].moveStep(); enemy[i].draw(); } } if (changedir && direction=="right") direction="left" else if (changedir && direction=="left") direction="right"; } function rect(x,y,w,h) { ctx.beginPath(); ctx.rect(x,y,w,h); ctx.closePath(); ctx.fill(); ctx.stroke(); } function clear() { ctx.clearRect(0, 0, WIDTH, HEIGHT); } function init() { canvas = document.getElementById("canvas"); ctx = canvas.getContext("2d"); enemy=new Array(); for (var i=0;i<10;i++) { enemy[i]=new enemy_object(); enemy[i].x=i*20; enemy[i].y=10; enemy[i].health=100; } return setInterval(draw, 33); } function Move() { if ( RIGHT_MOVE ) if (x + dx < WIDTH) x += dx; if ( LEFT_MOVE ) if (x - dx > 0) x -= dx; } function doKeyDown(evt){ switch (evt.keyCode) { case 37: /* Left arrow was pressed */ LEFT_MOVE = true; break; case 39: /* Right arrow was pressed */ RIGHT_MOVE = true; break; case 38: /* Up arrow was pressed */ TRIGGER_DOWN = true; break; } } function doKeyUp(evt){ switch (evt.keyCode) { case 37: /* Left arrow was pressed */ LEFT_MOVE = false; break; case 39: /* Right arrow was pressed */ RIGHT_MOVE = false; break; case 38: /* Up arrow was pressed */ TRIGGER_DOWN = false; break; } } function draw() { clear(); ctx.fillStyle = "white"; ctx.strokeStyle = "black"; rect(0,0,WIDTH,HEIGHT); ctx.fillStyle = "purple"; Move(); shootAnimiation(); MyGun.GunLoop(); hero(x, y); enemyAnimation(); } init(); window.addEventListener('keydown',doKeyDown,true); window.addEventListener('keyup',doKeyUp,true); </script> </body> </html>
Menee vähän monimutkaisemmaksi kuin ajattelin. Miten sitten tuon osuman tarkistuksen kanssa, kannattaisiko se olla oma funktionsa? Miten kerron funktiolle ammuksen ja vihollisen sijainnin?
Vaikkapa jotain tuohon tyyliin:
... function CheckCollision( bullet ) { for (var i=0;i<enemy.length;i++) { if ( bullet.bulletx >= enemy[i].x && ( bullet.bulletx< enemy[i].x + 10 ) && bullet.bullety >= enemy[i].y && ( bullet.bullety< enemy[i].y + 10 ) ) enemy[i].health = 0; } } ... if ( this.shot ) { this.bullety -= 9; CheckCollision( this ); } ...
Yritän saada ammuksen katoamaan, kun se osuu vihuun mutta ei oikein onnistu. Joko osumasta kuolee monta vihua tai ammuksia ei tule ollenkaan. Koitin mm. sitä että jos tulee osuma niin bullety arvoksi tulee -1 mutta se vaikuttaa kaikkiin ammuksiin.
Itse muuttaisin vihollisten ja ammuksien käsittelyä hiukan ja käsittelisin niitä listamaisesti vakiomittaisen taulukon sijasta. Mielestäni siten on yksinkertaisempi lisätä ja poistaa objekteja lennossa. Esimerkiksi vihollisen saadessa osuman poistetaan se vihollislistasta ja lisätään räjähdys räjähdyslistaan.
Muutenkin pelisi rakennetta voisi tuosta jonkun verran yksinkertaistaa. Oma vanha Lua:lla kirjoiteltu Crappy Invaders löytyy täältä. Tuo on muuten samalla ensimmäinen Lua:lla kirjoittamani ohjelma...
Taidan tehdä pelin yhden ammuksen taktiikalla niin pysyy koodi aisoissa. Edelleen on se ongelma että en saa ammusta poistettua. Haluaisin että viholliset ammuskelee satunnaisesti. Onnistuuko se ilman että tekee vihollisen ammuksista objekteja?
Ja vielä viimeinen kysymys ja sitten päätän tämän kokeilun. Haluaisin että peli päättyy tai lisää vihollisia kun ensimmäiset on ammuttu ja varmaan se parhaiten onnistuisi silloin kun kaikkien health on 0, mutta miten saan tarkistettua että kaikkien enemy.health on 0?
Siinä on lisättynä tuo vihollisten ampuminen yksinkertaisella tavalla. Jokainen vihu voi ampua yhden panoksen. Laitoin perus collision checkin, mut se vaatii hiomista. http://q-q.name/spaceinv/
Eli enemy objektiin lisätty uusi shoot funktio jossa arvotaan koska vihu ampuu uudestaan, jos sen nykyinen panos on ylittänyt canvaksen korkeuden. Herolle copy pastettu tuo aiempi collision homma ja käytetty sitä. Inittiin lisätty tuo enemy ampumishomma. Sit enemy drawiin lisätty panosten piirto.
<html> <head> <title>Space Invaders</title> <style type="text/css"> #canvas { background-color: #fff; } </style> </head> <body bgcolor="gray"> <canvas id="canvas" width="400" height="400"></canvas> <script type="text/javascript"> function bullet_object() { this.bulletx=0 this.bullety=-1; this.shot = false; this.shoot = shoot; function shoot( x,y ) { if ( !this.shot ) { this.bulletx=x; this.bullety=y; this.shot = true; } } this.draw = function() { if ( this.shot ) { ctx.save(); ctx.fillStyle = "rgb(0,0,0)" ctx.beginPath(); ctx.rect(this.bulletx+3,this.bullety,3,3); ctx.closePath(); ctx.fill(); ctx.restore(); } } this.bulletStep = function() { if ( this.shot ) { this.bullety -= 9; CheckCollision( this ); } if ( this.bullety<0 ) this.shot = false; } } function Gun( magSize,frequency ) { this.magSize = magSize; this.bullets = new Array(this.magSize); for ( var i=0; i<this.magSize; i++ ) this.bullets[ i ] = new bullet_object(); this.shoot = shoot; this.GunLoop = GunLoop; this.ShootingFreq = frequency; this.Gap = 0; function shoot( x,y ) { if ( this.Gap == 0 ) { for ( var i=0; i<this.magSize; i++ ) if ( !this.bullets[ i ].shot ) { this.bullets[ i ].shoot( x,y ); break; } this.Gap = this.ShootingFreq; } } function GunLoop() { if ( this.Gap>0 ) this.Gap--; for ( var i=0; i<this.magSize; i++ ) { this.bullets[ i ].bulletStep(); this.bullets[ i ].draw(); } } } function enemy_object() { this.x=0; this.y=0; this.bulletx=0; this.bullety=0; this.nextshot=0; this.health=0; this.draw = function() { ctx.save(); ctx.strokeStyle = "red"; ctx.beginPath(); ctx.moveTo(this.x+0,this.y+0); ctx.lineTo(this.x+10,this.y+0); ctx.lineTo(this.x+5,this.y+10); ctx.lineTo(this.x+0,this.y+0); ctx.closePath(); ctx.stroke(); ctx.restore(); if (this.bullety<HEIGHT) { ctx.save(); ctx.fillStyle = "rgb(184,0,0)" ctx.beginPath(); ctx.rect(this.bulletx+3,this.bullety,3,3); ctx.closePath(); ctx.fill(); ctx.restore(); } } this.moveStep = function() { if (direction=="right") this.x += 5; else if (direction=="left") this.x -= 5; this.y+=1; if (this.x > 400) changedir=true; else if (this.x < 0) changedir=true; if (this.y > 400) this.y = 0; } this.shoot = function() { if (this.nextshot<1 && this.bullety>=HEIGHT) { this.bullety=this.y+9; this.bulletx=this.x; this.nextshot=Math.random()*100; } else if (this.bullety<HEIGHT) this.bullety+=9; checkHeroCollision(this); this.nextshot--; } } var enemy; var canvas; var ctx; var x = 200; var y = 370; var MagSize = 100; var ShootingFrequency = 10; var MyGun = new Gun( MagSize,ShootingFrequency ); var direction="right"; var changedir; var dx = 5; var dy = 5; var WIDTH = 400; var HEIGHT = 400; var img; var RIGHT_MOVE = false; var LEFT_MOVE = false; var TRIGGER_DOWN = false; function hero(x,y) { ctx.beginPath(); ctx.moveTo(x+0,380); ctx.lineTo(x+10,380); ctx.lineTo(x+5,370); ctx.lineTo(x+0,380); ctx.closePath(); ctx.stroke(); } function heroShoot( ) { MyGun.shoot( x,y ) } function shootAnimiation() { if ( TRIGGER_DOWN ) heroShoot(); } function enemyAnimation() { changedir=false; for (var i=0;i<enemy.length;i++) { if (enemy[i].health>0) { enemy[i].moveStep(); enemy[i].shoot(); enemy[i].draw(); } } if (changedir && direction=="right") direction="left" else if (changedir && direction=="left") direction="right"; } function rect(x,y,w,h) { ctx.beginPath(); ctx.rect(x,y,w,h); ctx.closePath(); ctx.fill(); ctx.stroke(); } function clear() { ctx.clearRect(0, 0, WIDTH, HEIGHT); } function init() { canvas = document.getElementById("canvas"); ctx = canvas.getContext("2d"); enemy=new Array(); for (var i=0;i<10;i++) { enemy[i]=new enemy_object(); enemy[i].x=i*20; enemy[i].y=10; enemy[i].health=100; enemy[i].bulletx=0; enemy[i].bullety=HEIGHT; enemy[i].nextshot=Math.random()*100; } return setInterval(draw, 33); } function Move() { if ( RIGHT_MOVE ) if (x + dx < WIDTH) x += dx; if ( LEFT_MOVE ) if (x - dx > 0) x -= dx; } function doKeyDown(evt){ switch (evt.keyCode) { case 37: /* Left arrow was pressed */ LEFT_MOVE = true; break; case 39: /* Right arrow was pressed */ RIGHT_MOVE = true; break; case 38: /* Up arrow was pressed */ TRIGGER_DOWN = true; break; } } function doKeyUp(evt){ switch (evt.keyCode) { case 37: /* Left arrow was pressed */ LEFT_MOVE = false; break; case 39: /* Right arrow was pressed */ RIGHT_MOVE = false; break; case 38: /* Up arrow was pressed */ TRIGGER_DOWN = false; break; } } function draw() { clear(); ctx.fillStyle = "white"; ctx.strokeStyle = "black"; rect(0,0,WIDTH,HEIGHT); ctx.fillStyle = "purple"; Move(); shootAnimiation(); MyGun.GunLoop(); hero(x, y); enemyAnimation(); } function checkHeroCollision(enemy) { var bx=enemy.bulletx; var by=enemy.bullety; if (bx >= x && (bx < x + 10) && by >= y && (by < y + 10)) alert("YOU DIE"); } function CheckCollision( bullet ) { for (var i=0;i<enemy.length;i++) { if ( bullet.bulletx >= enemy[i].x && ( bullet.bulletx< enemy[i].x + 10 ) && bullet.bullety >= enemy[i].y && ( bullet.bullety< enemy[i].y + 10 ) ) enemy[i].health = 0; } } init(); window.addEventListener('keydown',doKeyDown,true); window.addEventListener('keyup',doKeyUp,true); </script> </body> </html>
No pelin päättymisen voit tarkistaa vaikka
var gameOn=false; for (var i=0;i<enemy.length;i++) if (enemy[i].health>0) gameOn=true if (!gameOn) enemyDefeated();
Tuolla saisi varmaankin panokset katoamaan:
function CheckCollision( bullet ) { for (var i=0;i<enemy.length;i++) { if ( enemy[i].health > 0 ) if ( bullet.bulletx >= enemy[i].x && ( bullet.bulletx< enemy[i].x + 10 ) && bullet.bullety >= enemy[i].y && ( bullet.bullety< enemy[i].y + 10 ) ) { bullet.bullety=-1; enemy[i].health = 0; } } }
Juu kekkasin tuon itsekkin, mutta mites noi vihollisten ammukset saisi katoamaan? Koitin niille samaa kikkaa, mutta sen jälkeen vihollisilta loppuu panokset ja koitin myös splice-toiminnolla poistaa ammuksen, mutta se poistaa mitä sattuu. Vielä kun saisi jonkun pistejärjestelmän tähän. Koitin tohon CheckCollisionin yhteyteen rakentaa jotain, mutta en oikein ymmärrä mikä logiikka siinä on. Lisäsin 'enemy[ i ].health = 0;' alle muuttujan joka nousee nollasta ylöspäin, kun tulee osuma, mutta luku kasvaa jonnekkin 40 60 välille. Ja sitten vielä vihovihoviimeinen kysymys. Eli miten saan setIntervalin pois? Koitin clearIntervalia mutta ilmeisesti en osaa käyttää sitä oikein.
<html> <head> <title>Space Invaders</title> <style type="text/css"> #canvas { background-color: #fff; } </style> </head> <center> <br><br><br> <body bgcolor="gray"> <canvas id="canvas" width="600" height="400"></canvas> </center> <script type="text/javascript"> function bullet_object() { this.bulletx=0 this.bullety=-1; this.shot = false; this.shoot = shoot; function shoot( x,y ) { if ( !this.shot ) { this.bulletx=x; this.bullety=y; this.shot = true; } } this.draw = function() { if ( this.shot ) { ctx.save(); ctx.fillStyle = "rgb(0,0,0)" ctx.beginPath(); ctx.rect(this.bulletx+3,this.bullety,3,3); ctx.closePath(); ctx.fill(); ctx.restore(); } } this.bulletStep = function() { if ( this.shot ) { this.bullety -= 9; CheckCollision( this ); WallCollision ( this ); } if ( this.bullety<0 ) this.shot = false; } } function Gun( magSize,frequency ) { this.magSize = magSize; this.bullets = new Array(this.magSize); for ( var i=0; i<this.magSize; i++ ) this.bullets[ i ] = new bullet_object(); this.shoot = shoot; this.GunLoop = GunLoop; this.ShootingFreq = frequency; this.Gap = 0; function shoot( x,y ) { if ( this.Gap == 0 ) { for ( var i=0; i<this.magSize; i++ ) if ( !this.bullets[ i ].shot ) { this.bullets[ i ].shoot( x,y ); break; } this.Gap = this.ShootingFreq; } } function GunLoop() { if ( this.Gap>0 ) this.Gap--; for ( var i=0; i<this.magSize; i++ ) { this.bullets[ i ].bulletStep(); this.bullets[ i ].draw(); } } } function enemy_object() { this.x=0; this.y=0; this.bulletx=0; this.bullety=0; this.nextshot=0; this.health=0; this.hit=false; this.draw = function() { ctx.save(); ctx.fillStyle = "rgb(200, 0, 0)"; ctx.beginPath(); ctx.moveTo(this.x+0,this.y+0); ctx.lineTo(this.x+8,this.y+5); ctx.lineTo(this.x+16,this.y+0); ctx.lineTo(this.x+8,this.y+15); ctx.lineTo(this.x+0,this.y+0); ctx.fill(); ctx.restore(); if (this.bullety<HEIGHT) { ctx.save(); ctx.fillStyle = "rgb(184,0,0)" ctx.beginPath(); ctx.rect(this.bulletx+3,this.bullety,3,3); ctx.closePath(); ctx.fill(); ctx.restore(); } } this.moveStep = function() { if (direction=="right") this.x += 5; else if (direction=="left") this.x -= 5; this.y+=1; if (this.x > 600) changedir=true; else if (this.x < 0) changedir=true; if (this.y > 290) this.y = 0; } this.shoot = function() { if (this.nextshot<1 && this.bullety>=HEIGHT) { this.bullety=this.y+9; this.bulletx=this.x; this.nextshot=Math.random()*100; } else if (this.bullety<HEIGHT) this.bullety+=9; checkHeroCollision(this); enemyWallCollision(this); this.nextshot--; } } var enemy; var canvas; var ctx; var x = 300; var y = 370; var MagSize = 100; var ShootingFrequency = 10; var MyGun = new Gun( MagSize,ShootingFrequency ); var direction="right"; var changedir; var dx = 5; var dy = 5; var WIDTH = 600; var HEIGHT = 400; var img; var RIGHT_MOVE = false; var LEFT_MOVE = false; var TRIGGER_DOWN = false; var wallx = 50; var wally = 300; var hit = 0; function hero(x,y) { ctx.beginPath(); ctx.fillStyle = "rgb(0, 0, 128)"; ctx.moveTo(x+0,380); ctx.lineTo(x+10,380); ctx.lineTo(x+5,360); ctx.lineTo(x+0,380); ctx.closePath(); ctx.fill(); } function heroShoot( ) { MyGun.shoot( x,y ) } function shootAnimiation() { if ( TRIGGER_DOWN ) heroShoot(); } function enemyAnimation() { changedir=false; for (var i=0;i<enemy.length;i++) { if (enemy[i].health>0) { enemy[i].moveStep(); enemy[i].shoot(); enemy[i].draw(); } } if (changedir && direction=="right") direction="left" else if (changedir && direction=="left") direction="right"; } function walls() { canvas = document.getElementById("canvas"); ctx = canvas.getContext("2d"); ctx.fillStyle="#5B5B5B"; ctx.fillRect(wallx,wally,100,20); ctx.fillRect(wallx+200,wally,100,20); ctx.fillRect(wallx+400,wally,100,20); } function score(){ ctx.font="bold 20px Verdana"; out = "KILLS:"+hit+""; ctx.fillStyle="rgb(0,0,0)"; ctx.fillText(out,250,340); } function rect(x,y,w,h) { ctx.beginPath(); ctx.rect(x,y,w,h); ctx.closePath(); ctx.fill(); ctx.stroke(); } function clear() { ctx.clearRect(0, 0, WIDTH, HEIGHT); } function init() { canvas = document.getElementById("canvas"); ctx = canvas.getContext("2d"); enemy=new Array(); for (var i=0;i<10;i++) { enemy[i]=new enemy_object(); enemy[i].x=i*30; enemy[i].y=10; enemy[i].health=100; enemy[i].bulletx=0; enemy[i].bullety=HEIGHT; enemy[i].nextshot=Math.random()*100; } intervalD = setInterval(draw, 33); return intervalD; } function Move() { if ( RIGHT_MOVE ) if (x + dx < WIDTH) x += dx; if ( LEFT_MOVE ) if (x - dx > 0) x -= dx; } function doKeyDown(evt){ switch (evt.keyCode) { case 37: /* Left arrow was pressed */ LEFT_MOVE = true; break; case 39: /* Right arrow was pressed */ RIGHT_MOVE = true; break; case 38: /* Up arrow was pressed */ TRIGGER_DOWN = true; break; } } function doKeyUp(evt){ switch (evt.keyCode) { case 37: /* Left arrow was pressed */ LEFT_MOVE = false; break; case 39: /* Right arrow was pressed */ RIGHT_MOVE = false; break; case 38: /* Up arrow was pressed */ TRIGGER_DOWN = false; break; } } function draw() { clear(); ctx.fillStyle = "white"; ctx.strokeStyle = "black"; rect(0,0,WIDTH,HEIGHT); ctx.fillStyle = "purple"; Move(); shootAnimiation(); MyGun.GunLoop(); hero(x, y); enemyAnimation(); Win(); walls(); score(); } function checkHeroCollision(enemy) { var bx=enemy.bulletx; var by=enemy.bullety; if (bx >= x && (bx < x + 10) && by >= y && (by < y + 10)) Loose(); } function CheckCollision( bullet ) { for (var i=0;i<enemy.length;i++) { if ( bullet.bulletx >= enemy[i].x && ( bullet.bulletx< enemy[i].x + 10 ) && bullet.bullety >= enemy[i].y && ( bullet.bullety< enemy[i].y + 10 ) && enemy[i].health > 0 ) { enemy[i].health = 0; hit++; bullet.bullety = -1; } } } function WallCollision( bullet ) { var bx=bullet.bulletx; var by=bullet.bullety; if (( bx >= wallx && ( bx < wallx + 100 ) || ( bx >= wallx + 200 ) && ( bx < wallx + 300 ) || ( bx >= wallx + 400 ) && ( bx < wallx + 500 )) && by >= wally && ( by < wally + 8 ) ) bullet.bullety = -1; } function enemyWallCollision( enemy ) { var bx=enemy.bulletx; var by=enemy.bullety; if (( bx >= wallx && ( bx < wallx + 100 ) || ( bx >= wallx + 200 ) && ( bx < wallx + 300 ) || ( bx >= wallx + 400 ) && ( bx < wallx + 500 )) && by >= wally && ( by < wally + 8 ) ) enemy.hit = true; } function Win() { var gameOn=false; for (var i=0;i<enemy.length;i++) if (enemy[i].health>0) gameOn=true if (!gameOn) { clearInterval(intervalD); clearInterval(intervalD); canvas = document.getElementById("canvas"); ctx = canvas.getContext("2d"); ctx.fillStyle="rgb(0,0,0)"; ctx.font="bold 70px Verdana"; out = "YOU WON!"; ctx.fillText(out,100,200); out = "YOU KILLED "+hit+" ENEMIES"; ctx.font="30px Verdana"; ctx.fillText(out,130,250); } } function Loose() { clearInterval(intervalD); canvas = document.getElementById("canvas"); ctx = canvas.getContext("2d"); ctx.fillStyle="rgb(0,0,0)"; ctx.font="bold 70px Verdana"; out = "YOU LOST!"; ctx.fillText(out,100,200); out = "YOU KILLED "+hit+" ENEMIES"; ctx.font="30px Verdana"; ctx.fillText(out,130,250); } init(); window.addEventListener('keydown',doKeyDown,true); window.addEventListener('keyup',doKeyUp,true); </script> </body> </html>
Itse en tiedä ainakaan suoraa tapaa tuohon setinvertalin poistoon, muutakuin document.reload tms. Parempi varmaan olisi käyttää setTimeout funktiota joka toimii kerran aina kun sitä kutsutaan, eli lisätä draw metodiin niin kauan setTimeout kun muuttuja gameOn==true tai jokin vastaava. Pisteiden laskuun voisit tehdä globaalin muuttujan (eli mitä nytki käytät x,y jne arvoille) ja kasvattaa sitä ainakun vihollinen katoaa eli sen health menee 0. Uusia tasoja voi esim ottaa käyttöön kutsumalla initin uudestaan, mutta lisäämällä inittiin uusia muuttujia kuten vihollisten määrän nykyisen tason perusteella. Voi olla hankalaa tosiaan ymmärtää nuita kaikkia objektiin liittyviä hommia, mutta testaamalla sitä oppii pikkuhiljaa.
Tuosta pistelaskusta, niin mitä kohtaa tarkoitat? Jos laitan sen funktioon CheckCollision, niin luku näyttää mitä sattuu. Koitin myös enemyAnimationiin laittaa
if (enemy[i].health==0) hit++;
mutta sekin näyttää mitä sattuu.
Tuohon törmäystarkistus funktioon se pitäisi laittaa, tottakai. Kai kyseisen if-lohkon sisältö on hakasulkeiden sisällä ja et kai vaan sattumoisin tarkista aina törmäystä taulukon kaikkiin vihollisiin (eli siis myöskin jo kuolleisiin -> health = 0)?
Tässäkin auttaisi listamainen peliobjektien käsittely, kun jäisi turhia vertailuja pois...
Joo eli lisäsin CheckCollisionin if-lauseeseen && health > 0, niin pistelasku toimii. Vielä kun saisi poistettua vihollisen ammukset. Yritin piirtää ne kankaan ulkopuolelle, mutta sen jälkeen ei tule enää uusia ammuksia.
... var intervalId = null; ... intervalId=setInterval(draw, 33); ... clearInterval(intervalId); ...
Laitoin edelliseen viestiini viimeisimmän version pelistä. Se on nyt melkein toimiva peli, mutta vihollisen ammuksia en saa pois kun ne osuu seinään tai sankariin.
Muut ongelmat on ilmeisesti ratkenneet. Oletko edes yrittänyt mitään?
Jees nyt on saatu pelin runko valmiiksi. Kiitoksia avusta!
Aihe on jo aika vanha, joten et voi enää vastata siihen.