Poniżej znajdziesz cały kod gry Skaczący Nanonaua. Jeśli podczas lektury książki napotkasz problemy z działaniem kodu, możesz wykorzystać ten listing, aby ułatwić sobie znalezienie błędów w grze.
<!DOCTYPE html> <html> <head> <title>Biegnący Nanonauta!</title> </head> <body> <script> // STAŁE var CANVAS_WIDTH = 800; var CANVAS_HEIGHT = 600; var NANONAUT_WIDTH = 181; var NANONAUT_HEIGHT = 229; var NANONAUT_Y_ACCELERATION = 1; var NANONAUT_X_SPEED = 5; var NANONAUT_NR_ANIMATION_FRAMES = 7; var NANONAUT_ANIMATION_SPEED = 3; // liczba klatek gry na klatkę animacji. var NANONAUT_JUMP_SPEED = 20; var GROUND_Y = 540; var BACKGROUND_WIDTH = 1000; var ROBOT_WIDTH = 141; var ROBOT_HEIGHT = 139; var ROBOT_NR_ANIMATION_FRAMES = 9; var ROBOT_ANIMATION_SPEED = 5; // liczba klatek gry na klatkę animacji. var ROBOT_X_SPEED = 4; var MIN_DISTANCE_BETWEEN_ROBOTS = 400; var MAX_DISTANCE_BETWEEN_ROBOTS = 1200; var MAX_ACTIVE_ROBOTS = 3; var SCREENSHAKE_RADIUS = 16; var NANONAUT_MAX_HEALTH = 100; var SPACE_KEYCODE = 32; var PLAY_GAME_MODE = 0; var GAME_OVER_GAME_MODE = 1; // KONFIGURACJA WSTĘPNA var canvas = document.createElement('canvas'); var c = canvas.getContext('2d'); canvas.width = CANVAS_WIDTH; canvas.height = CANVAS_HEIGHT; document.body.appendChild(canvas); var nanonautImage = new Image(); nanonautImage.src = 'animatedNanonaut.png'; var backgroundImage = new Image(); backgroundImage.src = 'background.png'; var bush1Image = new Image(); bush1Image.src = 'bush1.png'; var bush2Image = new Image(); bush2Image.src = 'bush2.png'; var robotImage = new Image(); robotImage.src = 'animatedRobot.png'; var nanonautSpriteSheet = { nrFramesPerRow: 5, spriteWidth: NANONAUT_WIDTH, spriteHeight: NANONAUT_HEIGHT, image: nanonautImage }; var robotSpriteSheet = { nrFramesPerRow: 3, spriteWidth: ROBOT_WIDTH, spriteHeight: ROBOT_HEIGHT, image: robotImage }; var nanonautCollisionRectangle = { xOffset: 60, yOffset: 20, width: 50, height: 200 }; var robotCollisionRectangle = { xOffset: 50, yOffset: 20, width: 50, height: 100 }; var nanonautX = CANVAS_WIDTH / 2; var nanonautY = GROUND_Y - NANONAUT_HEIGHT; var nanonautHealth = NANONAUT_MAX_HEALTH; var spaceKeyIsPressed = false; var nanonautYSpeed = 0; var nanonautIsInTheAir = false; var nanonautFrameNr = 0; var cameraX = 0; var cameraY = 0; var screenshake = false; var gameFrameCounter = 0; var gameMode = PLAY_GAME_MODE; var bushData = generateBushes(); var robotData = []; window.addEventListener('keydown', onKeyDown); window.addEventListener('keyup', onKeyUp); window.addEventListener('load', start); function start() { window.requestAnimationFrame(mainLoop); } function generateBushes() { var generatedBushData = []; var bushX = 0; while (bushX < (2 * CANVAS_WIDTH)) { var bushImage; if (Math.random() >= 0.5) { bushImage = bush1Image; } else { bushImage = bush2Image; } generatedBushData.push({ x: bushX, y: 80 + Math.random() * 20, image: bushImage }); bushX += 150 + Math.random() * 200; } return generatedBushData; } // PĘTLA GŁÓWNA function mainLoop() { update(); draw(); window.requestAnimationFrame(mainLoop); } // STEROWANIE function onKeyDown(event) { if (event.keyCode === SPACE_KEYCODE) { spaceKeyIsPressed = true; } } function onKeyUp(event) { if (event.keyCode === SPACE_KEYCODE) { spaceKeyIsPressed = false; } } // AKTUALIZACJA function update() { if (gameMode != PLAY_GAME_MODE) return; gameFrameCounter = gameFrameCounter + 1; // Zaktualizuj nanonautę. nanonautX = nanonautX + NANONAUT_X_SPEED; if (spaceKeyIsPressed && !nanonautIsInTheAir) { nanonautYSpeed = -NANONAUT_JUMP_SPEED; nanonautIsInTheAir = true; } nanonautY = nanonautY + nanonautYSpeed; nanonautYSpeed = nanonautYSpeed + NANONAUT_Y_ACCELERATION; if (nanonautY > (GROUND_Y - NANONAUT_HEIGHT)) { nanonautY = GROUND_Y - NANONAUT_HEIGHT; nanonautYSpeed = 0; nanonautIsInTheAir = false; } if ((gameFrameCounter % NANONAUT_ANIMATION_SPEED) === 0) { nanonautFrameNr = nanonautFrameNr + 1; if (nanonautFrameNr >= NANONAUT_NR_ANIMATION_FRAMES) { nanonautFrameNr = 0; } } // Zaktualizuj kamerę. cameraX = nanonautX - 150; // Zaktualizuj krzaczki. for (var i=0; i<bushData.length; i++) { if ((bushData[i].x - cameraX) < -CANVAS_WIDTH) { bushData[i].x += (2 * CANVAS_WIDTH) + 150; } } // Zaktualizuj roboty. screenshake = false; var nanonautTouchedARobot = updateRobots(); if (nanonautTouchedARobot) { screenshake = true; if (nanonautHealth > 0) nanonautHealth -= 1; } // Sprawdź, czy gra się zakończyła. if (nanonautHealth <= 0) { gameMode = GAME_OVER_GAME_MODE; screenshake = false; } } function updateRobots() { // Przemieść i animuj roboty oraz sprawdź kolizję z nanonautą. var nanonautTouchedARobot = false; for (var i=0; i<robotData.length; i++) { if (doesNanonautOverlapRobot( nanonautX + nanonautCollisionRectangle.xOffset, nanonautY + nanonautCollisionRectangle.yOffset, nanonautCollisionRectangle.width, nanonautCollisionRectangle.height, robotData[i].x + robotCollisionRectangle.xOffset, robotData[i].y + robotCollisionRectangle.yOffset, robotCollisionRectangle.width, robotCollisionRectangle.height )) { nanonautTouchedARobot = true; } robotData[i].x -= ROBOT_X_SPEED; if ((gameFrameCounter % ROBOT_ANIMATION_SPEED) === 0) { robotData[i].frameNr = robotData[i].frameNr + 1; if (robotData[i].frameNr >= ROBOT_NR_ANIMATION_FRAMES) { robotData[i].frameNr = 0; } } } // Usuń roboty, które wyjechały za ekran. var robotIndex = 0; while (robotIndex < robotData.length) { if (robotData[robotIndex].x < cameraX - ROBOT_WIDTH) { robotData.splice(robotIndex, 1); } else { robotIndex += 1; } } // Dodaj nowego robota, jeśli jest ich za mało. if (robotData.length < MAX_ACTIVE_ROBOTS) { var lastRobotX = CANVAS_WIDTH; if (robotData.length > 0) { lastRobotX = robotData[robotData.length - 1].x; } var newRobotX = lastRobotX + MIN_DISTANCE_BETWEEN_ROBOTS + Math.random() * (MAX_DISTANCE_BETWEEN_ROBOTS - MIN_DISTANCE_BETWEEN_ROBOTS); robotData.push({ x: newRobotX, y: GROUND_Y - ROBOT_HEIGHT, frameNr: 0 }); } return nanonautTouchedARobot; } function doesNanonautOverlapRobotAlongOneAxis(nanonautNearX, nanonautFarX, robotNearX, robotFarX) { var nanonautOverlapsNearRobotEdge = (nanonautFarX >= robotNearX) && (nanonautFarX <= robotFarX); var nanonautOverlapsFarRobotEdge = (nanonautNearX >= robotNearX) && (nanonautNearX <= robotFarX); var nanonautOverlapsEntireRobot = (nanonautNearX <= robotNearX) && (nanonautFarX >= robotFarX); return nanonautOverlapsNearRobotEdge || nanonautOverlapsFarRobotEdge || nanonautOverlapsEntireRobot; } function doesNanonautOverlapRobot(nanonautX, nanonautY, nanonautWidth, nanonautHeight, robotX, robotY, robotWidth, robotHeight) { var nanonautOverlapsRobotOnXAxis = doesNanonautOverlapRobotAlongOneAxis( nanonautX, nanonautX + nanonautWidth, robotX, robotX + robotWidth ); var nanonautOverlapsRobotOnYAxis = doesNanonautOverlapRobotAlongOneAxis( nanonautY, nanonautY + nanonautHeight, robotY, robotY + robotHeight ); return nanonautOverlapsRobotOnXAxis && nanonautOverlapsRobotOnYAxis; } // RYSOWANIE function draw() { // Potrząśnij ekranem, jeśli to konieczne. var shakenCameraX = cameraX; var shakenCameraY = cameraY; if (screenshake) { shakenCameraX += (Math.random() - .5) * SCREENSHAKE_RADIUS; shakenCameraY += (Math.random() - .5) * SCREENSHAKE_RADIUS; } // Narysuj niebo. c.fillStyle = 'LightSkyBlue'; c.fillRect(0, 0, CANVAS_WIDTH, GROUND_Y - 40); // Narysuj tło. var backgroundX = - (shakenCameraX % BACKGROUND_WIDTH); c.drawImage(backgroundImage, backgroundX, -210); c.drawImage(backgroundImage, backgroundX + BACKGROUND_WIDTH, -210); // Narysuj ziemię. c.fillStyle = 'ForestGreen'; c.fillRect(0, GROUND_Y - 40, CANVAS_WIDTH, CANVAS_HEIGHT - GROUND_Y + 40); // Narysuj krzaczki. for (var i=0; i<bushData.length; i++) { c.drawImage( bushData[i].image, bushData[i].x - shakenCameraX, GROUND_Y - bushData[i].y - shakenCameraY ); } // Narysuj roboty. for (var i=0; i<robotData.length; i++) { drawAnimatedSprite( robotData[i].x - shakenCameraX, robotData[i].y - shakenCameraY, robotData[i].frameNr, robotSpriteSheet ); } // Narysuj nanonautę. drawAnimatedSprite( nanonautX - shakenCameraX, nanonautY - shakenCameraY, nanonautFrameNr, nanonautSpriteSheet ); // Wyświetl dystans pokonany przez nanonautę. var nanonautDistance = nanonautX / 100; c.fillStyle = 'black'; c.font = '48px sans-serif'; c.fillText(nanonautDistance.toFixed(0) + 'm', 20, 40); // Narysuj pasek zdrowia. c.fillStyle = 'red'; c.fillRect(400, 10, nanonautHealth / NANONAUT_MAX_HEALTH * 380, 20); c.strokeStyle = 'red'; c.strokeRect(400, 10, 380, 20); // Jeśli gra jest skończona, wyświetl napis "KONIEC GRY". if (gameMode == GAME_OVER_GAME_MODE) { c.fillStyle = 'black'; c.font = '96px sans-serif'; c.fillText('KONIEC GRY', 120, 300); } } // Narysuj animowanego duszka. function drawAnimatedSprite(screenX, screenY, frameNr, spriteSheet) { var spriteSheetRow = Math.floor(frameNr / spriteSheet.nrFramesPerRow); var spriteSheetColumn = frameNr % spriteSheet.nrFramesPerRow; var spriteSheetX = spriteSheetColumn * spriteSheet.spriteWidth; var spriteSheetY = spriteSheetRow * spriteSheet.spriteHeight; c.drawImage( spriteSheet.image, // Arkusz animacji. spriteSheetX, spriteSheetY, // Współrzędne w arkuszu. spriteSheet.spriteWidth, spriteSheet.spriteHeight, // Wielkość klatki w arkuszu. screenX, screenY, // Położenie na ekranie. spriteSheet.spriteWidth, spriteSheet.spriteHeight // Wielkość na ekranie. ); } </script> </body> </html>