DEMO
ดาวน์โหลดทรัพยากรของเกม
บทนำ
เกมแพลตฟอร์มเป็นแนวเกมที่ได้รับความนิยมมาก เนื่องจากมีรูปแบบการเล่นที่เข้าใจง่ายและสามารถพัฒนาได้หลากหลายรูปแบบ ในบทความนี้ เราจะอธิบายการสร้างเกมแพลตฟอร์มโดยใช้ HTML5 Canvas และ JavaScript พร้อมอธิบายโครงสร้างโค้ดทั้งหมดอย่างละเอียด
โครงสร้างหลักของโค้ด
โค้ดที่เราใช้พัฒนาเกมนี้แบ่งออกเป็นส่วนต่างๆ ได้แก่:
- การกำหนดค่าเริ่มต้นและการสร้าง Canvas
- การโหลด sprite และการแสดงผลตัวละคร
- การสร้างแพลตฟอร์มและไอเท็ม
- การควบคุมตัวละคร
- การอัปเดตและแสดงผลเกม
- การตรวจจับการชนและกฎของเกม
1. การกำหนดค่าเริ่มต้นและการสร้าง Canvas
ในส่วนแรกของโค้ด เราจะกำหนดค่าเริ่มต้นและสร้าง Canvas สำหรับการแสดงผลของเกม
const canvas = document.getElementById("gameCanvas");
const ctx = canvas.getContext("2d");
canvas.width = 800;
canvas.height = 400;
อธิบายการทำงาน:
document.getElementById("gameCanvas")
ใช้เพื่อดึง<canvas>
จาก HTMLgetContext("2d")
ใช้สำหรับการวาดภาพแบบ 2 มิติบน Canvas- กำหนดขนาดของ Canvas เป็น 800x400 พิกเซล
2. การโหลด Sprite และการแสดงผลตัวละคร
const player = {
x: 50,
y: 300,
width: 40,
height: 50,
color: "blue",
speed: 5,
dx: 0,
dy: 0,
gravity: 0.5,
jumpPower: -10,
onGround: false
};
อธิบายการทำงาน:
player.x
,player.y
เป็นตำแหน่งของตัวละครwidth
,height
เป็นขนาดของตัวละครcolor
กำหนดสีของตัวละครspeed
เป็นความเร็วการเคลื่อนที่ในแนวราบdx
,dy
เป็นตัวแปรสำหรับความเร็วของตัวละครgravity
จำลองแรงโน้มถ่วงให้ตัวละครตกลงสู่พื้นjumpPower
กำหนดแรงกระโดดonGround
ใช้ตรวจสอบว่าตัวละครอยู่บนพื้นหรือไม่
3. การสร้างแพลตฟอร์มและไอเท็ม
const platforms = [
{ x: 0, y: 350, width: 800, height: 50, color: "green" },
{ x: 200, y: 250, width: 150, height: 20, color: "brown" },
{ x: 400, y: 200, width: 100, height: 20, color: "brown" }
];
อธิบายการทำงาน:
platforms
เป็นอาร์เรย์ที่เก็บข้อมูลของแพลตฟอร์มทั้งหมดในเกม- กำหนดค่าตำแหน่ง (
x
,y
), ขนาด (width
,height
), และสี (color
) ให้แต่ละแพลตฟอร์ม
4. การควบคุมตัวละคร
document.addEventListener("keydown", (e) => {
if (e.key === "ArrowRight") player.dx = player.speed;
if (e.key === "ArrowLeft") player.dx = -player.speed;
if (e.key === "ArrowUp" && player.onGround) {
player.dy = player.jumpPower;
player.onGround = false;
}
});
document.addEventListener("keyup", (e) => {
if (e.key === "ArrowRight" || e.key === "ArrowLeft") player.dx = 0;
});
อธิบายการทำงาน:
- ตรวจจับการกดปุ่มลูกศร (
keydown
) เพื่อให้ตัวละครเคลื่อนที่ไปทางซ้าย, ขวา และกระโดด keyup
ใช้เพื่อลดความเร็ว (dx
) เมื่อปล่อยปุ่มArrowUp
ใช้กระโดด โดยจะกระโดดได้เมื่อonGround
เป็นtrue
5. การอัปเดตและแสดงผลเกม
function update() {
player.dy += player.gravity;
player.x += player.dx;
player.y += player.dy;
if (player.y + player.height > canvas.height) {
player.y = canvas.height - player.height;
player.dy = 0;
player.onGround = true;
}
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = player.color;
ctx.fillRect(player.x, player.y, player.width, player.height);
platforms.forEach(platform => {
ctx.fillStyle = platform.color;
ctx.fillRect(platform.x, platform.y, platform.width, platform.height);
});
}
function gameLoop() {
update();
draw();
requestAnimationFrame(gameLoop);
}
gameLoop();
อธิบายการทำงาน:
update()
อัปเดตค่าตำแหน่งของตัวละครและใช้แรงโน้มถ่วงdraw()
วาดตัวละครและแพลตฟอร์มลงบน CanvasgameLoop()
เรียกใช้update()
และdraw()
อย่างต่อเนื่องโดยใช้requestAnimationFrame()
6. การตรวจจับการชน
platforms.forEach(platform => {
if (
player.x < platform.x + platform.width &&
player.x + player.width > platform.x &&
player.y + player.height > platform.y &&
player.y + player.height - player.dy < platform.y + 10
) {
player.y = platform.y - player.height;
player.dy = 0;
player.onGround = true;
}
});
อธิบายการทำงาน:
- ตรวจสอบว่าตัวละครชนกับแพลตฟอร์มหรือไม่
- ถ้าชนกับพื้นของแพลตฟอร์ม จะหยุดการตก (
dy = 0
) และกำหนดonGround = true
สรุป
เกมแพลตฟอร์มนี้มีโครงสร้างหลักที่ครบถ้วน ตั้งแต่การสร้าง Canvas, โหลด sprite, การควบคุมตัวละคร, การแสดงผล และการตรวจจับการชนกับแพลตฟอร์ม คุณสามารถนำโค้ดนี้ไปพัฒนาต่อยอดได้ เช่น เพิ่มศัตรู, ไอเท็ม หรือระบบคะแนน!
ตัวอย่างเกม
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Platform Game</title> <style> body { display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background-color: #f0f0f0; font-family: Arial, sans-serif; } #menu { text-align: center; } #menu h1 { font-size: 3rem; margin-bottom: 20px; } #menu button { padding: 15px 30px; font-size: 1.2rem; background-color: #4CAF50; color: white; border: none; border-radius: 5px; cursor: pointer; } #menu button:hover { background-color: #45a049; } canvas { border: 1px solid black; display: none; /* ซ่อน Canvas จนกว่าเกมจะเริ่ม */ } #gameOverModal { display: none; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background-color: rgba(0, 0, 0, 0.8); color: white; padding: 20px; border-radius: 10px; z-index: 1000; } #gameOverModal h2 { margin: 0; } #gameOverModal button { margin-top: 20px; padding: 10px 20px; background-color: red; color: white; border: none; border-radius: 5px; cursor: pointer; } #gameOverModal button:hover { background-color: darkred; } </style> </head> <body> <div id="menu"> <h1>Platform Game</h1> <button id="startButton">Start Game</button> </div> <canvas id="gameCanvas" width="800" height="600"></canvas> <div id="gameOverModal"> <h2>Game Over!</h2> <p>Your score: <span id="finalScore"></span></p> <button id="restartButton">Restart Game</button> </div> <script> // JavaScript สำหรับควบคุมหน้าเมนูและเริ่มเกม const menu = document.getElementById('menu'); const startButton = document.getElementById('startButton'); const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); const gameOverModal = document.getElementById('gameOverModal'); const restartButton = document.getElementById('restartButton'); const finalScoreDisplay = document.getElementById('finalScore'); startButton.addEventListener('click', () => { menu.style.display = 'none'; // ซ่อนหน้าเมนู canvas.style.display = 'block'; // แสดง Canvas resetGame(); // เริ่มเกม }); // ส่วนที่เหลือของโค้ดเกม (ตามโค้ดเดิมของคุณ) const player = { x: 50, y: canvas.height - 150 - 150, width: 150, height: 150, speed: 5, velocityX: 0, velocityY: 0, jumping: false, sprite: { Idle: [], Run: [], RunLeft: [], Jump: [], Dead: [] }, currentSprite: [], frameIndex: 0, facingLeft: false, lastFrameTime: 0, }; const background = { x: 0, image: new Image(), speed: 2.5 }; let items = []; const itemImages = []; const itemScores = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]; let itemInterval; let score = 0; let enemies = []; const enemyImages = []; let enemyInterval; let hp = 5; let animationFrameId; let isGameRunning = false; const frameDuration = 1000 / 16; background.image.src = './img/background.png'; background.image.onload = () => { console.log("Background image loaded successfully."); }; background.image.onerror = () => { console.error("Failed to load background image."); }; function loadSprite(spriteType, count) { for (let i = 1; i <= count; i++) { const img = new Image(); img.src = './img/' + spriteType + '(' + i + ').png'; img.onload = () => { player.sprite[spriteType].push(img); console.log(`Loaded ${spriteType} sprite: ${img.src}`); if (spriteType === 'Idle' && player.sprite[spriteType].length === count) { player.currentSprite = player.sprite.Idle; update(performance.now()); } }; img.onerror = () => { console.error(`Failed to load image: ${img.src}`); }; } } function loadItemImages(count) { for (let i = 1; i <= count; i++) { const img = new Image(); img.src = './img/item' + i + '.png'; img.onload = () => { itemImages.push(img); console.log(`Loaded item image: ${img.src}`); }; img.onerror = () => { console.error(`Failed to load item image: ${img.src}`); }; } } function loadEnemyImages(count) { for (let i = 1; i <= count; i++) { const img = new Image(); img.src = './img/enemy' + i + '.png'; img.onload = () => { enemyImages.push(img); console.log(`Loaded enemy image: ${img.src}`); }; img.onerror = () => { console.error(`Failed to load enemy image: ${img.src}`); }; } } loadSprite('Idle', 10); loadSprite('Run', 8); loadSprite('Jump', 10); loadSprite('Dead', 10); loadItemImages(12); loadEnemyImages(4); function drawBackground() { if (background.image.complete) { ctx.drawImage(background.image, background.x, 0, canvas.width, canvas.height); if (background.x < 0) { ctx.drawImage(background.image, background.x + canvas.width, 0, canvas.width, canvas.height); } else if (background.x > 0) { ctx.drawImage(background.image, background.x - canvas.width, 0, canvas.width, canvas.height); } if (background.x + canvas.width < canvas.width) { ctx.drawImage(background.image, background.x + canvas.width, 0, canvas.width, canvas.height); } } else { console.warn("Background image not loaded."); } } function drawPlayer() { const sprite = player.currentSprite[player.frameIndex]; if (sprite && sprite.complete) { if (player.facingLeft) { ctx.save(); ctx.translate(player.x + player.width, player.y); ctx.scale(-1, 1); ctx.drawImage(sprite, 0, 0, player.width, player.height); ctx.restore(); } else { ctx.drawImage(sprite, player.x, player.y, player.width, player.height); } } else { console.warn("Player sprite not loaded or undefined.", sprite); } } function drawItems() { items.forEach(item => { if (item.image && item.image.complete) { ctx.drawImage(item.image, item.x, item.y, 50, 50); } else { console.warn("Item image not loaded or undefined.", item); } }); } function drawEnemies() { enemies.forEach(enemy => { if (enemy.image && enemy.image.complete) { ctx.drawImage(enemy.image, enemy.x, enemy.y, enemy.width, enemy.height); } else { console.warn("Enemy image not loaded or undefined.", enemy); } }); } function drawText() { ctx.font = '24px Arial'; ctx.fillStyle = 'red'; ctx.fillText(`Score: ${score}`, 20, 30); ctx.fillStyle = 'green'; ctx.fillText(`HP: ${hp}`, canvas.width - 100, 30); } function updateBackground() { if (player.velocityX > 0 && player.x > canvas.width / 2 - player.width) { background.x -= player.velocityX * background.speed; player.x = canvas.width / 2 - player.width; } else if (player.velocityX < 0 && player.x <= 100) { background.x -= player.velocityX * background.speed; player.x = 100; } if (background.x <= -canvas.width) { background.x = 0; } else if (background.x >= canvas.width) { background.x = 0; } } function updateItems() { items.forEach(item => { item.x -= background.speed; }); items = items.filter(item => item.x + 50 > 0); } function updateEnemies() { enemies.forEach(enemy => { const enemySpeed = player.jumping ? 10 : 8; enemy.x -= enemySpeed; console.log(enemySpeed) }); enemies = enemies.filter(enemy => enemy.x + enemy.width > 0); } function checkCollisions() { items.forEach((item, index) => { if (player.x - 50 < item.x && player.x + player.width > item.x && player.y < item.y && player.y-50 + player.height > item.y) { score += item.score; items.splice(index, 1); } }); enemies.forEach((enemy, index) => { if (player.x + 80 < enemy.x + enemy.width && // ด้านขวาของผู้เล่น > ด้านซ้ายของศัตรู player.x + player.width > enemy.x + 80 && // ด้านซ้ายของผู้เล่น < ด้านขวาของศัตรู player.y < enemy.y + enemy.height&& // && ด้านล่างของผู้เล่น > ด้านบนของศัตรู player.y + player.height > enemy.y) { // ด้านบนของผู้เล่น < ด้านล่างของศัตรู hp -= 1; enemies.splice(index, 1); if (hp <= 0) { showGameOver(); } } }); } function update(currentTime) { if (!isGameRunning) return; const deltaTime = currentTime - player.lastFrameTime; if (deltaTime >= frameDuration) { player.frameIndex = (player.frameIndex + 1) % player.currentSprite.length; player.lastFrameTime = currentTime; } player.x += player.velocityX; player.y += player.velocityY; if (player.x < 0) { player.x = 0; } else if (player.x + player.width > canvas.width) { player.x = canvas.width - player.width; } if (player.jumping) { player.velocityY += 0.25; if (player.y + player.height >= canvas.height - 150) { player.y = canvas.height - 150 - player.height; player.velocityY = 0; player.jumping = false; player.currentSprite = player.velocityX !== 0 ? player.sprite.Run : player.sprite.Idle; } } updateBackground(); updateItems(); updateEnemies(); checkCollisions(); ctx.clearRect(0, 0, canvas.width, canvas.height); drawBackground(); drawPlayer(); drawItems(); drawEnemies(); drawText(); animationFrameId = requestAnimationFrame(update); } document.addEventListener('keydown', (e) => { if (e.code === 'ArrowRight') { player.velocityX = player.speed; player.facingLeft = false; player.currentSprite = player.sprite.Run; startItemGeneration(); } else if (e.code === 'ArrowLeft') { player.velocityX = -player.speed; player.facingLeft = true; player.currentSprite = player.sprite.Run; clearItemGeneration(); } else if (e.code === 'ArrowUp' && !player.jumping) { player.velocityY = -9.5; player.jumping = true; player.currentSprite = player.sprite.Jump; } }); document.addEventListener('keyup', (e) => { if (e.code === 'ArrowRight' || e.code === 'ArrowLeft') { player.velocityX = 0; player.currentSprite = player.velocityY !== 0 ? player.sprite.Jump : player.sprite.Idle; clearItemGeneration(); } }); function createItem() { const itemImage = itemImages[Math.floor(Math.random() * itemImages.length)]; const itemScore = itemScores[Math.floor(Math.random() * itemScores.length)]; const item = { image: itemImage, x: canvas.width, y: 150, score: itemScore }; items.push(item); } function createEnemy() { const enemyImage = enemyImages[Math.floor(Math.random() * enemyImages.length)]; const enemyWidth = 100; const enemy = { image: enemyImage, x: canvas.width, y: canvas.height - 150 - 100, width: enemyWidth, height: 100 }; enemies.push(enemy); } function startItemGeneration() { clearItemGeneration(); const randomInterval = Math.floor(Math.random() * 5000) + 4000; itemInterval = setInterval(() => { createItem(); const nextInterval = Math.floor(Math.random() * 5000) + 4000; clearInterval(itemInterval); itemInterval = setTimeout(startItemGeneration, nextInterval); }, randomInterval); } function startEnemyGeneration() { enemyInterval = setInterval(createEnemy, 8000); } function clearItemGeneration() { if (itemInterval) { clearInterval(itemInterval); clearTimeout(itemInterval); itemInterval = null; } } function clearEnemyGeneration() { if (enemyInterval) { clearInterval(enemyInterval); enemyInterval = null; } } function resetGame() { score = 0; hp = 5; items = []; enemies = []; player.x = 50; player.y = canvas.height - 150 - 150; player.velocityX = 0; player.velocityY = 0; player.jumping = false; player.currentSprite = player.sprite.Idle; background.x = 0; clearItemGeneration(); clearEnemyGeneration(); startItemGeneration(); startEnemyGeneration(); gameOverModal.style.display = 'none'; isGameRunning = true; update(performance.now()); } function showGameOver() { isGameRunning = false; cancelAnimationFrame(animationFrameId); clearItemGeneration(); clearEnemyGeneration(); finalScoreDisplay.textContent = score; gameOverModal.style.display = 'block'; } restartButton.addEventListener('click', resetGame); startItemGeneration(); startEnemyGeneration(); player.lastFrameTime = performance.now(); update(player.lastFrameTime); </script> </body> </html>
ขอให้สนุกกับการพัฒนาเกม!