فارسی

راهنمای جامع کانواس HTML5 برای توسعه بازی‌های دو بعدی، شامل راه‌اندازی، مفاهیم اصلی، بهینه‌سازی و تکنیک‌های پیشرفته.

کانواس HTML5: دروازه ورود شما به دنیای توسعه بازی‌های دو بعدی

عنصر کانواس (Canvas) در HTML5 یک پلتفرم قدرتمند و همه‌کاره برای ساخت بازی‌های دو بعدی مستقیماً در مرورگر وب فراهم می‌کند. این ویژگی باعث می‌شود بازی‌ها بدون نیاز به افزونه یا دانلود، در دسترس مخاطبان گسترده‌ای قرار گیرند. این راهنمای جامع شما را با اصول توسعه بازی با کانواس HTML5، از راه‌اندازی اولیه تا تکنیک‌های پیشرفته برای ساخت بازی‌های جذاب و بهینه، آشنا می‌کند.

چرا کانواس HTML5 را برای توسعه بازی‌های دو بعدی انتخاب کنیم؟

کانواس HTML5 مزایای متعددی برای توسعه بازی‌های دو بعدی ارائه می‌دهد:

راه‌اندازی محیط توسعه

برای شروع توسعه بازی با کانواس HTML5، به موارد زیر نیاز دارید:

در اینجا یک فایل HTML پایه برای راه‌اندازی کانواس شما آورده شده است:


<!DOCTYPE html>
<html>
<head>
  <title>اولین بازی کانواس من</title>
  <style>
    body { margin: 0; }
    canvas { background: #eee; display: block; margin: 0 auto; }
  </style>
</head>
<body>
  <canvas id="gameCanvas" width="640" height="480"></canvas>
  <script>
    const canvas = document.getElementById('gameCanvas');
    const ctx = canvas.getContext('2d');

    // کد بازی شما در اینجا قرار می‌گیرد
  </script>
</body>
</html>

این کد یک عنصر کانواس با شناسه "gameCanvas" ایجاد کرده و عرض و ارتفاع آن را تنظیم می‌کند. همچنین زمینه رندر دو بعدی (2D rendering context) را که برای ترسیم روی کانواس استفاده می‌شود، بازیابی می‌کند.

مفاهیم اصلی توسعه بازی با کانواس HTML5

حلقه بازی (The Game Loop)

حلقه بازی قلب هر بازی است. این یک چرخه مداوم است که وضعیت بازی را به‌روزرسانی می‌کند، گرافیک بازی را رندر می‌کند و ورودی کاربر را مدیریت می‌کند. یک حلقه بازی معمولی به این شکل است:


function gameLoop() {
  update();
  render();
  requestAnimationFrame(gameLoop);
}

function update() {
  // منطق بازی را به‌روزرسانی کنید (مثلاً موقعیت بازیکن، هوش مصنوعی دشمن)
}

function render() {
  // پاک کردن کانواس
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // ترسیم عناصر بازی (مثلاً بازیکن، دشمنان، پس‌زمینه)
}

requestAnimationFrame(gameLoop);

requestAnimationFrame یک API مرورگر است که یک تابع را برای فراخوانی قبل از رفرش بعدی صفحه زمان‌بندی می‌کند. این امر انیمیشن روان و کارآمد را تضمین می‌کند.

ترسیم اشکال و تصاویر

API کانواس متدهایی برای ترسیم اشکال مختلف از جمله مستطیل، دایره و خط فراهم می‌کند. همچنین به شما امکان می‌دهد تصاویر را روی کانواس ترسیم کنید.

ترسیم یک مستطیل


ctx.fillStyle = 'red'; // تنظیم رنگ پر کردن
ctx.fillRect(10, 10, 50, 50); // ترسیم یک مستطیل توپر در نقطه (10, 10) با عرض 50 و ارتفاع 50

ctx.strokeStyle = 'blue'; // تنظیم رنگ خط دور
ctx.strokeRect(70, 10, 50, 50); // ترسیم خط دور یک مستطیل در نقطه (70, 10) با عرض 50 و ارتفاع 50

ترسیم یک دایره


ctx.beginPath();
ctx.arc(150, 35, 25, 0, 2 * Math.PI); // ترسیم یک دایره در نقطه (150, 35) با شعاع 25
ctx.fillStyle = 'green';
ctx.fill();
ctx.closePath();

ترسیم یک تصویر


const image = new Image();
image.src = 'path/to/your/image.png';

image.onload = function() {
  ctx.drawImage(image, 200, 10); // ترسیم تصویر در نقطه (200, 10)
};

مدیریت ورودی کاربر

برای تعاملی کردن بازی خود، باید ورودی‌های کاربر مانند فشردن کلیدهای کیبورد، کلیک‌های ماوس و رویدادهای لمسی را مدیریت کنید. می‌توانید از event listener های جاوا اسکریپت برای شناسایی این رویدادها استفاده کنید.

ورودی کیبورد


document.addEventListener('keydown', function(event) {
  if (event.key === 'ArrowLeft') {
    // حرکت بازیکن به چپ
  }
  if (event.key === 'ArrowRight') {
    // حرکت بازیکن به راست
  }
});

ورودی ماوس


canvas.addEventListener('mousedown', function(event) {
  const x = event.clientX - canvas.offsetLeft;
  const y = event.clientY - canvas.offsetTop;

  // بررسی اینکه آیا کلیک در یک ناحیه خاص رخ داده است
});

تشخیص برخورد (Collision Detection)

تشخیص برخورد فرآیند تعیین زمانی است که دو شیء بازی با یکدیگر همپوشانی یا تلاقی دارند. این برای بسیاری از مکانیک‌های بازی مانند برخورد بازیکن با دشمن یا اصابت پرتابه‌ها ضروری است.

تشخیص برخورد مستطیلی ساده


function checkCollision(rect1, rect2) {
  return (
    rect1.x < rect2.x + rect2.width &&
    rect1.x + rect1.width > rect2.x &&
    rect1.y < rect2.y + rect2.height &&
    rect1.y + rect1.height > rect2.y
  );
}

// مثال استفاده:
const player = { x: 10, y: 10, width: 32, height: 32 };
const enemy = { x: 100, y: 100, width: 32, height: 32 };

if (checkCollision(player, enemy)) {
  // برخورد شناسایی شد!
}

انیمیشن اسپرایت (Sprite Animation)

انیمیشن اسپرایت تکنیکی است که برای ایجاد توهم حرکت با نمایش سریع دنباله‌ای از تصاویر (اسپرایت‌ها) استفاده می‌شود. هر تصویر یک فریم متفاوت از انیمیشن را نشان می‌دهد.

برای پیاده‌سازی انیمیشن اسپرایت، به یک sprite sheet نیاز دارید که یک تصویر واحد شامل تمام فریم‌های انیمیشن است. سپس می‌توانید از متد drawImage برای ترسیم فریم‌های خاص از sprite sheet روی کانواس استفاده کنید.


const spriteSheet = new Image();
spriteSheet.src = 'path/to/your/sprite-sheet.png';

const frameWidth = 32; // عرض هر فریم
const frameHeight = 32; // ارتفاع هر فریم
let currentFrame = 0; // ایندکس فریم فعلی

function animate() {
  // محاسبه مختصات x و y فریم فعلی در sprite sheet
  const spriteX = currentFrame * frameWidth;
  const spriteY = 0; // با فرض اینکه همه فریم‌ها در یک ردیف هستند

  // ترسیم فریم فعلی روی کانواس
  ctx.drawImage(
    spriteSheet,
    spriteX,
    spriteY,
    frameWidth,
    frameHeight,
    100, // مختصات x روی کانواس
    100, // مختصات y روی کانواس
    frameWidth,
    frameHeight
  );

  // افزایش ایندکس فریم فعلی
  currentFrame = (currentFrame + 1) % numberOfFrames; // numberOfFrames تعداد کل فریم‌های انیمیشن است
}

تکنیک‌های پیشرفته و بهینه‌سازی

حالات بازی (Game States)

مدیریت حالات مختلف بازی (مانند منو، بازی، توقف، پایان بازی) برای سازماندهی منطق بازی شما حیاتی است. می‌توانید از یک state machine ساده برای مدیریت این حالات استفاده کنید.


let gameState = 'menu'; // حالت اولیه بازی

function update() {
  switch (gameState) {
    case 'menu':
      updateMenu();
      break;
    case 'game':
      updateGame();
      break;
    case 'pause':
      updatePause();
      break;
    case 'gameover':
      updateGameOver();
      break;
  }
}

function render() {
  // پاک کردن کانواس
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  switch (gameState) {
    case 'menu':
      renderMenu();
      break;
    case 'game':
      renderGame();
      break;
    case 'pause':
      renderPause();
      break;
    case 'gameover':
      renderGameOver();
      break;
  }
}

استخرهای اشیاء (Object Pools)

ایجاد و تخریب مکرر اشیاء می‌تواند از نظر محاسباتی پرهزینه باشد. Object pools راهی برای استفاده مجدد از اشیاء به جای ایجاد اشیاء جدید فراهم می‌کنند. این کار می‌تواند به طور قابل توجهی عملکرد را بهبود بخشد، به خصوص برای بازی‌هایی با تعداد زیادی اشیاء پویا مانند پرتابه‌ها.


function createObjectPool(size, objectFactory) {
  const pool = [];

  for (let i = 0; i < size; i++) {
    pool.push(objectFactory());
  }

  return {
    get: function() {
      if (pool.length > 0) {
        return pool.pop();
      } else {
        // به صورت اختیاری، اگر استخر خالی بود یک شیء جدید ایجاد کنید
        return objectFactory();
      }
    },
    release: function(object) {
      pool.push(object);
    }
  };
}

// مثال استفاده:
function createBullet() {
  return { x: 0, y: 0, speed: 10, active: false };
}

const bulletPool = createObjectPool(100, createBullet);

نقشه‌های کاشی‌کاری (Tile Maps)

Tile maps یک تکنیک رایج برای ایجاد دنیاهای بازی است. یک tile map شبکه‌ای از کاشی‌ها است که هر کاشی یک تصویر یا الگوی کوچک را نشان می‌دهد. Tile maps برای ایجاد محیط‌های بازی بزرگ و پرجزئیات کارآمد هستند.

برای پیاده‌سازی tile maps، به یک tile sheet نیاز دارید که شامل تمام کاشی‌های مجزا است. همچنین به یک ساختار داده نیاز دارید که چیدمان tile map را تعریف کند. این ساختار داده می‌تواند یک آرایه دو بعدی ساده باشد.


const tileSheet = new Image();
tileSheet.src = 'path/to/your/tile-sheet.png';

const tileWidth = 32;
const tileHeight = 32;

const mapData = [
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  [0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
  [0, 1, 0, 0, 0, 0, 0, 0, 1, 0],
  [0, 1, 0, 0, 0, 0, 0, 0, 1, 0],
  [0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
];

function drawTileMap() {
  for (let row = 0; row < mapData.length; row++) {
    for (let col = 0; col < mapData[row].length; col++) {
      const tileIndex = mapData[row][col];

      // محاسبه مختصات x و y کاشی در tile sheet
      const spriteX = (tileIndex % numberOfTilesPerRow) * tileWidth; // numberOfTilesPerRow تعداد کاشی‌ها در هر ردیف tile sheet است
      const spriteY = Math.floor(tileIndex / numberOfTilesPerRow) * tileHeight;

      // ترسیم کاشی روی کانواس
      ctx.drawImage(
        tileSheet,
        spriteX,
        spriteY,
        tileWidth,
        tileHeight,
        col * tileWidth, // مختصات x روی کانواس
        row * tileHeight, // مختصات y روی کانواس
        tileWidth,
        tileHeight
      );
    }
  }
}

بهینه‌سازی عملکرد

بهینه‌سازی بازی کانواس شما برای دستیابی به عملکرد روان و پاسخگو، به ویژه در دستگاه‌های ضعیف‌تر، حیاتی است.

کتابخانه‌ها و فریمورک‌های مفید

چندین کتابخانه و فریمورک جاوا اسکریپت می‌توانند توسعه بازی با کانواس HTML5 را ساده‌تر کنند:

نمونه‌هایی از بازی‌های ساخته شده با کانواس HTML5

بسیاری از بازی‌های محبوب و موفق با استفاده از کانواس HTML5 ساخته شده‌اند که قابلیت‌های آن را به نمایش می‌گذارند:

نتیجه‌گیری

کانواس HTML5 یک پلتفرم قدرتمند و در دسترس برای توسعه بازی‌های دو بعدی است. با سازگاری بین پلتفرمی، استانداردهای باز و جامعه بزرگ، کانواس پایه‌ای محکم برای ساخت بازی‌های جذاب و بهینه فراهم می‌کند. با تسلط بر مفاهیم اصلی و تکنیک‌های پیشرفته مورد بحث در این راهنما، می‌توانید پتانسیل کامل کانواس HTML5 را آزاد کرده و ایده‌های بازی خود را به واقعیت تبدیل کنید.

به یاد داشته باشید که کتابخانه‌ها و فریمورک‌های موجود را برای ساده‌سازی بیشتر فرآیند توسعه و بهره‌گیری از قابلیت‌های از پیش ساخته شده، کاوش کنید. در مسیر توسعه بازی خود موفق باشید!