diff --git a/packages/client/src/assets/images/sprites/bang.png b/packages/client/src/assets/images/sprites/bang.png
new file mode 100644
index 0000000..b220ec0
Binary files /dev/null and b/packages/client/src/assets/images/sprites/bang.png differ
diff --git a/packages/client/src/assets/images/sprites/enemy.svg b/packages/client/src/assets/images/sprites/enemy.svg
deleted file mode 100644
index 51a4469..0000000
--- a/packages/client/src/assets/images/sprites/enemy.svg
+++ /dev/null
@@ -1,14 +0,0 @@
-
diff --git a/packages/client/src/assets/images/sprites/shot.png b/packages/client/src/assets/images/sprites/shot.png
new file mode 100644
index 0000000..991d07e
Binary files /dev/null and b/packages/client/src/assets/images/sprites/shot.png differ
diff --git a/packages/client/src/assets/images/sprites/steel.png b/packages/client/src/assets/images/sprites/steel.png
new file mode 100644
index 0000000..ad85f90
Binary files /dev/null and b/packages/client/src/assets/images/sprites/steel.png differ
diff --git a/packages/client/src/assets/images/sprites/tank.svg b/packages/client/src/assets/images/sprites/tank.svg
deleted file mode 100644
index 1c8083e..0000000
--- a/packages/client/src/assets/images/sprites/tank.svg
+++ /dev/null
@@ -1,14 +0,0 @@
-
diff --git a/packages/client/src/assets/images/sprites/tree.png b/packages/client/src/assets/images/sprites/tree.png
new file mode 100644
index 0000000..51bd31a
Binary files /dev/null and b/packages/client/src/assets/images/sprites/tree.png differ
diff --git a/packages/client/src/assets/images/sprites/wall.png b/packages/client/src/assets/images/sprites/wall.png
new file mode 100644
index 0000000..c84a5c9
Binary files /dev/null and b/packages/client/src/assets/images/sprites/wall.png differ
diff --git a/packages/client/src/assets/images/sprites/wall.svg b/packages/client/src/assets/images/sprites/wall.svg
deleted file mode 100644
index 380f0dd..0000000
--- a/packages/client/src/assets/images/sprites/wall.svg
+++ /dev/null
@@ -1,9 +0,0 @@
-
diff --git a/packages/client/src/components/Game/Game.tsx b/packages/client/src/components/Game/Game.tsx
index ddef2c4..abedf67 100644
--- a/packages/client/src/components/Game/Game.tsx
+++ b/packages/client/src/components/Game/Game.tsx
@@ -7,7 +7,7 @@ import {
import { PLAYER_DEFAULT_PARAMS } from '@/components/Game/player'
import { gameLoop } from '@/components/Game/gameLoop'
import { handleKeyDown, handleKeyUp } from '@/components/Game/controls'
-import { AbstractEntity, Obstacle } from '@/components/Game/gameTypes'
+import { AbstractEntity, Effect, Obstacle } from '@/components/Game/gameTypes'
import {
initializeCompanyMapObstacle,
initializeRandomObstacle,
@@ -22,6 +22,7 @@ export const Game: React.FC = () => {
const enemiesRef = useRef(initializeRandomEnemies(5))
const bulletsRef = useRef([])
const obstaclesRef = useRef(initializeRandomObstacle(20))
+ const effectsRef = useRef([])
const livesRef = useRef(livesUse)
const [gameStarted, setGameStarted] = useState(false)
const [isPaused, setIsPaused] = useState(false)
@@ -50,6 +51,7 @@ export const Game: React.FC = () => {
enemiesRef,
bulletsRef,
obstaclesRef,
+ effectsRef,
livesRef,
handleGameOver
)
diff --git a/packages/client/src/components/Game/bullet.tsx b/packages/client/src/components/Game/bullet.tsx
index 2a69dc4..e527003 100644
--- a/packages/client/src/components/Game/bullet.tsx
+++ b/packages/client/src/components/Game/bullet.tsx
@@ -1,4 +1,10 @@
import { AbstractEntity } from '@/components/Game/gameTypes'
+import { createShotEffect } from './effects'
+
+const bulletSize = {
+ width: 12,
+ height: 18,
+}
export const createBullet = (enemy: AbstractEntity): AbstractEntity => {
const bulletSpeed = 5 // Задайте скорость пули
@@ -10,29 +16,31 @@ export const createBullet = (enemy: AbstractEntity): AbstractEntity => {
if (enemy.direction.y < 0) {
// Вверх
bulletX = enemy.x + enemy.width / 2 // Центр по X
- bulletY = enemy.y // Верхняя часть врага
+ bulletY = enemy.y - bulletSize.height // Верхняя часть врага
} else if (enemy.direction.y > 0) {
// Вниз
bulletX = enemy.x + enemy.width / 2 // Центр по X
- bulletY = enemy.y + enemy.height // Нижняя часть врага
+ bulletY = enemy.y + enemy.height + bulletSize.height // Нижняя часть врага
} else if (enemy.direction.x < 0) {
// Влево
- bulletX = enemy.x // Левый край врага
+ bulletX = enemy.x - bulletSize.height // Левый край врага
bulletY = enemy.y + enemy.height / 2 // Центр по Y
} else if (enemy.direction.x > 0) {
// Вправо
- bulletX = enemy.x + enemy.width // Правый край врага
+ bulletX = enemy.x + enemy.width + bulletSize.height // Правый край врага
bulletY = enemy.y + enemy.height / 2 // Центр по Y
} else {
bulletX = enemy.x + enemy.width / 2 // По умолчанию - центр
bulletY = enemy.y + enemy.height / 2 // По умолчанию - центр
}
+ createShotEffect(bulletX, bulletY, bulletDirection)
+
return {
- x: bulletX,
- y: bulletY,
- width: 5, // Ширина пули
- height: 5, // Высота пули
+ x: bulletX - bulletSize.width / 2,
+ y: bulletY - bulletSize.height / 2,
+ width: bulletSize.width, // Ширина пули
+ height: bulletSize.height, // Высота пули
speed: bulletSpeed,
direction: bulletDirection,
}
diff --git a/packages/client/src/components/Game/collision.tsx b/packages/client/src/components/Game/collision.tsx
index aa62ce3..1b7fd49 100644
--- a/packages/client/src/components/Game/collision.tsx
+++ b/packages/client/src/components/Game/collision.tsx
@@ -8,7 +8,8 @@ export const detectCollision = (
player.x < obstacle.x + obstacle.width &&
player.x + player.width > obstacle.x &&
player.y < obstacle.y + obstacle.height &&
- player.y + player.height > obstacle.y
+ player.y + player.height > obstacle.y &&
+ obstacle.isCollide
)
}
diff --git a/packages/client/src/components/Game/effects.tsx b/packages/client/src/components/Game/effects.tsx
new file mode 100644
index 0000000..c9d67ed
--- /dev/null
+++ b/packages/client/src/components/Game/effects.tsx
@@ -0,0 +1,74 @@
+import { Direction, Effect } from '@/components/Game/gameTypes'
+import { MutableRefObject } from 'react'
+
+interface EffectSettings {
+ [key: string]: {
+ width: number
+ height: number
+ animation: Effect['animation']
+ }
+}
+
+let effectsRef: MutableRefObject
+
+const effectSettings: EffectSettings = {
+ bang: {
+ width: 60,
+ height: 60,
+ animation: {
+ frameInterval: 5,
+ frameCount: 0,
+ totalFrames: 8,
+ currentFrame: 0,
+ },
+ },
+ shot: {
+ width: 40,
+ height: 40,
+ animation: {
+ frameInterval: 5,
+ frameCount: 0,
+ totalFrames: 4,
+ currentFrame: 0,
+ },
+ },
+}
+
+export const initEffects = (effects: MutableRefObject) => {
+ effectsRef = effects
+}
+
+export const createEffect = (
+ type: string,
+ x: number,
+ y: number,
+ direction?: Direction
+) => {
+ if (effectsRef) {
+ effectsRef.current.push({
+ type,
+ x: x - effectSettings[type].width / 2,
+ y: y - effectSettings[type].height / 2,
+ width: effectSettings[type].width,
+ height: effectSettings[type].height,
+ direction,
+ animation: { ...effectSettings[type].animation },
+ })
+ }
+}
+
+export const createBangEffect = (x: number, y: number) => {
+ createEffect('bang', x, y)
+}
+
+export const createShotEffect = (
+ x: number,
+ y: number,
+ direction: Direction
+) => {
+ createEffect('shot', x, y, direction)
+}
+
+export const deleteEffect = (effect: Effect) => {
+ effectsRef.current = effectsRef.current.filter(i => i !== effect)
+}
diff --git a/packages/client/src/components/Game/enemy.tsx b/packages/client/src/components/Game/enemy.tsx
index 20b2526..7c8a3fc 100644
--- a/packages/client/src/components/Game/enemy.tsx
+++ b/packages/client/src/components/Game/enemy.tsx
@@ -1,7 +1,10 @@
import { getRandomEdgePosition } from './utils'
import { AbstractEntity, Enemy, Obstacle } from '@/components/Game/gameTypes'
import { createBullet } from '@/components/Game/bullet'
-import { detectCollision } from '@/components/Game/collision'
+import {
+ detectCollision,
+ detectEnemyCollision,
+} from '@/components/Game/collision'
const enemyParams = {
width: 70,
@@ -37,8 +40,8 @@ export const initializeCampanyEnemies = (): Enemy[] => {
{
...enemyParams,
id: 0,
- x: 50,
- y: 55,
+ x: 360,
+ y: 0,
animation: {
currentFrame: 0,
totalFrames: 4,
@@ -49,8 +52,8 @@ export const initializeCampanyEnemies = (): Enemy[] => {
{
...enemyParams,
id: 1,
- x: 320,
- y: 250,
+ x: 0,
+ y: 108,
animation: {
currentFrame: 0,
totalFrames: 4,
@@ -61,8 +64,8 @@ export const initializeCampanyEnemies = (): Enemy[] => {
{
...enemyParams,
id: 2,
- x: 715,
- y: 60,
+ x: 720,
+ y: 108,
animation: {
currentFrame: 0,
totalFrames: 4,
@@ -119,7 +122,7 @@ export const updateEnemyPositions = (
// Проверка столкновений с другими врагами
const hasEnemyCollision = enemiesRef.current.some(otherEnemy => {
if (otherEnemy === enemy) return false
- return detectCollision({ ...enemy, x: newX, y: newY }, otherEnemy)
+ return detectEnemyCollision({ ...enemy, x: newX, y: newY }, otherEnemy)
})
// Проверка коллизий с препятствиями
@@ -143,7 +146,7 @@ const isPositionOccupied = (
enemies: AbstractEntity[]
) => {
return enemies.some(enemy =>
- detectCollision({ ...enemy, x: position.x, y: position.y }, enemy)
+ detectEnemyCollision({ ...enemy, x: position.x, y: position.y }, enemy)
)
}
diff --git a/packages/client/src/components/Game/gameLoop.tsx b/packages/client/src/components/Game/gameLoop.tsx
index 5061a44..e1c8833 100644
--- a/packages/client/src/components/Game/gameLoop.tsx
+++ b/packages/client/src/components/Game/gameLoop.tsx
@@ -6,12 +6,14 @@ import {
drawEnemies,
drawObstacles,
drawBullets,
+ drawEffects,
} from './utils'
import {
ControlsProps,
AbstractEntity,
Obstacle,
Enemy,
+ Effect,
} from '@/components/Game/gameTypes'
import {
detectBulletCollision,
@@ -20,6 +22,7 @@ import {
import { updatePlayerAction } from '@/components/Game/controls'
import { updateBullets } from '@/components/Game/bullet'
import { handleBulletObstacleCollisions } from '@/components/Game/obstacle'
+import { createBangEffect, initEffects } from './effects'
/**
* Основной игровой цикл, который обновляет состояние игры и перерисовывает экран каждый кадр.
@@ -39,6 +42,7 @@ export const gameLoop = (
enemiesRef: React.MutableRefObject,
bulletsRef: React.MutableRefObject,
obstaclesRef: React.MutableRefObject,
+ effectsRef: React.MutableRefObject,
livesRef: React.MutableRefObject,
handleGameOver: () => void
) => {
@@ -62,6 +66,8 @@ export const gameLoop = (
// Обработка столкновений с препятствиями
handleBulletObstacleCollisions(bulletsRef.current, obstaclesRef.current)
+ initEffects(effectsRef)
+
bulletsRef.current = updateBullets(
bulletsRef.current,
canvasRef.current.width,
@@ -69,9 +75,10 @@ export const gameLoop = (
)
// Отрисовка всех игровых объектов
- drawObstacles(context, obstaclesRef.current)
drawPlayer(context, playerRef.current)
drawEnemies(context, enemiesRef.current)
+ drawObstacles(context, obstaclesRef.current)
+ drawEffects(context, effectsRef.current)
drawBullets(context, bulletsRef.current) // Отрисовка пуль
// Проверка на столкновения пуль с врагами
@@ -81,6 +88,11 @@ export const gameLoop = (
if (hit) {
// Убираем врага, если попали
killEnemy(enemiesRef, enemy)
+ // Эффект поподания
+ createBangEffect(
+ bullet.x + bullet.width / 2,
+ bullet.y + bullet.height / 2
+ )
// Убираем пулю, если попали
bulletsRef.current = bulletsRef.current.filter(b => b !== bullet)
return false
@@ -90,6 +102,11 @@ export const gameLoop = (
if (detectBulletCollision(bullet, playerRef.current)) {
// Уменьшаем жизни игрока
livesRef.current -= 1
+ // Эффект поподания
+ createBangEffect(
+ bullet.x + bullet.width / 2,
+ bullet.y + bullet.height / 2
+ )
// Удаляем пулю после попадания
bulletsRef.current = bulletsRef.current.filter(b => b !== bullet)
// Проверка на окончание игры
diff --git a/packages/client/src/components/Game/gameTypes.tsx b/packages/client/src/components/Game/gameTypes.tsx
index 7978ee5..b28238a 100644
--- a/packages/client/src/components/Game/gameTypes.tsx
+++ b/packages/client/src/components/Game/gameTypes.tsx
@@ -1,18 +1,23 @@
interface AnimationParams {
currentFrame: number
totalFrames: number
- frameInterval: number
+ frameInterval?: number
frameCount?: number
}
+export interface Direction {
+ x: number
+ y: number
+}
+
export interface AbstractEntity {
x: number
y: number
width: number
height: number
speed: number
- direction: { x: number; y: number }
- animation: AnimationParams
+ direction: Direction
+ animation?: AnimationParams
}
export interface Enemy extends AbstractEntity {
@@ -21,10 +26,24 @@ export interface Enemy extends AbstractEntity {
}
export interface Obstacle {
+ type: string
x: number
y: number
width: number
height: number
+ hp: number
+ isCollide: boolean
+ animation: AnimationParams
+}
+
+export interface Effect {
+ type: string
+ x: number
+ y: number
+ width: number
+ height: number
+ direction?: Direction
+ animation: AnimationParams
}
export interface ControlsProps {
diff --git a/packages/client/src/components/Game/obstacle.tsx b/packages/client/src/components/Game/obstacle.tsx
index e1e9d7e..64314ab 100644
--- a/packages/client/src/components/Game/obstacle.tsx
+++ b/packages/client/src/components/Game/obstacle.tsx
@@ -4,35 +4,164 @@ import {
detectCollision,
detectObstacleCollision,
} from '@/components/Game/collision'
+import { createBangEffect } from './effects'
+
+enum Types {
+ Wall = 'wall',
+ Steel = 'steel',
+ Tree = 'tree',
+}
+
+export const OBSTACLE_SIZE = 36
+
+const ObstacleSettings = {
+ [Types.Wall]: {
+ hp: 2,
+ isCollide: true,
+ frames: [
+ {
+ hp: 1,
+ index: 1,
+ },
+ {
+ hp: 2,
+ index: 0,
+ },
+ ],
+ },
+ [Types.Steel]: {
+ hp: 1000,
+ isCollide: true,
+ frames: [
+ {
+ hp: 1000,
+ index: 0,
+ },
+ ],
+ },
+ [Types.Tree]: {
+ hp: 1000,
+ isCollide: false,
+ frames: [
+ {
+ hp: 1000,
+ index: 0,
+ },
+ ],
+ },
+}
export const initializeCompanyMapObstacle = (): Obstacle[] => {
- return [
- { x: 0, y: 0, width: 120, height: 50 },
- { x: 200, y: 0, width: 70, height: 50 },
- { x: 350, y: 0, width: 70, height: 50 },
- { x: 500, y: 0, width: 120, height: 50 },
- { x: 700, y: 0, width: 100, height: 50 },
-
- { x: 0, y: 130, width: 50, height: 120 },
- { x: 130, y: 130, width: 50, height: 70 },
- { x: 260, y: 130, width: 50, height: 200 },
- { x: 390, y: 130, width: 50, height: 70 },
- { x: 520, y: 130, width: 50, height: 120 },
- { x: 650, y: 130, width: 50, height: 125 },
-
- { x: 0, y: 330, width: 120, height: 50 },
- { x: 200, y: 330, width: 150, height: 50 },
- { x: 350, y: 330, width: 70, height: 50 },
- { x: 500, y: 330, width: 120, height: 50 },
- { x: 700, y: 330, width: 100, height: 100 },
-
- { x: 0, y: 460, width: 50, height: 140 },
- { x: 130, y: 530, width: 50, height: 70 },
- { x: 260, y: 480, width: 50, height: 120 },
- { x: 390, y: 460, width: 50, height: 70 },
- { x: 520, y: 490, width: 50, height: 120 },
- { x: 650, y: 480, width: 50, height: 120 },
- ]
+ return createMap([
+ { type: Types.Steel, x: 72, y: 108, width: 72, height: 72 },
+ { type: Types.Steel, x: 648, y: 108, width: 72, height: 72 },
+
+ { type: Types.Steel, x: 144, y: 360, width: 72, height: 72 },
+ { type: Types.Steel, x: 576, y: 360, width: 72, height: 72 },
+ { type: Types.Steel, x: 360, y: 360, width: 72, height: 72 },
+
+ { type: Types.Wall, x: 252, y: 72, width: 108, height: 36 },
+ { type: Types.Wall, x: 324, y: 108, width: 36, height: 144 },
+ { type: Types.Wall, x: 252, y: 216, width: 72, height: 36 },
+ { type: Types.Wall, x: 252, y: 144, width: 72, height: 36 },
+ { type: Types.Wall, x: 252, y: 108, width: 36, height: 36 },
+
+ { type: Types.Wall, x: 432, y: 72, width: 108, height: 36 },
+ { type: Types.Wall, x: 504, y: 108, width: 36, height: 108 },
+ { type: Types.Wall, x: 432, y: 108, width: 36, height: 108 },
+ { type: Types.Wall, x: 432, y: 216, width: 108, height: 36 },
+
+ { type: Types.Wall, x: 72, y: 324, width: 72, height: 180 },
+ { type: Types.Wall, x: 216, y: 324, width: 72, height: 180 },
+ { type: Types.Wall, x: 504, y: 324, width: 72, height: 180 },
+ { type: Types.Wall, x: 648, y: 324, width: 72, height: 180 },
+
+ { type: Types.Tree, x: 0, y: 324, width: 72, height: 180 },
+ { type: Types.Tree, x: 720, y: 324, width: 72, height: 180 },
+ { type: Types.Tree, x: 0, y: 504, width: 180, height: 72 },
+ { type: Types.Tree, x: 612, y: 504, width: 180, height: 72 },
+ ])
+ return createMap([
+ // /{ type: Types.Steel, x: 0, y: 0, width: 120, height: 50 },
+ { type: Types.Steel, x: 200, y: 0, width: 70, height: 36 },
+ { type: Types.Steel, x: 350, y: 0, width: 70, height: 36 },
+ { type: Types.Steel, x: 500, y: 0, width: 120, height: 36 },
+ // { type: Types.Wall, x: 700, y: 0, width: 100, height: 50 },
+
+ // { type: Types.Wall, x: 0, y: 130, width: 50, height: 120 },
+ { type: Types.Wall, x: 130, y: 130, width: 50, height: 70 },
+ // { type: Types.Wall, x: 260, y: 130, width: 50, height: 200 },
+ { type: Types.Wall, x: 390, y: 130, width: 50, height: 70 },
+ { type: Types.Wall, x: 520, y: 130, width: 50, height: 120 },
+ // { type: Types.Wall, x: 650, y: 130, width: 50, height: 125 },
+
+ // { type: Types.Wall, x: 0, y: 330, width: 120, height: 50 },
+ { type: Types.Wall, x: 200, y: 330, width: 150, height: 36 },
+ { type: Types.Wall, x: 350, y: 330, width: 70, height: 36 },
+ { type: Types.Wall, x: 500, y: 330, width: 120, height: 36 },
+ // { type: Types.Wall, x: 700, y: 330, width: 100, height: 100 },
+
+ { type: Types.Wall, x: 0, y: 460, width: 50, height: 140 },
+ { type: Types.Wall, x: 130, y: 530, width: 50, height: 70 },
+ { type: Types.Wall, x: 260, y: 480, width: 50, height: 120 },
+ //{ type: Types.Wall, x: 390, y: 460, width: 50, height: 70 },
+ { type: Types.Wall, x: 520, y: 490, width: 50, height: 120 },
+ { type: Types.Wall, x: 650, y: 480, width: 50, height: 120 },
+ ])
+}
+
+export const createMap = (
+ params: {
+ type: Types
+ x: number
+ y: number
+ width: number
+ height: number
+ }[]
+) => {
+ let obstacles: Obstacle[] = []
+
+ params.forEach(obstacle => {
+ const { type, x, y, width, height } = obstacle
+ obstacles = [...obstacles, ...createObstacle(type, x, y, width, height)]
+ })
+
+ return obstacles
+}
+
+export const createObstacle = (
+ type: Types,
+ x: number,
+ y: number,
+ width: number,
+ height: number
+): Obstacle[] => {
+ const obstacles: Obstacle[] = []
+ const horizontalCount = Math.ceil(width / OBSTACLE_SIZE)
+ const verticalCount = Math.ceil(height / OBSTACLE_SIZE)
+
+ Array.from({ length: horizontalCount }).forEach((_, i) => {
+ Array.from({ length: verticalCount }).forEach((_, j) => {
+ const obstacleX = x + i * OBSTACLE_SIZE
+ const obstacleY = y + j * OBSTACLE_SIZE
+
+ obstacles.push({
+ type,
+ x: obstacleX,
+ y: obstacleY,
+ width: OBSTACLE_SIZE,
+ height: OBSTACLE_SIZE,
+ hp: ObstacleSettings[type].hp,
+ isCollide: ObstacleSettings[type].isCollide,
+ animation: {
+ currentFrame: 0,
+ totalFrames: ObstacleSettings[type].frames.length,
+ },
+ })
+ })
+ })
+
+ return obstacles
}
export const initializeRandomObstacle = (
@@ -42,7 +171,13 @@ export const initializeRandomObstacle = (
while (obstacles.length < numberOfObstacles) {
const { x, y } = getRandomPosition(800, 600)
- const obstacle: Obstacle = { x, y, width: 50, height: 50 }
+ const obstacle: Obstacle = createObstacle(
+ Types.Wall,
+ x,
+ y,
+ OBSTACLE_SIZE,
+ OBSTACLE_SIZE
+ )[0]
// Проверяем, нет ли коллизий с существующими препятствиями
const hasCollision = obstacles.some(existingObstacle =>
@@ -67,6 +202,10 @@ export const handleBulletObstacleCollisions = (
if (detectCollision(bullet, obstacle)) {
// Логика для уничтожения пули, если она попала в препятствие
bullets.splice(bullets.indexOf(bullet), 1) // Пример, удаляем пулю, если попала в препятствие
+ createBangEffect(
+ bullet.x + bullet.width / 2,
+ bullet.y + bullet.height / 2
+ )
killObstacle(obstacles, obstacle)
}
})
@@ -74,5 +213,20 @@ export const handleBulletObstacleCollisions = (
}
const killObstacle = (obstacles: Obstacle[], obstacle: Obstacle) => {
- obstacles.splice(obstacles.indexOf(obstacle), 1)
+ const settings = ObstacleSettings[obstacle.type as Types]
+ const { frames } = settings
+
+ obstacle.hp = obstacle.hp - 1
+
+ const currentFrame = frames.find(
+ (frame: { hp: number; index: number }) => obstacle.hp <= frame.hp
+ )
+
+ if (currentFrame) {
+ obstacle.animation.currentFrame = currentFrame.index
+ }
+
+ if (obstacle.hp === 0) {
+ obstacles.splice(obstacles.indexOf(obstacle), 1)
+ }
}
diff --git a/packages/client/src/components/Game/player.tsx b/packages/client/src/components/Game/player.tsx
index 92a46dc..c432ff7 100644
--- a/packages/client/src/components/Game/player.tsx
+++ b/packages/client/src/components/Game/player.tsx
@@ -6,7 +6,7 @@ export const PLAYER_DEFAULT_PARAMS = {
y: 560,
width: 70,
height: 70,
- speed: 2,
+ speed: 3,
direction: { x: 0, y: 0 },
animation: {
currentFrame: 0, // Текущий кадр спрайта
diff --git a/packages/client/src/components/Game/utils.tsx b/packages/client/src/components/Game/utils.tsx
index 64c25cb..13284bb 100644
--- a/packages/client/src/components/Game/utils.tsx
+++ b/packages/client/src/components/Game/utils.tsx
@@ -1,8 +1,18 @@
import enemiesSpritePath from '@/assets/images/sprites/enemy.png'
import playerSpritePath from '@/assets/images/sprites/tank.png'
import bulletSpritePath from '@/assets/images/sprites/bullet.png'
-import wallSpritePath from '@/assets/images/sprites/wall.svg'
-import { AbstractEntity, Enemy, Obstacle } from '@/components/Game/gameTypes'
+import wallSpritePath from '@/assets/images/sprites/wall.png'
+import steelSpritePath from '@/assets/images/sprites/steel.png'
+import treeSpritePath from '@/assets/images/sprites/tree.png'
+import bangSpritePath from '@/assets/images/sprites/bang.png'
+import shotSpritePath from '@/assets/images/sprites/shot.png'
+import {
+ AbstractEntity,
+ Effect,
+ Enemy,
+ Obstacle,
+} from '@/components/Game/gameTypes'
+import { deleteEffect } from './effects'
export const getRandomEdgePosition = (
canvasWidth: number,
@@ -53,15 +63,17 @@ const darawTank = (
}
// Если смещение кратно 10 меняем кадр
- if (moovment % animation.frameInterval === 0) {
+ if (animation?.frameInterval && moovment % animation.frameInterval === 0) {
animation.currentFrame =
(animation.currentFrame + 1) % animation?.totalFrames
}
- spriteSettings.width = sprite.width / animation.totalFrames
- spriteSettings.height = sprite.height
- spriteSettings.sourceX = animation.currentFrame * spriteSettings.width
- spriteSettings.sourceY = 0
+ if (animation) {
+ spriteSettings.width = sprite.width / animation.totalFrames
+ spriteSettings.height = sprite.height
+ spriteSettings.sourceX = animation.currentFrame * spriteSettings.width
+ spriteSettings.sourceY = 0
+ }
context.save()
context.translate(data.x + data.width / 2, data.y + data.height / 2)
@@ -96,8 +108,6 @@ const enemiesSprite = new Image()
enemiesSprite.src = enemiesSpritePath
-const lastEnemyDirection: Record = {}
-
export const drawEnemies = (
context: CanvasRenderingContext2D,
enemies: Enemy[]
@@ -108,29 +118,53 @@ export const drawEnemies = (
}
const wallSprite = new Image()
+const steelSprite = new Image()
+const treeSprite = new Image()
+
wallSprite.src = wallSpritePath
+steelSprite.src = steelSpritePath
+treeSprite.src = treeSpritePath
export const drawObstacles = (
context: CanvasRenderingContext2D,
obstacles: Obstacle[]
) => {
- const SPRITE_SIZE = 50
-
obstacles.forEach(obstacle => {
- const horizontalCount = Math.ceil(obstacle.width / SPRITE_SIZE)
- const verticalCount = Math.ceil(obstacle.height / SPRITE_SIZE)
+ let sprite: HTMLImageElement
+
+ switch (obstacle.type) {
+ case 'steel':
+ sprite = steelSprite
+
+ break
+ case 'wall':
+ sprite = wallSprite
+
+ break
+ case 'tree':
+ sprite = treeSprite
+
+ break
+
+ default:
+ sprite = treeSprite
- Array.from({ length: horizontalCount }).forEach((_, i) => {
- Array.from({ length: verticalCount }).forEach((_, j) => {
- const x = obstacle.x + i * SPRITE_SIZE
- const y = obstacle.y + j * SPRITE_SIZE
+ break
+ }
- const width = Math.min(SPRITE_SIZE, obstacle.width - i * SPRITE_SIZE)
- const height = Math.min(SPRITE_SIZE, obstacle.height - j * SPRITE_SIZE)
+ const spriteSize = sprite.width / obstacle.animation.totalFrames
- context.drawImage(wallSprite, 0, 0, width, height, x, y, width, height)
- })
- })
+ context.drawImage(
+ sprite,
+ obstacle.animation.currentFrame * spriteSize,
+ 0,
+ spriteSize,
+ spriteSize,
+ obstacle.x,
+ obstacle.y,
+ spriteSize,
+ spriteSize
+ )
})
}
@@ -162,3 +196,83 @@ export const drawBullets = (
context.restore()
})
}
+
+const bangSprite = new Image()
+const shotSprite = new Image()
+
+bangSprite.src = bangSpritePath
+shotSprite.src = shotSpritePath
+
+export const drawEffects = (
+ context: CanvasRenderingContext2D,
+ effects: Effect[]
+) => {
+ effects.forEach(effect => {
+ const spriteSettings = {
+ width: 0,
+ height: 0,
+ sourceX: 0,
+ sourceY: 0,
+ }
+ let sprite: HTMLImageElement
+
+ if (typeof effect.animation.frameCount === 'number') {
+ effect.animation.frameCount++
+
+ if (
+ effect.animation?.frameInterval &&
+ effect.animation.frameCount % effect.animation.frameInterval === 0
+ ) {
+ effect.animation.currentFrame = effect.animation.currentFrame + 1
+
+ if (
+ effect.animation.currentFrame ===
+ effect.animation.totalFrames - 1
+ ) {
+ deleteEffect(effect)
+ }
+ }
+ }
+
+ switch (effect.type) {
+ case 'shot':
+ sprite = shotSprite
+
+ break
+
+ case 'bang':
+ sprite = bangSprite
+
+ break
+
+ default:
+ sprite = bangSprite
+ }
+
+ spriteSettings.width = sprite.width / effect.animation.totalFrames
+ spriteSettings.height = sprite.height
+ spriteSettings.sourceX =
+ effect.animation.currentFrame * spriteSettings.width
+ spriteSettings.sourceY = 0
+
+ context.save()
+ context.translate(effect.x + effect.width / 2, effect.y + effect.height / 2)
+
+ if (effect.direction)
+ context.rotate(Math.atan2(effect.direction.x, -effect.direction.y))
+
+ context.drawImage(
+ sprite,
+ spriteSettings.sourceX,
+ spriteSettings.sourceY,
+ spriteSettings.width,
+ spriteSettings.height,
+ -effect.width / 2,
+ -effect.height / 2,
+ effect.width,
+ effect.height
+ )
+
+ context.restore()
+ })
+}