Как сделать камеру слежения за игроком и перемещаться по карте, не теряя игрока из поля зрения?

Здраствуйте, у меня проблема с непониманием того как в canvas сделать камеру слежения за игроком, и как перемещаться по карте не теряя игрока с поля зрения.

Вот код моего кастомного хука для получения позиции:


export const usePosition = (speed: number) => {
  const [keysPressed, setKeysPressed] = useState<{ [key: string]: boolean }>({})
  const [x, setX] = useState<number>(0)
  const [y, setY] = useState<number>(0)

  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      setKeysPressed((prevState) => ({
        ...prevState,
        [e.key]: true,
      }))
    }

    const handleKeyUp = (e: KeyboardEvent) => {
      setKeysPressed((prevState) => ({
        ...prevState,
        [e.key]: false,
      }))
    }

    window.addEventListener("keydown", handleKeyDown)
    window.addEventListener("keyup", handleKeyUp)

    return () => {
      window.removeEventListener("keydown", handleKeyDown)
      window.removeEventListener("keyup", handleKeyUp)
    }
  }, [])

  useEffect(() => {
    const moveCharacter = () => {
      let newX = x
      let newY = y

      if (keysPressed["ArrowUp"] || keysPressed["w"]) newY -= speed
      if (keysPressed["ArrowDown"] || keysPressed["s"]) newY += speed
      if (keysPressed["ArrowLeft"] || keysPressed["a"]) newX -= speed
      if (keysPressed["ArrowRight"] || keysPressed["d"]) newX += speed

      setX(newX)
      setY(newY)
    }

    const animationFrame = requestAnimationFrame(moveCharacter)

    return () => cancelAnimationFrame(animationFrame)
  }, [keysPressed, x, y])

  return { x, y }
}


А вот код компонента игры:

"use client"

import { useEffect, useRef } from "react"
import { usePosition } from "@/hooks/usePosition"
import { socket } from "@/components/Socket"
import { userId } from "@/components/userId"
import { map } from "@/components/map"

socket.emit("new_player", { userId })

const Game = () => {
  const { x, y } = usePosition(3)
  const canvasRef = useRef<HTMLCanvasElement>(null)

  socket.emit("move", { x, y })

  useEffect(() => {
    const ctx = canvasRef.current?.getContext("2d")

    if (ctx) {
      socket.on("state", (players: Player[]) => {
        ctx.clearRect(
          0,
          0,
          canvasRef.current?.width || 0,
          canvasRef.current?.height || 0
        )

        map(ctx, 32)

        for (let id in players) {
          const currentPlayer: Player = players[id]

          const color = () => {
            if (userId == currentPlayer.userId) {
              return "red"
            } else {
              return "#30363d"
            }
          }
          ctx.fillStyle = color()
          ctx.beginPath()
          ctx.arc(
            currentPlayer.positionX,
            currentPlayer.positionY,
            25,
            0,
            Math.PI * 2
          )
          ctx.fill()
        }
      })
    }

    return () => {
      socket.off("state")
    }
  }, [socket])

  return (
    <main>
      <canvas ref={canvasRef} width={700} height={700}></canvas>
      <div>
        <div>
          <div>x: {x}</div>
          <div>y: {y}</div>
        </div>
      </div>
    </main>
  )
}

export default Game

Код компонента по создания карты:

  const tilemap: number[][] = [
    [1, 2, 0, 1, 2, 0, 1, 2, 0],
    [2, 2, 0, 2, 2, 0, 1, 2, 0],
    [1, 2, 0, 1, 2, 0, 1, 2, 0],
    [0, 0, 1, 0, 0, 1, 0, 0, 1],
    [0, 0, 1, 0, 0, 1, 0, 0, 1],
    [1, 0, 1, 0, 0, 1, 0, 0, 1],
    [0, 0, 2, 0, 0, 2, 0, 0, 2],
    [1, 0, 2, 0, 0, 0, 0, 0, 1],
    [0, 0, 1, 0, 0, 2, 0, 0, 2],
  ]

  for (let i = 0; i < tilemap.length; i++) {
    for (let j = 0; j < tilemap[i].length; j++) {
      const tile = tilemap[i][j]

      const tileX = j * tileSize
      const tileY = i * tileSize

      if (tile === 1) {
        ctx.fillStyle = "green"
      } else if (tile === 2) {
        ctx.fillStyle = "blue"
      } else {
        ctx.fillStyle = "white"
      }

      ctx.fillRect(tileX, tileY, tileSize, tileSize)
    }
  }
}

Ответы (0 шт):