2.7: Мотор гри
Що ми будемо робити?
У цьому підрозділі ми створимо клас Game.js
, який буде головним класом гри, що координує всі компоненти та керує ігровим циклом.
Створення класу Game.js
Створіть файл Game.js
:
javascript
/**
* 🎮 Клас Game - головний клас гри
*
* Відповідає за:
* - Ініціалізацію всіх компонентів гри
* - Управління ігровим циклом
* - Координацію між різними частинами гри
*/
import { canvas, ctx, GAME_CONFIG, logger } from './main.js';
import { Player } from './Player.js';
import { Enemy } from './Enemy.js';
import { GameField } from './GameField.js';
export class Game {
constructor() {
// Canvas елемент з HTML
this.canvas = canvas;
// контекст для малювання
this.ctx = ctx;
// конфігурація гри
this.config = GAME_CONFIG;
// гравець (поки що не створений)
this.player = null;
// ворог (поки що не створений)
this.enemy = null;
// ігрове поле (поки що не створене)
this.gameField = null;
// чи запущена гра
this.isRunning = false;
// час останнього кадру
this.lastTime = 0;
}
/**
* Ініціалізація гри
* Створює всі необхідні об'єкти
*/
init() {
// записуємо в лог
logger.gameEvent('Ініціалізація гри');
// створюємо нове ігрове поле
this.gameField = new GameField(this.ctx, this.config);
this.player = new Player({
// позиція X гравця
x: 100,
// позиція Y гравця
y: 100,
// жовтий колір для гравця
color: '#f1c40f',
// розмір танка
size: this.config.TILE_SIZE
});
this.enemy = new Enemy({
// позиція X ворога
x: 300,
// позиція Y ворога
y: 200,
// червоний колір для ворога
color: '#e74c3c',
// розмір танка
size: this.config.TILE_SIZE
});
// записуємо успіх в лог
logger.success('Гра ініціалізована успішно!');
}
/**
* Запуск гри
*/
start() {
// позначаємо гру як запущену
this.isRunning = true;
// запускаємо ігровий цикл
this.gameLoop();
// записуємо в лог
logger.gameEvent('Гра запущена!');
}
/**
* Зупинка гри
*/
stop() {
// позначаємо гру як зупинену
this.isRunning = false;
// записуємо в лог
logger.gameEvent('Гра зупинена!');
}
/**
* Оновлення стану гри
* @param {number} deltaTime - Час з останнього оновлення
*/
update(deltaTime) {
// оновлюємо стан поля
this.gameField.update(deltaTime);
// оновлюємо стан гравця
this.player.update(deltaTime);
// оновлюємо стан ворога
this.enemy.update(deltaTime);
}
/**
* Малювання гри
*/
render() {
// видаляємо все попереднє малювання
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
// малюємо фон та сітку
this.gameField.render();
// малюємо жовтий танк гравця
this.player.render(this.ctx);
// малюємо червоний танк ворога
this.enemy.render(this.ctx);
}
/**
* Головний ігровий цикл
* @param {number} currentTime - Поточний час
*/
gameLoop(currentTime = 0) {
// якщо гра не запущена, виходимо
if (!this.isRunning) return;
// різниця часу між кадрами
const deltaTime = currentTime - this.lastTime;
// зберігаємо поточний час
this.lastTime = currentTime;
// оновлюємо всі об'єкти гри
this.update(deltaTime);
// малюємо все на Canvas
this.render();
// плануємо наступний кадр
requestAnimationFrame((time) => this.gameLoop(time));
}
}
Що робить цей клас?
Основні властивості:
canvas
- HTML Canvas елементctx
- контекст для малюванняconfig
- конфігурація гриplayer
- об'єкт гравцяenemy
- об'єкт ворогаgameField
- об'єкт ігрового поляisRunning
- чи запущена граlastTime
- час останнього кадру
Основні методи:
init()
- ініціалізація всіх компонентівstart()
- запуск гриstop()
- зупинка гриupdate(deltaTime)
- оновлення стану всіх об'єктівrender()
- малювання всіх об'єктівgameLoop(currentTime)
- головний ігровий цикл
Ігровий цикл
Структура циклу:
- Перевірка стану - чи гра запущена
- Розрахунок часу - різниця між кадрами
- Оновлення - оновлення всіх об'єктів
- Малювання - малювання всіх об'єктів
- Планування - планування наступного кадру
Порядок оновлення:
- Ігрове поле -
gameField.update()
- Гравець -
player.update()
- Ворог -
enemy.update()
Порядок малювання:
- Очищення Canvas -
clearRect()
- Ігрове поле -
gameField.render()
- Гравець -
player.render()
- Ворог -
enemy.render()
Ініціалізація об'єктів
Позиції танків:
- Гравець: (100, 100) - жовтий танк
- Ворог: (300, 200) - червоний танк
Розміри:
- Танки: 32x32 пікселі (розмір клітинки)
- Canvas: 800x600 пікселів
Використання
javascript
// Створення гри
const game = new Game();
// Ініціалізація
game.init();
// Запуск гри
game.start();
// Зупинка гри (опціонально)
// game.stop();
Логування
Клас автоматично логує:
- Ініціалізацію гри -
gameEvent('Ініціалізація гри')
- Успішну ініціалізацію -
success('Гра ініціалізована успішно!')
- Запуск гри -
gameEvent('Гра запущена!')
- Зупинку гри -
gameEvent('Гра зупинена!')
Особливості
requestAnimationFrame:
- Плавна анімація - 60 FPS
- Оптимізація - браузер сам керує частотою
- Економія ресурсів - зупиняється коли вкладка неактивна
deltaTime:
- Незалежність від FPS - рух буде однаковим на різних пристроях
- Плавність - компенсація змін частоти кадрів
Результат
Після створення цього класу у вас буде:
- ✅ Повноцінний ігровий движок
- ✅ Координація всіх компонентів
- ✅ Плавний ігровий цикл
- ✅ Готовність для додавання нових функцій
Що далі?
У наступному підрозділі ми оновимо головний файл main.js
для запуску всієї гри.