Idle aranybánya
Az "Idle" (tétlen) vagy "klikkalapú" játékok 2013 környékén váltak rendkívül népszerűvé, így egy kicsit lemaradtunk a buliról, de azért még szórakozhatunk egy ilyennek a létrehozásával.
Ezeknek a játékoknak az ötlete az, hogy a játékos jutalmat kapjon egy egyszerű műveletért (például egy kattintásért). Vannak, akik élvezik ezeket a játékokat, mások nem szívesen pazarolják rájuk az értékes idejüket. Én nem ítélkezem.
A mi esetünkben a játékos egy aranybányát fog üzemeltetni, és a kattintásért járó jutalma egy új, voxel-stílusú munkás lesz. Ez a "jutalom" 5 dollárba fog kerülni.
Minden munkás fáradhatatlanul ássa az aranyrögöket és szállítja azokat a ládákhoz, ahol egy automatizált lift felveszi és egy másik, felszíni ládába viszi őket.
Onnan egy másik csapat a raktárba szállítja a rögöket, és a játékos minden egyes darabért 1 dollárt kap (úgy tűnik, az aranypiac jelenleg nem túl forró).
Mindössze 20 dollárért nyithatsz egy új aknát.
A játék, akárcsak az emberi kapzsiság, sosem ér véget, de elég hamar unalmassá válik: megnyitod az összes aknát és megtöltöd őket munkásokkal. Ezen a ponton már csak tétlenül nézheted, ahogy a vagyonod nő, vagy megnézheted az alábbi oktatóanyagok egyikét.
Íme, hogyan működik (teljes kód alább):
[1-7] A HTML5 vászon (canvas) és a betűtípusának beállítása.
[8-27] Képek
[29-30] Akna méretei
[32] 5$ egy új munkás felvételéhez
[33] max 3 munkás aknánként
[34] 20$ egy új akna megnyitása (micsoda alku!)
[35] 10 akna maximum - ennyi fér el a vásznunkon
[36-37] a képek méretei
[38] minden animációs szekvencia 20 képkockából áll
[39] a vászon vízszintes és függőleges nyújtásának aránya, hogy illeszkedjen az eszköz képernyőjéhez
[40] a pontszám az aktuális készpénzed
[41] az aknák tömbjének inicializálása
[42] az aktuális animációs képkocka (0-val kezdődik)
[43-81] a lift objektum:
[44] kezdeti sebesség
[45-59] Amikor a lift megérkezik egy aknához:
[47-52] ha a föld alatt van, mozgasd az aranyat (ha van) a ládából a liftbe, ürítsd ki az akna ládáját, indítsd el a rakodási számlálót
[54-58] különben mozgasd az aranyat a liftből az akna ládájába
[60-67] A lift mozgatása:
[61] az y koordináta megváltoztatása
[63-64] ha a lift megérkezett egy aknához, hajtsd végre a megfelelő függvényt
[65-66] ha elérte a tetejét vagy az alját, fordítsd meg a sebességet
[68-80] A lift rajzolása és frissítése:
[69] A bittérkép rajzolása
[70] A láda tartalmának megjelenítése
[71-75] Ha a rakodási számláló be van kapcsolva, rajzold a nyilat az aktuális irányba
[76] és csökkentsd a számlálót
[77-78] különben mozgasd a liftet.
[83-100] Az akna
[84] munkások nélkül indul
[85] és üres ládával
[86] alapértelmezés szerint zárva (később nyitjuk meg)
[87-93] Az akna rajzolása:
[88-92] Ha van hely több munkásnak, mutasd a "munkás felvétele" gombot (aktív vagy inaktív, attól függően, hogy van-e elég pénzed)
[94-98] Új akna megnyitása:
[95] váltás "nyitva" állapotra
[96] a nyitott aknák számlálójának növelése
[97] az első munkás automatikus felvétele
[98] és kifizetése.
[102-154] Munkások
[103] véletlenszerűen kezdenek valahol a lift közelében
[104] jobbra nézve
[107-153] Munkás logika:
[110-117] Ha ás:
[111] Növeld az ásási számlálót
[112] használd a spritesheet 5. sorát:

[113] Ha már 100 képkocka óta ás, fejezze be az ásást és menjen a lifthez.
[118-130] Ha jobbra megy:
[119] használd a spritesheet 1. (felső) sorát
[120] mozgás jobbra
[121] ha közel van a jobb oldalhoz, kezdjen el ásni, vagy (ha a felszínen van) helyezze el a szállítmányt a raktárban és menjen vissza a lifthez
[131-146] Ha balra megy:
[132] Használd a spritesheet 2. sorát
[133] mozgás balra
[134-144] ha közel van a ládához:
[135] forduljon meg
[136-7] adja az aranyat a ládához, ha a föld alatt van
[138-143] ha van arany a ládában, vegye el
[147-148] Ha a felszínen van, menjen le két sort a spritesheeten (hogy megfordítsa az irányt, amerre néz). A felszínen balra haladva cipeli a rögöt, jobbra haladva pedig üres kézzel megy. A föld alatt ez fordítva van.
[149-150] Ha a felszínen van és üres a keze, használja az 1. sort.
[152] a képkocka kirajzolása a spritesheetről.
[156-169] A képernyő rajzolása:
[157] a háttérkép kirajzolása a nyitott aknákhoz
[158] a pontszám mutatása
[159-162] a nyitott aknák kirajzolása
[165-168] az aktív/inaktív "új akna" gomb kirajzolása
[171-183] Fő animációs ciklus
[173-177] minden munkás frissítése
[179-181] A képkocka-számláló növelése és szükség esetén visszaállítása.
[182] Várakozás a következő animációs képkockára.
[185-203] A változók kezdeti értékei.
[205-214] Ha az ablakot átméretezik (pl. a mobil eszközt elforgatják), változtasd meg a vászon stílusának méreteit, hogy kitöltse az egész képernyőt, és számítsd ki a frissített x/y arányokat.
[216-230] Kattintás kezelése
[217-218] a koordináták újraszámítása az arányok figyelembevételével
[219] Melyik akna gombjára kattintottak?
[221-224] ha egy "munkás felvétele" gombra kattintottak, adj hozzá egy munkást és fizesd ki
[225-227] különben nyiss meg egy új aknát
[232-242] Amikor az ablak először betöltődik:
[233-238] hozd létre az aknákat és állítsd be a munkások maximális számát
[235-236] a felszínen 5-ször több munkás lehet, mint az aknákban.
| <html> |
| <body> |
| <canvas id='canvas' width='600' height='600' style='position:absolute; left:0; top:0'></canvas> |
| <script> |
| const canvas = document.getElementById('canvas'); |
| const context = canvas.getContext('2d'); |
| context.font = 'bold 16px sans-serif'; |
| const backgroundImg = new Image(); |
| backgroundImg.src = 'background.png'; |
| const liftImg = new Image(); |
| liftImg.src = 'lift.png'; |
| const arrowLeftImg = new Image(); |
| arrowLeftImg.src = 'arrow.png'; |
| const arrowRightImg = new Image(); |
| arrowRightImg.src = 'arrow_right.png'; |
| const spritesheetImg = new Image(); |
| spritesheetImg.src = 'spritesheet.png'; |
| const closeImg = new Image(); |
| closeImg.src = 'close.png'; |
| const workerActiveButtonsImg = new Image(); |
| workerActiveButtonsImg.src = 'worker_active.png'; |
| const workerInactiveButtonsImg = new Image(); |
| workerInactiveButtonsImg.src = 'worker_inactive.png'; |
| const shaftActiveButtonsImg = new Image(); |
| shaftActiveButtonsImg.src = 'shaft_active.png'; |
| const shaftInactiveButtonsImg = new Image(); |
| shaftInactiveButtonsImg.src = 'shaft_inactive.png'; |
| |
| const shaftHeight = 60, |
| shaftWidth = 450, |
| liftWidth = 40, |
| workerCost = 5, |
| max_workers = 3, |
| shaftCost = 20, |
| max_shafts = 10, |
| buttonWidth = 150, |
| spriteSize = 60, |
| max_frames = 19; |
| let aspectX, aspectY; |
| let score, shaftsOpen; |
| let shafts = []; |
| let frame = 0; |
| let lift = { |
| speed: .5, |
| arrivedAtShaft: function(shaftNumber) { |
| let shaft = shafts[shaftNumber]; |
| if (shaftNumber > 0) { |
| if (shaft.chest > 0) { |
| this.chest = this.chest + shaft.chest; |
| shaft.chest = 0; |
| this.loadingCounter = 100; |
| } |
| } else { |
| if (this.chest > 0) |
| this.loadingCounter = 100; |
| shaft.chest = shaft.chest + this.chest; |
| this.chest = 0; |
| } |
| }, |
| move: function() { |
| this.y = this.y + this.speed; |
| let currentShaft = this.y / shaftHeight; |
| if (currentShaft == Math.floor(currentShaft)) |
| this.arrivedAtShaft(currentShaft); |
| if (this.y >= (shaftsOpen - 1) * shaftHeight || this.y <= 0) |
| this.speed = -this.speed; |
| }, |
| update: function() { |
| context.drawImage(liftImg, 0, this.y); |
| context.fillText(this.chest, 10, this.y + 25); |
| if (this.loadingCounter) { |
| if (this.y > 0) |
| context.drawImage(arrowLeftImg, liftWidth * .5, this.y); |
| else |
| context.drawImage(arrowRightImg, liftWidth * .5, this.y); |
| this.loadingCounter--; |
| } else { |
| this.move(); |
| } |
| }, |
| }; |
| |
| function Shaft() { |
| this.workers = []; |
| this.chest = 0; |
| this.closed = true; |
| this.draw = function(shaftNumber) { |
| if (shafts[shaftNumber].workers.length < shafts[shaftNumber].max_workers) |
| if (score >= workerCost) |
| context.drawImage(workerActiveButtonsImg, shaftWidth, shaftHeight * shaftNumber); |
| else |
| context.drawImage(workerInactiveButtonsImg, shaftWidth, shaftHeight * shaftNumber); |
| }; |
| this.open = function() { |
| this.closed = false; |
| shaftsOpen++; |
| this.workers.push(new Worker()); |
| score = score - shaftCost; |
| }; |
| } |
| |
| function Worker() { |
| this.x = Math.floor(Math.random() * liftWidth); |
| this.mode = 'goRight'; |
| this.counter = 0; |
| this.load = 0; |
| this.update = function(shaftNumber) { |
| let row; |
| switch (this.mode) { |
| case 'dig': |
| this.counter++; |
| row = 4; |
| if (this.counter > 100) { |
| this.counter = 0; |
| this.mode = 'goLeft'; |
| } |
| break; |
| case 'goRight': |
| row = 0; |
| this.x++; |
| if (this.x >= shaftWidth - spriteSize * 1.5 + Math.random() * spriteSize) { |
| if (shaftNumber > 0) |
| this.mode = 'dig'; |
| else { |
| score = score + this.load; |
| this.load = 0; |
| this.mode = 'goLeft'; |
| } |
| } |
| break; |
| case 'goLeft': |
| row = 1; |
| this.x--; |
| if (this.x <= 2 * liftWidth) { |
| this.mode = 'goRight'; |
| if (shaftNumber > 0) { |
| shafts[shaftNumber].chest = shafts[shaftNumber].chest + 1; |
| } else { |
| if (shafts[0].chest > 0) { |
| this.load = 1; |
| shafts[0].chest = shafts[0].chest - 1; |
| } |
| } |
| } |
| break; |
| } |
| if (shaftNumber == 0) { |
| row = row + 2; |
| if (this.load == 0 && this.mode == 'goRight') |
| row = 0; |
| } |
| context.drawImage(spritesheetImg, frame * spriteSize, row * spriteSize, spriteSize, spriteSize, this.x, shaftNumber * shaftHeight, spriteSize, spriteSize); |
| }; |
| } |
| |
| function drawScreen() { |
| context.drawImage(backgroundImg, 0, 0, shaftWidth + buttonWidth, shaftHeight * shaftsOpen, 0, 0, shaftWidth + buttonWidth, shaftHeight * shaftsOpen); |
| context.fillText(score, 300, 15); |
| shafts.forEach((shaft, shaftNumber) => { |
| if (!shaft.closed) { |
| context.fillText(shaft.chest, liftWidth + 15, shaftNumber * shaftHeight + 25); |
| shaft.draw(shaftNumber); |
| } |
| }); |
| if (score >= shaftCost) |
| context.drawImage(shaftActiveButtonsImg, shaftWidth, shaftHeight * (shaftsOpen)); |
| else |
| context.drawImage(shaftInactiveButtonsImg, shaftWidth, shaftHeight * (shaftsOpen)); |
| } |
| |
| function animate() { |
| drawScreen(); |
| shafts.forEach((shaft, shaftNumber) => { |
| shaft.workers.forEach((worker) => { |
| worker.update(shaftNumber); |
| }); |
| }); |
| lift.update(); |
| frame++; |
| if (frame > max_frames) |
| frame = 0; |
| window.requestAnimationFrame(animate); |
| } |
| |
| function reset() { |
| shaftsOpen = 0; |
| score = 70; |
| lift.y = 0; |
| lift.chest = 0; |
| lift.loadingCounter = 0; |
| shafts.forEach((shaft) => { |
| shaft.closed = true; |
| shaft.workers = []; |
| }); |
| shafts[0].open(); |
| shafts[1].open(); |
| for (let i = shaftsOpen; i < max_shafts; i++) { |
| shafts[i].closed = true; |
| shafts[i].workers = []; |
| context.drawImage(closeImg, 0, i * shaftHeight); |
| } |
| animate(); |
| } |
| |
| function resize() { |
| let picSizeX = window.innerWidth; |
| let picSizeY = window.innerHeight; |
| canvas.style.width = window.innerWidth; |
| canvas.style.height = window.innerHeight; |
| aspectX = picSizeX / 600; |
| aspectY = picSizeY / 600; |
| } |
| |
| window.onresize = resize; |
| |
| window.onpointerdown = function(event) { |
| let x = event.offsetX / aspectX; |
| let y = event.offsetY / aspectY; |
| let shaftNumber = Math.floor(y / shaftHeight); |
| let shaft = shafts[shaftNumber]; |
| if (x > shaftWidth) |
| if (shaftNumber < shaftsOpen && score >= workerCost && shaft.workers.length <= shaft.max_workers) { |
| shaft.workers.push(new Worker()); |
| score = score - workerCost; |
| } else { |
| if (shaftNumber == shaftsOpen && score >= shaftCost) { |
| shaft.open(); |
| } |
| } |
| }; |
| |
| window.onload = function() { |
| for (let i = 0; i < max_shafts; i++) { |
| shafts[i] = new Shaft(); |
| if (i == 0) |
| shafts[i].max_workers = max_workers * 5; |
| else |
| shafts[i].max_workers = max_workers; |
| } |
| reset(); |
| resize(); |
| }; |
| </script> |
| </body> |
| </html> |
További JavaScript oktatóanyagok:
Buborékrendezés vizualizációja
Tűzijáték animáció
Stick Champ játék