Как заставить контекст холста следовать за игроком?

Я создаю игру с использованием JavaScript и холстов и хочу реализовать область просмотра, чтобы отображать более крупный игровой мир. Я уже создал элемент холста и загрузил несколько стационарных объектов и объект игрока, который можно перемещать с помощью ввода с клавиатуры.

Однако я не уверен, как реализовать окно просмотра, чтобы отображать только часть игрового мира и позволять игроку перемещаться по нему. Может ли кто-нибудь привести пример того, как реализовать окно просмотра с помощью JavaScript и холстов на основе предоставленного кода?

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

Вот мой текущий код. Обратите внимание, я очень новичок в js:

    // Get the canvas element and its context
    const canvas = document.createElement("canvas");
    document.body.append(canvas);
    const ctx = canvas.getContext("2d");

    // Player coords and update method that moves the player based on input
    const player = {
      x: 0,
      y: 0,
      vel: 1,
      update(){
        if (key.left) this.x -= this.vel;
        if (key.right) this.x += this.vel;
        if (key.up) this.y -= this.vel;
        if (key.down) this.y += this.vel;
      }
    };





    // Keys pressed (Used for movement)
    const key = {
      left: false,
      right: false,
      up: false,
      down: false
    }
    
    
    // Get Input
    window.addEventListener('keydown', (e) =>
    {
      switch(e.code){
        case "KeyW":
          key["up"] = true;
          break;
        case "KeyS":
          key["down"] = true;
          break;
        case "KeyA":
          key["left"] = true;
          break;
        case "KeyD":
          key["right"] = true;
          break;
      }
    });

    // Relieve Input
    window.addEventListener('keyup', (e) =>
    {
      switch(e.code){
        case "KeyW":
          key["up"] = false;
          break;
        case "KeyS":
          key["down"] = false;
          break;
        case "KeyA":
          key["left"] = false;
          break;
        case "KeyD":
          key["right"] = false;
          break;
      }
    });

    function update(){
      ctx.clearRect(0,0,canvas.width, canvas.height);
      // Draw stationary objects
      ctx.fillRect(100, 100, 10, 10);
      ctx.fillRect(100, 120, 10, 10);

      // Draw Player and update position
      player.update();
      ctx.fillRect(player.x, player.y, 10, 10);
      requestAnimationFrame(update);
    }
    update();

Я попытался сохранить контекст и перевести, а затем восстановить. Однако это не сработало, я ожидал, что игрок останется в центре экрана. Вот код, который я пробовал:

    function update(){
      ctx.clearRect(0,0,canvas.width, canvas.height);
      // Draw stationary objects
      ctx.fillRect(100, 100, 10, 10);
      ctx.fillRect(100, 120, 10, 10);

      // Draw Player and update position
      ctx.save();
      ctx.translate(player.x, player.y);
      player.update();
      ctx.fillRect(canvas.width/2 - 5, canvas.height/2 - 5, 10, 10);
      ctx.restore();
      requestAnimationFrame(update);
    }
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
0
55
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Большинство реализаций 2D-сцен в играх работают с родительским/дочерним деревом, где сцена является верхним родителем (родительское положение объекта просто делает его зависимым от положения другого объекта).

Получив это, вы можете легко перемещать область просмотра, добавляя каждый объект к одному и тому же родителю (например, сцене), содержащему все ваши объекты, и перемещая этот родитель для имитации камеры.

Вот некоторый псевдокод, чтобы получить представление о такой системе.

//Pseudo code, don't simply copy-paste it
// Make a class from which all the objects in the game will inherit; it'll take care of managing the position.

class GameObject {
    position: {x: 0, y: 0}
    parent: null;
    children: [];
    function setPosition(pX, pY) { //Sets the position within the parent
        position.x = pX;
        position.y = pY;
    }
    //This is the core of our parent/child tree
    function addChild(pGameObject) {
        children.push(pGameObject);
        pGameObject.parent = this;
    }
    function removeChild() //take care of updating the parent/children vars

    function toGlobalCoordinates() { //Allows us to get the global coordinates (= coordinates on the canvas)
       let lParent = this.parent;
       let globalPos = {x: 0, y: 0}
       //Goes up the parents tree, breaks when there's no more parent thanks to the default: parent = null in the class
       while(lParent) {
           globalPos.x += lParent.position.x;
           globalPos.y += lParent.position.y;
           lParent = lParent.parent;
       }
       globalPos.x += this.position.x;
       globalPos.y += this.position.y;
       return globalPos;
    }
 
    function draw(pCtx) { //We pass the canvas ctx to be able to draw on it
        //Use the global position to draw your object
        let drawingPos = this.toGlobalCoordinates();
        pCtx.fillRect(drawingPos.x, drawingPos.y, 100, 100);
    }
}

Затем у нас есть другие классы, такие как Player, и мы должны позаботиться о рендеринге.

//extends from GameObject to benefit from the position and its methods
class Player extends GameObject {
    methods() ... //Implement methods specific to the player
}

//Use the scene as the base gameobject (our top parent)
let scene  = new GameObject();
let player = new Player();
let stationnaryObject1 = new GameObject()

scene.addChild(player)
scene.addChild(stationnaryObject1)

//The function called on requestAnimationFrame
update() {
   //clearRect to clean the canvas then

   //On key press, move the player, move the scene in the opposite direction to keep the keep focus on the player
   if (keypress) {
     player.setPosition(player.position.x + newX, player.position.x + newY)
     scene.setPosition(scene.position.x - newX, scene.position.x - newY)
   }
   
   //Draw the objects with their new position
   scene.draw(ctx);
   player.draw(ctx);
}

Это более или менее базовый способ сделать камеру (т.е. переместить область просмотра в игровом мире).

Вам следует взглянуть на PixiJS, если вы ищете очень надежный способ справиться с этим и многим другим. Это очень мощная и простая в освоении JS-библиотека для обработки 2D-отображения на WebGL и холсте (больше не нужно рисовать самостоятельно!)

Чтобы узнать больше о графе/дереве сцены, о котором я говорил: https://pixijs.io/guides/basics/scene-graph.html такое же дерево использовалось, например, во Flash-играх.

TLDR: Создание камеры — это изменение позиции, к которой привязаны позиции всех объектов.

Надеюсь, вы получите удовольствие от разработки игр!

Спасибо! Это кажется очень похожим на то, как работает дерево объектов игрового движка Unity, я обязательно посмотрю на Pixi Js.

VkQuads 31.03.2023 22:28

Другие вопросы по теме