3.4: Рух гравця
Що ми будемо робити?
У цьому підрозділі ми оновимо клас Player.js
, додавши логіку руху за клавішами та інтеграцію з системою керування.
Оновлення класу Player.js
Оновіть файл Player.js
:
javascript
import { Tank } from './Tank.js';
import { logger } from './main.js';
/**
* 🎮 Клас Player - представляє гравця
*
* Відповідає за:
* - Специфічну логіку гравця
* - Керування гравцем
* - Рух за клавішами
*/
export class Player extends Tank {
constructor(options = {}) {
// Викликаємо конструктор батьківського класу Tank
super({
...options, // передаємо всі опції батьківському класу
// жовтий колір за замовчуванням
color: options.color || '#f1c40f',
// гравець рухається швидше за ворога
speed: options.speed || 2,
// початковий напрямок дула вгору
direction: options.direction || 'up'
});
// Система керування (буде встановлена ззовні)
this.inputManager = null;
// Межі руху (розміри Canvas)
this.bounds = {
minX: 0,
minY: 0,
maxX: 800,
maxY: 600
};
// Стан руху
this.movementState = {
isMoving: false,
lastDirection: 'up'
};
// записуємо в лог
logger.playerAction('Гравець створений', `позиція: (${this.x}, ${this.y})`);
}
/**
* Встановлення системи керування
* @param {InputManager} inputManager - Система керування
*/
setInputManager(inputManager) {
this.inputManager = inputManager;
logger.info('Система керування підключена до гравця');
}
/**
* Встановлення меж руху
* @param {Object} bounds - Межі руху
*/
setBounds(bounds) {
this.bounds = { ...this.bounds, ...bounds };
}
/**
* Оновлення стану гравця
* @param {number} deltaTime - Час з останнього оновлення
*/
update(deltaTime) {
if (!this.isAlive) return;
// Оновлюємо рух
this.updateMovement(deltaTime);
// Оновлюємо напрямок дула
this.updateDirection();
}
/**
* Оновлення руху гравця
* @param {number} deltaTime - Час з останнього оновлення
*/
updateMovement(deltaTime) {
if (!this.inputManager) return;
// Отримуємо напрямок руху від системи керування
const direction = this.inputManager.getMovementDirection();
// Розраховуємо нову позицію
let newX = this.x;
let newY = this.y;
let isMoving = false;
// Рух вгору
if (direction.up) {
newY -= this.speed;
isMoving = true;
this.movementState.lastDirection = 'up';
}
// Рух вниз
if (direction.down) {
newY += this.speed;
isMoving = true;
this.movementState.lastDirection = 'down';
}
// Рух вліво
if (direction.left) {
newX -= this.speed;
isMoving = true;
this.movementState.lastDirection = 'left';
}
// Рух вправо
if (direction.right) {
newX += this.speed;
isMoving = true;
this.movementState.lastDirection = 'right';
}
// Перевіряємо межі руху
if (this.checkBounds(newX, newY)) {
this.x = newX;
this.y = newY;
}
// Оновлюємо стан руху
this.movementState.isMoving = isMoving;
// Логуємо рух (тільки при зміні стану)
if (isMoving && !this.movementState.isMoving) {
logger.playerAction('Гравець почав рухатися', `напрямок: ${this.movementState.lastDirection}`);
}
}
/**
* Оновлення напрямку дула
*/
updateDirection() {
if (!this.inputManager) return;
const direction = this.inputManager.getMovementDirection();
// Встановлюємо напрямок дула відповідно до руху
if (direction.up) {
this.direction = 'up';
} else if (direction.down) {
this.direction = 'down';
} else if (direction.left) {
this.direction = 'left';
} else if (direction.right) {
this.direction = 'right';
}
// Якщо не рухається, залишаємо попередній напрямок
}
/**
* Перевірка меж руху
* @param {number} newX - Нова X координата
* @param {number} newY - Нова Y координата
* @returns {boolean} - true якщо позиція в межах
*/
checkBounds(newX, newY) {
return newX >= this.bounds.minX &&
newX + this.width <= this.bounds.maxX &&
newY >= this.bounds.minY &&
newY + this.height <= this.bounds.maxY;
}
/**
* Малювання гравця на екрані
* @param {CanvasRenderingContext2D} ctx - Контекст для малювання
*/
render(ctx) {
// якщо гравець мертвий, не малюємо
if (!this.isAlive) return;
// зберігаємо поточний стан контексту (колір, стиль тощо)
ctx.save();
// викликаємо метод render батьківського класу
super.render(ctx);
// малюємо жовтий круг
this.drawPlayerMark(ctx);
// малюємо індикатор руху (якщо рухається)
if (this.movementState.isMoving) {
this.drawMovementIndicator(ctx);
}
// відновлюємо стан контексту (повертаємо попередні налаштування)
ctx.restore();
}
/**
* Малювання позначки гравця (жовтий круг)
* @param {CanvasRenderingContext2D} ctx - Контекст для малювання
*/
drawPlayerMark(ctx) {
// розмір позначки в пікселях
const markSize = 4;
// центр танка по X
const centerX = this.x + this.width / 2;
// центр танка по Y
const centerY = this.y + this.height / 2;
// помаранчево-жовтий колір
ctx.fillStyle = '#f39c12';
// починаємо малювати шлях
ctx.beginPath();
// малюємо коло
ctx.arc(centerX, centerY, markSize, 0, 2 * Math.PI);
// заповнюємо коло кольором
ctx.fill();
}
/**
* Малювання індикатора руху
* @param {CanvasRenderingContext2D} ctx - Контекст для малювання
*/
drawMovementIndicator(ctx) {
// зелений колір для індикатора руху
ctx.fillStyle = '#27ae60';
// розмір індикатора
const indicatorSize = 3;
// розміщуємо індикатор в правому нижньому куті танка
const indicatorX = this.x + this.width - indicatorSize - 2;
const indicatorY = this.y + this.height - indicatorSize - 2;
// малюємо маленький квадрат
ctx.fillRect(indicatorX, indicatorY, indicatorSize, indicatorSize);
}
/**
* Отримання позиції для стрільби
* @returns {Object} - Позиція кулі
*/
getShootPosition() {
const centerX = this.x + this.width / 2;
const centerY = this.y + this.height / 2;
// Розраховуємо позицію кулі залежно від напрямку
switch (this.direction) {
case 'up':
return { x: centerX - 2, y: this.y - 4 };
case 'down':
return { x: centerX - 2, y: this.y + this.height };
case 'left':
return { x: this.x - 4, y: centerY - 2 };
case 'right':
return { x: this.x + this.width, y: centerY - 2 };
default:
return { x: centerX - 2, y: this.y - 4 };
}
}
/**
* Отримання стану руху
* @returns {Object} - Стан руху
*/
getMovementState() {
return { ...this.movementState };
}
}
Що додано до класу Player?
Нові властивості:
inputManager
- посилання на систему керуванняbounds
- межі руху (розміри Canvas)movementState
- стан руху (чи рухається, останній напрямок)
Нові методи:
setInputManager()
- встановлення системи керуванняsetBounds()
- встановлення меж рухуupdateMovement()
- оновлення руху за клавішамиupdateDirection()
- оновлення напрямку дулаcheckBounds()
- перевірка меж рухуgetShootPosition()
- отримання позиції для стрільбиdrawMovementIndicator()
- малювання індикатора руху
Особливості руху
Керування:
- WASD або стрілки для руху
- Автоматичне оновлення напрямку дула
- Обмеження руху межами Canvas
Візуальні ефекти:
- Зелений індикатор руху в правому нижньому куті
- Логування початку руху
- Плавний рух з урахуванням швидкості
Система меж
Перевірка меж:
javascript
newX >= this.bounds.minX &&
newX + this.width <= this.bounds.maxX &&
newY >= this.bounds.minY &&
newY + this.height <= this.bounds.maxY
Налаштування меж:
- За замовчуванням: 0 до 800x600
- Налаштовується через
setBounds()
- Запобігає виходу за межі екрану
Позиція стрільби
Розрахунок позиції:
- Вгору: центр танка, вище танка
- Вниз: центр танка, нижче танка
- Вліво: центр танка, лівіше танка
- Вправо: центр танка, правіше танка
Використання
javascript
// Створення гравця
const player = new Player({
x: 100,
y: 100,
color: '#f1c40f',
size: 32
});
// Підключення системи керування
player.setInputManager(inputManager);
// Встановлення меж руху
player.setBounds({
maxX: 800,
maxY: 600
});
// Оновлення в ігровому циклі
player.update(deltaTime);
// Отримання позиції для стрільби
const shootPos = player.getShootPosition();
Результат
Після оновлення цього класу у вас буде:
- ✅ Рух гравця за клавішами WASD
- ✅ Автоматичне оновлення напрямку дула
- ✅ Обмеження руху межами екрану
- ✅ Візуальні індикатори руху
- ✅ Готовність для стрільби
Що далі?
У наступному підрозділі ми додамо стрільбу гравця з використанням клавіші пробілу.