Если вы еще не пересекались с P5.js, вы должны знать, что это самый простой способ рисования в Интернете и в то же время один из самых мощных. P5.js вдохновлен библиотекой Java под названием Processing. Но в то время как Processing многословен и сложен для поиска хороших примеров, в P5 есть много отличных примеров и практически нет шаблонов.

В этом примере мы собираемся построить простой и маленький Pong. Pong — это игра atari, в которой 2 игрока пытаются забить гол на другой стороне времени.

Это будет просто, потому что это будет первый подход к P5, и он будет маленьким, потому что чем меньше, тем симпатичнее.



Самый простой способ начать играть с P5 — использовать онлайн-редактор P5 https://editor.p5js.org/. Вы также можете использовать его, загрузив в заголовок HTML-файла. Код, который мы собираемся использовать, можно найти здесь https://editor.p5js.org/davidalencia/sketches/hmX4-A8lw.

настроить и нарисовать

P5 загружает множество служебных функций, упрощающих работу с анимацией. Две основные функции — setup, которая будет запускаться один раз при старте, и draw, которая будет вызываться каждый кадр.

Мы собираемся начать с создания панели Pong.

setup(){
  createCanvas(80, 80);
}
draw(){
  background(0);  
  rect(10, 40, 4, 14);
}

При настройке главное, что мы должны сделать, это создать холст, на котором мы будем рисовать. Вызвав createCanvas, мы можем определить размер, в данном случае 80 на 80 пикселей.

В Draw мы определяем цвет фона как черный и создаем прямоугольник в точках (10, 40) шириной 4 и высотой 14. Важно отметить, что если мы вызовем background в конце, он закрасит все, что было. до.

Использование классов более удобно для анимации, поэтому мы собираемся использовать их.

Бары

У нас уже есть бар, так что давайте начнем с того, что сделаем его классом. Наш класс должен принимать координату x, чтобы мы могли использовать ее для левой и правой панели и отслеживать точки. Он также должен иметь метод рисования, чтобы мы могли обрабатывать анимацию.

class Bar {
  constructor(x){
    this.x = x;
    this.y = 30;
    this.height = 14;
    this.width = 4;
    this.points = 0;
  }
  draw(){
    rect(this.x, this.y, this.width, this.height);
  }
}

Теперь мы можем создать две полоски, необходимые для игры. Здесь мы переписываем функцию рисования.

let left = new Bar(2);
let right = new Bar(76);
draw(){
  background(0);
  left.draw()
  right.draw()
}

Вы должны увидеть что-то вроде этого. Но вряд ли это бары Понга, они должны как минимум двигаться вверх-вниз. Мы изменим класс Bar, чтобы он имел метод up и down. Оба метода должны проверять ребра и не перемещаться, если они находятся рядом с одним.

Если вы уже что-то анимировали или немного поиграли с кодом, вы можете заметить, что ось «y» идет сверху вниз, а не снизу вверх, как можно подумать. Итак, чтобы сдвинуть планку вниз, мы должны увеличить «y», а чтобы сдвинуть ее вверх, мы должны уменьшить ее.

class Bar {
  .
  .
  .
  up(){
    this.y = (this.y < 80-this.height)? this.y-1: this.y
  }
  down(){
    this.y = (this.y > 0)? this.y+1: this.y
  }
}

Это не двигает планку само по себе. К счастью, в P5 есть функция, которая будет вызываться каждый раз, когда происходит событие клавиатуры, и переменная, принимающая значение последней нажатой клавиши.

Теперь мы будем перемещать левую полосу вверх с помощью w и вниз с помощью s, коды которых равны 87 и 83 соответственно. А для правой панели мы будем использовать стрелки вверх и вниз, в P5 есть константы для этих клавиш. Если вам интересно, вы можете просмотреть их все здесь https://p5js.org/es/reference/#/p5/keyPressed.

У нас уже есть экземпляры панели, поэтому нам просто нужно прослушивать нажатия клавиш и перемещать их.

function keyPressed(){
    if(p.keyCode==87)
      left.up()
    else if(p.keyCode==83)
      left.down()
    else if(p.keyCode==p.UP_ARROW)
      right.up()
    else if(p.keyCode==p.DOWN_ARROW)
      right.down()
  }

Мяч

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

class Ball{
  constructor(){
    this.x = 40
    this.y = 40
    this.xs =(Math.random()-0.5)
    this.ys = (Math.random()-0.5)
  }
}

Как и в нашем классе Bar, у класса ball также должен быть метод draw. Но мяч движется сам по себе, поэтому мы должны обновлять позицию каждый раз, когда рисуем мяч. И если мяч движется сам по себе, мы должны проверить края. В понге, когда мяч касается верхнего или нижнего края, он отскакивает, а это означает, что ускорение по оси «у» умножается на -1.

Ни с того ни с сего я решил сделать шар квадратом 2 на 2. Преимущество в том, что мы уже знаем, как рисовать прямоугольник, а квадрат — это разновидность прямоугольника.

draw(){
    //drawing the square
    rect(this.x, this.y, 2, 2)
    //updating the position
    this.y *=this.ys
    this.x +=this.xs
    // looking for edges 
    if(this.y<=0 || this.y>=80-2){
      this.ys = this.ys *(-1)
      this.y += this.ys
    }
}

Хороший. У нас есть класс мяча, теперь нам нужен экземпляр и его нужно нарисовать.

let ball = new Ball()
function draw(){
  .
  .
  .
  ball.draw()
}

У вас должно получиться что-то вроде этого ниже.

Взаимодействие мяча и брусьев

Итак, теперь у нас есть движущийся мяч и два играбельных бара. Но мяч проходит через решетку. Если мяч находится перед перекладиной, то мы должны оттолкнуть мяч от оси «х». И если мяч проходит за перекладиной, не касаясь ее, то другая перекладина получает очко, и мы создаем новый мяч.

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

Итак, давайте проверим эти условия в розыгрыше. Если координата x мяча меньше, чем у левой полосы, или больше, чем сумма координаты x и ширины правой полосы, мы увеличиваем соответствующий счет и создаем новый мяч.

function draw(){
  .
  .
  .
  if(ball.x<left.x){
      right.points++;
      ball = new Ball()
  }
  else if(ball.x>right.x+right.width){
    left.points++;
    ball = new Ball();
 }

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

function draw(){
  const inScope = (ball, bar)=>ball.y>bar.y &&
                             ball.y<bar.y+bar.height;
  .
  .
  .   
  else if(inScope(ball, left) && 
            ball.x < left.x+left.width){
      ball.x = left.x+left.width
      ball.xs *= -1
  }
  else if(inScope(ball, right) && 
            ball.x > right.x-2){
      ball.x = this.right.x-2
      ball.xs *= -1
  }
}

Последние штрихи

Осталось только табло. P5, будучи одним из самых простых доступных фреймворков, позволяет нам сделать это одной строкой, передав сначала сообщение, а затем нужные нам координаты.

function draw(){
  .
  .
  .
  text(`${left.points} : ${right.points}`, 40, 10)
}

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

function draw(){
  .
  .
  .    
  textAlign(p.CENTER);
  fill(255, 255, 255);
  text(`${left.points} : ${right.points}`, 40, 10)
}

У вас должно получиться что-то похожее на изображение, показанное ниже.

Поздравляем, у вас наконец-то есть хорошая игра Pong, закодированная в P5!

Помните, что весь код находится по следующей ссылке: https://editor.p5js.org/davidalencia/sketches/hmX4-A8lw