"use strict"; const bgmList = [ { normal: "music/Scary.mp3", gameover: "music/ScaryGO.mp3" }, { normal: "music/Fear.mp3", gameover: "music/FearGO.mp3" }, { normal: "music/Chill.mp3", gameover: "music/ChillGO.mp3" }, ]; let currentBGM = new Audio(); let gameOverBGM = new Audio(); currentBGM.loop = true; gameOverBGM.loop = true; function pickRandomBGM() { const bgm = bgmList[Math.floor(Math.random() * bgmList.length)]; currentBGM.src = bgm.normal; gameOverBGM.src = bgm.gameover; } var canvasWidth = 1280; var canvasHeight = 650; var c = undefined; var ctx = undefined; var gameStarted = false; var musicMuted = false; let lastFrameTime = 0; const frameInterval = 1000 / 80; var game = { level: 1, speed: 1, gameOver: false, frames: 0, timer: 0, }; var keys = { up: false, down: false, left: false, right: false, fire: false, }; var playerShipImg = new Image(); playerShipImg.src = "img/Player/pesawat22.png"; var bg0 = new Image(); bg0.src = "img/bg_0.png"; var bg1 = new Image(); bg1.src = "img/bg_1.png"; var bg2 = new Image(); bg2.src = "img/bg_2.png"; var enemy1 = new Image(); enemy1.src = "img/alien_0.png"; var enemyImgArray = []; enemyImgArray.length = 4; let audioStarted = false; window.addEventListener("keydown", () => { if (!audioStarted) { currentBGM.play(); audioStarted = true; } }); for (var i = 0; i < enemyImgArray.length; i++) { enemyImgArray[i] = new Image(); enemyImgArray[i].src = "img/alien_" + [i] + ".png"; } var missilesArray = []; var enemyShipArray = []; var laser = document.createElement("audio"); laser.src = "music/laser2.mp3"; var explosion_enemy = document.createElement("audio"); explosion_enemy.src = "music/explosion-small.mp3"; var planetImages = []; for (let i = 1; i <= 4; i++) { let img = new Image(); img.src = `img/SpritesPlanet/planet_${i}.png`; planetImages.push(img); } window.onload = function () { init(); }; function init() { c = document.getElementById("canvas"); ctx = c.getContext("2d"); document.addEventListener("keydown", keyDownPressed, false); document.addEventListener("keyup", keyUpPressed, false); gameStarted = true; pickRandomBGM(); currentBGM.volume = 1; currentBGM.play(); requestAnimationFrame(gameLoop); } function gameLoop(timestamp) { if (!gameStarted) return; if (game.gameOver) { drawGameOver(); return; } if (timestamp - lastFrameTime >= frameInterval) { lastFrameTime = timestamp; clearGame(); updateGame(); drawGame(); } requestAnimationFrame(gameLoop); } function keyDownPressed(e) { if (e.keyCode == 87 || e.keyCode == 38) { keys.up = true; } else if (e.keyCode == 83 || e.keyCode == 40) { keys.down = true; } if (e.keyCode == 65 || e.keyCode == 37) { keys.left = true; } if (e.keyCode == 68 || e.keyCode == 39) { keys.right = true; } if (e.keyCode == 32) { keys.fire = true; missilesArray.push( new LaserBullet(player1.x + player1.width, player1.y + player1.height / 2) ); laser.currentTime = 0; laser.play(); laser.volume = 0.4; } } function keyUpPressed(e) { if (e.keyCode == 87 || e.keyCode == 38) { keys.up = false; } else if (e.keyCode == 83 || e.keyCode == 40) { keys.down = false; } if (e.keyCode == 65 || e.keyCode == 37) { keys.left = false; } if (e.keyCode == 68 || e.keyCode == 39) { keys.right = false; } if (e.keyCode == 32) { keys.fire = false; } } function clearGame() { ctx.clearRect(0, 0, canvasWidth, canvasHeight); } function updateGame() { addStarField(); addShips(); player1.update(); if (player1.invincible > 0) player1.invincible--; spawnPlanet(); if (currentPlanet) currentPlanet.update(); game.frames++; } function drawGame() { if (currentPlanet) currentPlanet.draw(); player1.draw(); for (var i = 0; i < enemyShipArray.length; i++) { var s = enemyShipArray[i]; s.draw(); s.update(); if (s.x < -200) { enemyShipArray.splice(i, 1); i--; continue; } if (Tabrakan(player1, s)) { if (player1.invincible <= 0) { player1.lives--; player1.invincible = 60; explosion_enemy.play(); player1.x = 100; player1.y = canvasHeight / 2 - player1.height / 2; player1.vx = 0; player1.vy = 0; game.frames = 0; enemyShipArray.splice(i, 1); i--; if (player1.lives <= 0) { game.gameOver = true; crossfadeToGameOver(); } continue; } } } for (var i = 0; i < missilesArray.length; i++) { var m = missilesArray[i]; m.draw(); m.update(); for (var j = 0; j < enemyShipArray.length; j++) { var en = enemyShipArray[j]; if (Tabrakan(m, en)) { player1.score += 100; explosion_enemy.play(); missilesArray.splice(i, 1); enemyShipArray.splice(j, 1); } } if (m.x > canvasWidth) { missilesArray.splice(i, 1); i--; } } drawNewText("Score: " + player1.score, 30, 610, "white"); drawNewText("Player Lives: " + player1.lives, 1100, 610, "white"); } class PlayerObject { constructor(x, y) { this.x = x; this.y = y; this.width = 100; this.height = 64; this.image = playerShipImg; this.vx = 0; this.vy = 0; this.acceleration = 0.8; this.friction = 0.92; this.maxSpeed = 10; this.lives = 3; this.score = 0; this.health = 100; this.invincible = 0; this.totalFrames = 5; this.frameIndex = 2; this.spriteWidth = 0; this.sourceHeight = 0; this.scale = 1.3; this.image.onload = () => { this.spriteWidth = this.image.width / this.totalFrames; this.sourceHeight = this.image.height; this.width = this.spriteWidth * this.scale; this.height = this.sourceHeight * this.scale; this.y = canvasHeight / 2 - this.height / 2; }; } draw() { ctx.save(); if (this.invincible > 0 && this.invincible % 4 < 2) { ctx.globalAlpha = 0.5; } if (this.spriteWidth > 0) { ctx.drawImage( this.image, this.frameIndex * this.spriteWidth, 0, this.spriteWidth, this.sourceHeight, this.x, this.y, this.width, this.height ); } else { ctx.fillStyle = "red"; ctx.fillRect(this.x, this.y, 50, 50); } ctx.restore(); } update() { if (keys.up) { this.vy -= this.acceleration; } if (keys.down) { this.vy += this.acceleration; } if (keys.left) { this.vx -= this.acceleration; } if (keys.right) { this.vx += this.acceleration; } this.vx *= this.friction; this.vy *= this.friction; this.x += this.vx; this.y += this.vy; const bleedY = this.height * 0.4; const bleedX = this.width * 0.4; if (this.y < -bleedY) { this.y = -bleedY; if (this.vy < 0) this.vy = 0; } if (this.y > canvasHeight - this.height + bleedY) { this.y = canvasHeight - this.height + bleedY; if (this.vy > 0) this.vy = 0; } if (this.x < -bleedX) { this.x = -bleedX; if (this.vx < 0) this.vx = 0; } if (this.x > canvasWidth - this.width + bleedX) { this.x = canvasWidth - this.width + bleedX; if (this.vx > 0) this.vx = 0; } if (this.vy < -2.5) { this.frameIndex = 4; } else if (this.vy < -0.5) { this.frameIndex = 3; } else if (this.vy > 2.5) { this.frameIndex = 0; } else if (this.vy > 0.5) { this.frameIndex = 1; } else { this.frameIndex = 2; } } } let player1 = new PlayerObject(100, 300); function drawNewText(txt, x, y, color) { ctx.font = "20px Arial"; ctx.fillStyle = color; ctx.fillText(txt, x, y); } class backgroundObj { constructor(img, x, y, speed) { this.x = x; this.y = y; this.width = 2000; this.height = 1200; this.img = img; this.speed = speed; } draw() { ctx.save(); ctx.drawImage(this.img, this.x, this.y, this.width, this.height); ctx.restore(); } update() { this.x -= this.speed; if (this.x < -2000) { this.x = 2000; } } } let background1 = new backgroundObj(bg0, 0, 0, game.speed * 3); let background1a = new backgroundObj(bg0, 2000, 0, game.speed * 3); let background2 = new backgroundObj(bg1, 0, 0, game.speed * 2); let background2a = new backgroundObj(bg1, 2000, 0, game.speed * 2); let background3 = new backgroundObj(bg2, 0, 0, game.speed * 1); let background3a = new backgroundObj(bg2, 2000, 0, game.speed * 1); function addStarField() { background3.draw(); background3.update(); background3a.draw(); background3a.update(); background2.draw(); background2.update(); background2a.draw(); background2a.update(); background1.draw(); background1.update(); background1a.draw(); background1a.update(); } class LaserBullet { constructor(x, y) { this.x = x; this.y = y; this.width = 14; this.height = 4; this.speed = 16; } draw() { let g = ctx.createLinearGradient( this.x, this.y, this.x + this.width, this.y ); g.addColorStop(0, "#00e1ff"); g.addColorStop(0.5, "#ffffff"); g.addColorStop(1, "#00e1ff"); ctx.fillStyle = g; ctx.shadowColor = "#00ffff"; ctx.shadowBlur = 15; ctx.fillRect(this.x, this.y, this.width, this.height); ctx.shadowBlur = 0; } update() { this.x += this.speed; } } class EnemyObj { constructor(x, y, speed, img) { this.x = x; this.y = y; this.width = 170; this.height = 105; this.image = img; this.speed = speed; this.health = 100; this.damage = 10; } draw() { ctx.save(); ctx.drawImage(this.image, this.x, this.y, this.width, this.height); ctx.restore(); } update() { this.x -= this.speed; } } let enemy = new EnemyObj(800, 200, 12, enemyImgArray[0]); class Planet { constructor(img) { this.image = img; this.width = 160; this.height = 160; this.x = canvasWidth + 50; this.y = Math.random() * 300 + 50; this.speed = 1.2; this.active = true; } draw() { ctx.drawImage(this.image, this.x, this.y, this.width, this.height); } update() { this.x -= this.speed; if (this.x < -this.width) { this.active = false; } } } let currentPlanet = null; function spawnPlanet() { if (currentPlanet == null || currentPlanet.active === false) { let randomImg = planetImages[Math.floor(Math.random() * planetImages.length)]; currentPlanet = new Planet(randomImg); } } function addShips() { if (game.frames > 200) { if (game.frames % 150 == 0) { var randomY = Math.floor(Math.random() * 500) + 20; var randomSpeed = Math.floor(Math.random() * 10) + 1; var randomShip = Math.floor(Math.random() * enemyImgArray.length); enemyShipArray.push( new EnemyObj(1300, randomY, randomSpeed, enemyImgArray[randomShip]) ); } } } function Tabrakan(o, p) { if ( o.x + o.width > p.x && o.x < p.x + p.width && o.y + o.height > p.y && o.y < p.y + p.height ) { return true; } return false; } function drawGameOver() { ctx.fillStyle = "rgba(0,0,0,0.7)"; ctx.fillRect(0, 0, canvasWidth, canvasHeight); ctx.font = "80px Arial"; ctx.fillStyle = "red"; ctx.textAlign = "center"; ctx.fillText("GAME OVER", canvasWidth / 2, canvasHeight / 2 - 50); ctx.font = "40px Arial"; ctx.fillStyle = "white"; ctx.fillText("Refresh to Restart", canvasWidth / 2, canvasHeight / 2 + 30); } function crossfadeToGameOver() { let fadeSpeed = 0.02; gameOverBGM.volume = 0; gameOverBGM.play(); let fadeInterval = setInterval(() => { currentBGM.volume -= fadeSpeed; if (currentBGM.volume < 0) currentBGM.volume = 0; gameOverBGM.volume += fadeSpeed; if (gameOverBGM.volume > 1) gameOverBGM.volume = 1; if (currentBGM.volume === 0) { currentBGM.pause(); clearInterval(fadeInterval); } }, 1000 / 30); }