diff --git a/docs/public/assets/background/menu_start.png b/docs/public/assets/background/menu_start.png new file mode 100644 index 0000000..ad1d003 Binary files /dev/null and b/docs/public/assets/background/menu_start.png differ diff --git a/docs/public/assets/background/room_tutorial.png b/docs/public/assets/background/room_tutorial.png new file mode 100644 index 0000000..6790d41 Binary files /dev/null and b/docs/public/assets/background/room_tutorial.png differ diff --git a/docs/public/assets/door/door_close.jpg b/docs/public/assets/door/door_close.jpg deleted file mode 100644 index f4e0325..0000000 Binary files a/docs/public/assets/door/door_close.jpg and /dev/null differ diff --git a/docs/public/assets/door/door_close.png b/docs/public/assets/door/door_close.png new file mode 100644 index 0000000..60f0a02 Binary files /dev/null and b/docs/public/assets/door/door_close.png differ diff --git a/docs/public/assets/door/door_open.jpg b/docs/public/assets/door/door_open.jpg deleted file mode 100644 index ae747cb..0000000 Binary files a/docs/public/assets/door/door_open.jpg and /dev/null differ diff --git a/docs/public/assets/door/door_open.png b/docs/public/assets/door/door_open.png new file mode 100644 index 0000000..a315469 Binary files /dev/null and b/docs/public/assets/door/door_open.png differ diff --git a/docs/public/assets/fonts/PressStart2P.ttf b/docs/public/assets/fonts/PressStart2P.ttf new file mode 100644 index 0000000..2442aff Binary files /dev/null and b/docs/public/assets/fonts/PressStart2P.ttf differ diff --git a/docs/public/index.html b/docs/public/index.html index 74613f6..6ae27a0 100644 --- a/docs/public/index.html +++ b/docs/public/index.html @@ -21,6 +21,8 @@ + + diff --git a/docs/src/controllers/CollisionDetector.js b/docs/src/controllers/CollisionDetector.js index 47d6fbb..64fcae1 100644 --- a/docs/src/controllers/CollisionDetector.js +++ b/docs/src/controllers/CollisionDetector.js @@ -1,28 +1,26 @@ class CollisionDetector { - detectPlayerCollision(playerObj, objArray) { + detectPlayerCollision(playerObj, objArr) { // Check the collision between the player and other objects. If there is a collision, // the player bounces back. - return objArray.some(obj => this.detectCollision(playerObj, obj)); + return objArr.some(obj => this.detectCollision(playerObj, obj)); } - detectBulletEnemyCollision(bulletArray, enemyArray) { + detectBulletEnemyCollision(bulletArr, enemyArr) { // Check the collision between bullets and enemies. If there is a collision, // the bullet vanishes and causes damage to the enemy. - enemyArray.forEach((enemyObj, enemyIndex) => { - bulletArray.forEach((bulletObj, bulletIndex) => { + enemyArr.forEach((enemyObj, enemyIndex) => { + bulletArr.forEach((bulletObj, bulletIndex) => { if (this.detectCollision(bulletObj, enemyObj)) { - enemyArray[enemyIndex].hp = max(0, enemyObj.hp - bulletObj.damage); + enemyArr[enemyIndex].hp = max(0, enemyObj.hp - bulletObj.damage); hitSound.currentTime=0; //music 让音效从头播放 hitSound.play(); - if(enemyArray[enemyIndex].hp === 0){ + if(enemyArr[enemyIndex].hp === 0){ deathSound.currentTime = 0; deathSound.play(); } - - - bulletArray.splice(bulletIndex, 1); + bulletArr.splice(bulletIndex, 1); } }); }); @@ -40,7 +38,7 @@ class CollisionDetector { hitBoundary(obj) { let x = obj.position.x + obj.velocity.x; let y = obj.position.y + obj.velocity.y; - return x < 0 || x > widthInPixel - obj.size.x || y < 0 || y > heightInPixel - obj.size.y; + return x < leftBoundary || x > rightBoundary - obj.size.x || y < topBoundary || y > bottomBoundary - obj.size.y; } } diff --git a/docs/src/controllers/InputHandler.js b/docs/src/controllers/InputHandler.js index 1a41e59..e6d4f3e 100644 --- a/docs/src/controllers/InputHandler.js +++ b/docs/src/controllers/InputHandler.js @@ -1,16 +1,17 @@ class InputHandler { - constructor(cooldownTime = 2000, bulletDmg = 50) { + constructor(roomObj, cooldownTime = 2000) { + this.currentRoom = roomObj; this.collisionDetector = new CollisionDetector(); this.collisionCoolDownTime = cooldownTime; - this.bulletDamage = bulletDmg; this.lastCollisionTime = millis(); } update() { + this.currentRoom.update(); player.updateVelocity(); player.updatePosition(); - const collideWithEnemies = this.collisionDetector.detectPlayerCollision(player, enemies); - const collideWithObstacles = this.collisionDetector.detectPlayerCollision(player, obstacles); + const collideWithEnemies = this.collisionDetector.detectPlayerCollision(player, this.currentRoom.enemies); + const collideWithObstacles = this.collisionDetector.detectPlayerCollision(player, this.currentRoom.obstacles); const playerHitBoundary = this.collisionDetector.hitBoundary(player); if (collideWithEnemies || collideWithObstacles || playerHitBoundary) { player.revertPosition(); @@ -26,8 +27,9 @@ class InputHandler { // player.shoot(direction); // } this.updateBullets(); - this.collisionDetector.detectBulletEnemyCollision(player.bullets, enemies); - this.removeEnemies(enemies); + this.collisionDetector.detectBulletEnemyCollision(player.bullets, this.currentRoom.enemies); + this.removeEnemies(this.currentRoom.enemies); + this.moveToNextRoom(); } handlePlayerShooting() { @@ -53,7 +55,6 @@ class InputHandler { }); } - decreasePlayerHp() { // The player will not receive any damage in the future 2 seconds. if (millis() - this.lastCollisionTime < this.collisionCoolDownTime) { @@ -65,4 +66,18 @@ class InputHandler { hurtSound.currentTime = 0; hurtSound.play(); } + + moveToNextRoom(tolerance=player.size.x) { + if (!this.currentRoom.checkClearCondition()) return; + + const playerMidX = player.position.x + player.size.x / 2; + const playerMidY = player.position.y + player.size.y / 2; + const doorX = this.currentRoom.door.position.x; + const doorY = this.currentRoom.door.position.y + this.currentRoom.door.size.y / 2; + + if (dist(playerMidX, playerMidY, doorX, doorY) < tolerance) { + console.log("Move to the next room!"); + loadRoom(); + } + } } \ No newline at end of file diff --git a/docs/src/core/controller.js b/docs/src/core/controller.js index 2629ff5..311ad4a 100644 --- a/docs/src/core/controller.js +++ b/docs/src/core/controller.js @@ -1,7 +1,7 @@ function checkSavePoint() { // Save when player crosses the target position - const distanceX = abs(player.position.x - savePoint.position.x); - const distanceY = abs(player.position.y - savePoint.position.y); + const distanceX = abs(player.position.x - room.savePoint.position.x); + const distanceY = abs(player.position.y - room.savePoint.position.y); if (!nearSavedPosition && distanceX < player.size.x && distanceY < player.size.y) { saveGameData(); nearSavedPosition = true; @@ -17,16 +17,16 @@ function checkSavePoint() { function saveGameData() { if (nearSavedPosition) return; - localStorage.setItem('lastSavePoint', JSON.stringify(savePoint)); + localStorage.setItem('lastSavePoint', JSON.stringify(room.savePoint)); localStorage.setItem('playerHp', JSON.stringify(player.hp)); - lastSavedPosition.xPos = savePoint.position.x; - lastSavedPosition.yPos = savePoint.position.y; + lastSavedPosition.xPos = room.savePoint.position.x; + lastSavedPosition.yPos = room.savePoint.position.y; console.log("Game Saved!"); } function loadGameData() { - generateEnemies(); - generateObstacles(); + room.generateEnemies(); + room.generateObstacles(); let savedPosition = localStorage.getItem('lastSavePoint'); let playerHp = localStorage.getItem('playerHp'); if (!savedPosition || !playerHp) { @@ -93,19 +93,22 @@ function exitGame() { } function resetGame() { + menuDisplayed = false; + isGamePaused = false; + gameOver = false; + player = new Player(playerX, playerY); - generateObstacles(); - generateEnemies(); - console.log("Player is reset!") - startTime = millis(); -} - -function checkWinCondition() { - // 当所有敌人被消灭,且玩家位于画布上方(yPos < 10)且血量大于 0 时,认为达成胜利条件 - return (enemies.length === 0 && player.position.y < 10 && player.hp > 0) + room = new Room(); + room.setup(); + inputHandler = new InputHandler(room); + console.log("Game is reset!") } function isGameOver() { return player.hp <= 0; } +function loadRoom() { + +} + diff --git a/docs/src/core/main.js b/docs/src/core/main.js index 76f680b..c2ae9a1 100644 --- a/docs/src/core/main.js +++ b/docs/src/core/main.js @@ -1,64 +1,35 @@ let pauseSound = new Audio("assets/music/Pause.mp3"); let hitSound = new Audio("assets/music/Enemy_Hurt.mp3"); -let deathSound = new Audio("assets/music.Enemy_Death.mp3"); +let deathSound = new Audio("assets/music/Enemy_Death.mp3"); let shootSound = new Audio("assets/music/Player_Shoot.mp3"); let hurtSound = new Audio("assets/music/Player_Hurt.mp3"); let deathSound2 = new Audio("assets/music/Player_Death.mp3"); let walkSound = new Audio("assets/music/Player_Walk.mp3"); walkSound.loop = true; -function generateObstacles() { - obstacles = []; - for (let i = 0; i < obstacleCount; i++) { - let x = random(hPadding, widthInPixel - hPadding); - let y = random(vPadding, heightInPixel - vPadding); - obstacles.push(new Obstacle(x, y)); - } -} - -function generateEnemies() { - enemies = []; - for (let i = 0; i < enemyCount; i++) { - let x = random(hPadding, widthInPixel - hPadding); - let y = random(vPadding, heightInPixel - vPadding); - - let hp = random([smallEnemyHp, largeEnemyHp]); - enemies.push(new Enemy(x, y, hp)); - } -} - -function updateObstacles() { - obstacles.forEach(o => o.display()); -} - -function updateEnemies() { - enemies.forEach(e => { - e.update(); - e.display(); - }); -} - function preload() { + uiFont = loadFont('assets/fonts/PressStart2P.ttf'); heart = loadImage('assets/icons/heart.svg'); damagedHeart = loadImage('assets/icons/damagedHeart.svg'); + startMenuImg = loadImage('assets/background/menu_start.png'); + closedDoorImg = loadImage('assets/door/door_close.png'); + openDoorImg = loadImage('assets/door/door_open.png'); + officeRoomImg = loadImage('assets/background/room_tutorial.png'); } function setup() { cnv = createCanvas(windowWidth, windowHeight); adjustCanvasWithAspectRatio(); player = new Player(playerX, playerY); - savePoint = new SavePoint(savePointX, savePointY); - inputHandler = new InputHandler(); + room = new Room(); + room.setup(); + inputHandler = new InputHandler(room); setupMenu(); setupPauseMenu(); - startTime = millis(); - generateObstacles(); - generateEnemies(); } function draw() { - // push(); adjustCanvasWithAspectRatio(); background(220); if (menuDisplayed) { @@ -70,18 +41,13 @@ function draw() { } else { displayTutorial(); - updateObstacles(); - updateEnemies(); - + // room.update(); inputHandler.update(); - savePoint.display(); player.display(); drawUiHub(); checkSavePoint(); - checkWinCondition(); } - // pop(); } function keyPressed() { diff --git a/docs/src/entities/Door.js b/docs/src/entities/Door.js new file mode 100644 index 0000000..bbcc7df --- /dev/null +++ b/docs/src/entities/Door.js @@ -0,0 +1,16 @@ +class Door { + constructor(x = rightBoundary, y = heightInPixel / 2 - doorSize.h / 2) { + this.position = createVector(x, y); + this.size = createVector(doorSize.w, doorSize.h); + this.currentDoorImg = closedDoorImg; + } + + display() { + image(this.currentDoorImg, this.position.x, this.position.y, this.size.x, this.size.y); + } + + open() { + if (this.currentDoorImg === openDoorImg) return; + this.currentDoorImg = openDoorImg; + } +} diff --git a/docs/src/entities/Obstacle.js b/docs/src/entities/Obstacle.js index a5c1f71..a22e5fc 100644 --- a/docs/src/entities/Obstacle.js +++ b/docs/src/entities/Obstacle.js @@ -1,11 +1,11 @@ class Obstacle { constructor(x, y) { this.position = createVector(x, y); - this.size = createVector(obstacleSize.w, obstacleSize.h); + this.size = createVector(heightInPixel / 12, heightInPixel / 12); } display() { fill(100); rect(this.position.x, this.position.y, this.size.x, this.size.y); } -} \ No newline at end of file +} diff --git a/docs/src/entities/Player.js b/docs/src/entities/Player.js index e683f0a..2182791 100644 --- a/docs/src/entities/Player.js +++ b/docs/src/entities/Player.js @@ -7,7 +7,7 @@ class Player { this.velocity = createVector(0, 0); this.atk = defaultAtk; this.maxAtk = playerMaxAtk; - this.size = createVector(playerSize.w, playerSize.h); + this.size = createVector(heightInPixel / 12, heightInPixel / 12); this.bullets = []; } diff --git a/docs/src/entities/Room.js b/docs/src/entities/Room.js new file mode 100644 index 0000000..4c2640c --- /dev/null +++ b/docs/src/entities/Room.js @@ -0,0 +1,66 @@ +class Room { + constructor() { + this.savePoint = null; + this.door = null; + this.enemies = []; + this.obstacles = []; + } + + setup() { + this.savePoint = new SavePoint(savePointParam.x, savePointParam.y); + this.door = new Door(); + this.generateObstacles(); + this.generateEnemies(); + startTime = millis(); + } + + update() { + image(officeRoomImg, 0, 0, widthInPixel, heightInPixel); + this.savePoint.display(); + this.updateObstacles(); + this.updateEnemies(); + this.updateDoor(); + this.checkClearCondition(); + } + + generateObstacles() { + this.obstacles = []; + const maxEntitySize = heightInPixel / 8; + for (let i = 0; i < obstacleCount; i++) { + let x = random(leftBoundary, rightBoundary - maxEntitySize); + let y = random(topBoundary, bottomBoundary - maxEntitySize); + this.obstacles.push(new Obstacle(x, y)); + } + } + + generateEnemies() { + this.enemies = []; + const maxEntitySize = heightInPixel / 8; + for (let i = 0; i < enemyCount; i++) { + let x = random(leftBoundary, rightBoundary - maxEntitySize); + let y = random(topBoundary, bottomBoundary - maxEntitySize); + let hp = random([smallEnemyHp, largeEnemyHp]); + this.enemies.push(new Enemy(x, y, hp)); + } + } + + updateObstacles() { + this.obstacles.forEach(o => o.display()); + } + + updateEnemies() { + this.enemies.forEach(e => { + e.update(); + e.display(); + }); + } + + updateDoor() { + if (this.checkClearCondition()) this.door.open(); + this.door.display(); + } + + checkClearCondition() { + return (this.enemies.length === 0 && player.hp > 0); + } +} diff --git a/docs/src/entities/SavePoint.js b/docs/src/entities/SavePoint.js index 1280194..1ded2ee 100644 --- a/docs/src/entities/SavePoint.js +++ b/docs/src/entities/SavePoint.js @@ -1,8 +1,7 @@ class SavePoint { constructor(x, y) { this.position = createVector(x, y); - this.size = savePointSize; - this.size = createVector(savePointSize.w, savePointSize.h); + this.size = createVector(savePointParam.w, savePointParam.h); } display() { diff --git a/docs/src/entities/enemies/Enemy.js b/docs/src/entities/enemies/Enemy.js index 1bdf08a..060f2f7 100644 --- a/docs/src/entities/enemies/Enemy.js +++ b/docs/src/entities/enemies/Enemy.js @@ -3,18 +3,18 @@ class Enemy { this.hp = hp; this.position = createVector(x, y); this.size = createVector( - hp === smallEnemyHp ? smallEnemySize.w : largeEnemySize.w, - hp === smallEnemyHp ? smallEnemySize.h : largeEnemySize.h + hp === smallEnemyHp ? heightInPixel / 12 : largeEnemySize.w, + hp === smallEnemyHp ? heightInPixel / 12 : largeEnemySize.h ); this.velocity = createVector(random([-1, 1]), random([-1, 1])); } update() { this.position.add(this.velocity); - if (this.position.x < 0 || this.position.x > widthInPixel - this.size.x) { + if (this.position.x < leftBoundary || this.position.x > rightBoundary - this.size.x) { this.velocity.x *= -1; } - if (this.position.y < 0 || this.position.y > heightInPixel - this.size.y) { + if (this.position.y < topBoundary || this.position.y > bottomBoundary - this.size.y) { this.velocity.y *= -1; } } diff --git a/docs/src/ui/hud.js b/docs/src/ui/hud.js index 0de278c..9fb8348 100644 --- a/docs/src/ui/hud.js +++ b/docs/src/ui/hud.js @@ -40,7 +40,9 @@ function drawHealthBar() { function drawCurrentLevel() { fill(255); - textFont('Courier New', uiTextSize); + stroke(0); + strokeWeight(5); + textFont(uiFont, uiTextSize); textAlign(LEFT, BOTTOM); text(`Level:${currentLevel}-${currentStage}`, hPadding, heightInPixel - vPadding); } @@ -51,9 +53,7 @@ function drawBossStatus() { let barX = (widthInPixel / 2) - (bossHpWidth / 2); // Draw HP bar background - fill(200); - stroke(0); - strokeWeight(3); + fill(0); rect(barX, vPadding, bossHpWidth, bossHpHeight, bossHpCorner); // Draw HP bar @@ -65,9 +65,6 @@ function drawBossStatus() { let lineX = barX + bossHpWidth * i; line(lineX, vPadding, lineX, vPadding + bossHpHeight); } - - // Back to default weight - strokeWeight(1); } function adjustBossStatusColor(percentage) { @@ -86,7 +83,9 @@ function drawTimer() { let secs = totalSecs % 60; fill(255); - textFont('Courier New', uiTextSize); + stroke(0); + strokeWeight(5); + textFont(uiFont, uiTextSize); textAlign(RIGHT, BOTTOM); text(`Time Taken:${String(mins).padStart(2, '0')}:${String(secs).padStart(2, '0')}`, widthInPixel - hPadding, heightInPixel - vPadding); } diff --git a/docs/src/ui/menu.js b/docs/src/ui/menu.js index 1dac26c..fb54e37 100644 --- a/docs/src/ui/menu.js +++ b/docs/src/ui/menu.js @@ -27,17 +27,15 @@ function setupPauseMenu() { } function drawMenu() { - fill(0); - textSize(uiTextSize); - textAlign(CENTER, CENTER); - text("Welcome to the Game!", widthInPixel / 2, heightInPixel / 3); - + image(startMenuImg, 0, 0, widthInPixel, heightInPixel); btnContinue.position(windowWidth / 2 - hPadding, windowHeight / 2 - vPadding); btnNewGame.position(windowWidth / 2 - hPadding, windowHeight / 2 + vPadding); } function drawPauseMenu() { - fill(0); + fill(255); + stroke(0); + strokeWeight(5); textSize(uiTextSize); textAlign(CENTER, CENTER); text("Paused", widthInPixel / 2, heightInPixel / 3); diff --git a/docs/src/utils.js b/docs/src/utils.js index 404dc4f..afb035b 100644 --- a/docs/src/utils.js +++ b/docs/src/utils.js @@ -1,5 +1,4 @@ // TODO: Merge into classes -let tempPlayerHp; let lastCollisionTime = 0; let menuDisplayed = true; @@ -7,15 +6,20 @@ let isGamePaused = false; let isBossStage = false; let btnPause, btnResume, btnExit, btnContinue, btnNewGame; let inputHandler = null; +let room = null; + +let openDoorImg, closedDoorImg; let startTime; let timeSpent = 0; -let savePointX = 300; -let savePointY = 200; let currentLevel = 1; let currentStage = 1; +const uiTextSize = 20; +const hPadding = 50; +const vPadding = 20; + let nearSavedPosition = false; let lastSavedPosition = { xPos: null, yPos: null }; let minDistanceToSave = 50; @@ -25,11 +29,10 @@ const chaserSize = 30; const shootSize = 25; const smallEnemyHp = 50; const largeEnemyHp = 100; -const smallEnemySize = { w: 40, h: 40 }; const largeEnemySize = { w: 50, h: 60 }; -let player, savePoint; -let enemyCount = 5; +let player; +let enemyCount = 1; let obstacleCount = 5; // Add variables and functions from feature_enemies_lyz_before0225 @@ -42,7 +45,7 @@ const tutorialMessages = [ "You can start the game now!" ]; -const obstacleSize = { w: 30, h: 30 }; +const doorSize = { w: 73, h: 95 }; const defaultSpeed = 5; const defaultHp = 3; @@ -50,12 +53,8 @@ const defaultAtk = 50; const playerMaxHp = 5; const playerMaxSpeed = 15; const playerMaxAtk = 20; -const playerSize = { w: 20, h: 20 }; -const playerX = 50; -const playerY = 50; - -const savePointSize = { w: 30, h: 30 }; +const savePointParam = { x: 300, y: 200, w: 30, h: 30 }; const iconSize = 24; const iconPadding = 15; @@ -66,7 +65,11 @@ const bossHpCorner = 10; const widthInPixel = 1024; const heightInPixel = 576; +const boundaryInPixel = { w: 80 , h: 72 } +const leftBoundary = boundaryInPixel.w; +const rightBoundary = widthInPixel - boundaryInPixel.w; +const topBoundary = boundaryInPixel.h; +const bottomBoundary = heightInPixel - boundaryInPixel.h; -const uiTextSize = 24; -const hPadding = 50; -const vPadding = 20; +const playerX = leftBoundary; +const playerY = heightInPixel / 2;