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>
ขอให้สนุกกับการพัฒนาเกม!