Как использовать функцию JS ctx.translate для блокировки проигрывателя в центре экрана?

#javascript #canvas #html5-canvas

Вопрос:

У меня есть следующая функция JS для отображения «игрока» в центре окна просмотра.

 //state fires every 60ms 
socket.on('state', (gameState) => {
//iterates through gameState players
    for (let player in gameState.players) {
//draws each player
        renderEnemies(gameState.players[player]);
    }
});

function renderEnemies(player) {
    const ctx = document.getElementById('gameboard').getContext('2d');
    const canvas = document.getElementById('gameboard');
//align player with center of viewport (BROKEN)
    ctx.translate(canvas.width/2- player.x, canvas.height/2-player.y);
//colour player red
    ctx.fillStyle = "#FF0000";
//add the player to the canvas (gameboard)
    ctx.fillRect(player.x, player.y, player.width, player.width); 
}
 

Однако часть перевода функции renderEnemies не работает. Я уже экспериментировал с ctx.clear() и restore, но безрезультатно.

Я был бы признателен, если бы кто-нибудь указал мне правильное направление. Спасибо!

ОБНОВЛЕНИЕ: Я исправил ошибку окна просмотра с помощью приведенного ниже кода, но теперь движение игрока нарушено. Функция рисования теперь выглядит следующим образом:

 function renderEnemies(player) {
    const ctx = document.getElementById('gameboard').getContext('2d');
    const canvas = document.getElementById('gameboard');

    ctx.save();
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.translate(window.innerWidth/2- player.x, window.innerHeight/2-player.y);
    ctx.fillStyle = "#FF0000";
    ctx.fillRect(player.x, player.y, player.width, player.width); 
    ctx.restore();
}
 

Ответ №1:

Это может быть связано с тем, что вы не сохраняете и не восстанавливаете или, по крайней мере, не переводите обратно после рисования. Как вы можете видеть в приведенном ниже фрагменте, если я переведу, не выполнив ни того, ни другого, враг будет переведен неправильно (в этом примере с холста).

Когда вы отмените комментарий, сохраните И восстановите его правильные позиции. Вы также можете просто перевести его обратно, добавив отрицательные значения после рисования. Поэтому в приведенном ниже фрагменте вы можете закомментировать save() reastore() и раскомментировать второе translate , и вы получите те же результаты.

 let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
canvas.width = 300;
canvas.height = 300;
ctx.fillStyle = "lightgrey";
ctx.fillRect(0, 0, canvas.width, canvas.height);
class Enemy {
  constructor(x, y) {
    this.x = x;
    this.y = y;
    this.w = 20;
    this.h = 20;
    this.c = "blue";
  }
  draw() {
    ctx.save();
    ctx.translate(canvas.width / 2 - this.w/2, canvas.height / 2 - this.h/2);
    //colour player red
    ctx.fillStyle = "#FF0000";
    //add the player to the canvas (gameboard)
    ctx.fillRect(this.x, this.y, this.w, this.h);
    ctx.restore();
    //ctx.translate(-(canvas.width / 2 - this.w/2), -(canvas.height / 2 - this.h/2)); //or use this
  }
}
let enemy1 = new Enemy(0, 0);

function animate() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.fillStyle = "grey";
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  enemy1.draw();
  requestAnimationFrame(animate);
}
animate(); 
 <canvas id="canvas"></canvas> 

Без всего вашего кода я не знаю, что еще это может быть.

Обновить:

Знание основ того, что translate() вы делаете, поможет вам понять, что происходит, когда вы что-то переводите. Не просто думайте о переводе, когда холст перемещает этот объект, но думайте об этом так, как будто этот объект находится на собственном листе холста, и весь этот лист перемещается. Вот почему, если вы поворачиваете элемент, он поворачивается не от центра, а в (0,0) положении холста. Подумайте об этом таким образом: когда вы вводите значения в translate , вы указываете ему, где вы хотите, чтобы левый верхний угол этого листа холста был для этого конкретного элемента.

введите описание изображения здесь

На изображении выше сплошной черный цвет-это холст. Если я переведу красный квадрат ctx.translate(canvas.width/2, canvas.height/2) , то вы увидите, что на самом деле я перевожу лист холста, изображенный серой каймой. Верхний левый угол этого холста центрирован на холсте, но поскольку квадрат нарисован на (0,0), будет казаться, что квадрат не центрирован, но немного вправо и вниз. Кроме того, если бы я дал объекту красного квадрата координату x (холст.ширина/2, 0), вы могли бы увидеть, что произойдет, изображенное квадратом красной границы.

Теперь на следующем изображении, если бы я действительно хотел центрировать красный квадрат, мне нужно было бы учитывать половину ширины и высоты. Я могу сделать это в двух местах. Если я вычитаю его в методе перевода ctx.translate(canvas.width/2 - block.width/2, canvas.height/2 - block.height/2) , он будет центрироваться, но это также удерживает квадраты (0,0) в соответствии с холстом (0,0). Это нормально до тех пор, пока вы не захотите повернуть красный квадрат из центра.

введите описание изображения здесь

На рисунке выше я установил вычитание координат красных квадратов вместо (0 - this.width/2, 0 - this.height/2) этого, это помещает (0,0) холста в центр красного квадрата. Поскольку вращение происходит от (0,0) полотна, этот красный квадрат, если его повернуть, будет вращаться от центра.

Учитывая все сказанное, если вы применяете координаты x и y к своему объекту и пытаетесь перевести одинаковое количество, у вас будут ошибки, такие как первый красный контур изображения. Другими словами, не делайте этого (если вы этого не хотите).

ctx.translate(100, 100)
ctx.fillRect(100, 100, 50, 50)

это переведет его на 100 в каждую сторону, а затем объект будет иметь дополнительные 100 в каждом направлении. Кроме того, вам, вероятно, не нужно использовать clearRect внутри функции сохранения(). Обычно это вызывается при обновлении в начале цикла анимации, чтобы очистить холст перед перерисовкой.

Комментарии:

1. Спасибо! Я вроде как исправил это с помощью window.innerWidth и высоты, но теперь возникли проблемы с движением. Я знаю, что прошу многого, но не могли бы вы взглянуть на мое редактирование вопроса с обновленным кодом и попытаться понять, почему движение не работает? Я бы предположил, что это как-то связано с моими ctx.save() и ctx.restore()

2. Я обновил свой комментарий, чтобы попытаться внести некоторую ясность translate . Кроме того, если это нарушило ваше движение, мне интересно, настроено ли у вас обнаружение столкновений на холсте, и, возможно, поскольку переводы неверны, оно все время сталкивается.