Загрузка изображений в одну функцию - PullRequest
1 голос
/ 23 октября 2019

Я пытаюсь выучить JavaScript, делая свою первую игру. Как я могу сделать все изображения onload в одном function, а затем нарисовать его на холсте, чтобы мой код был короче?

Как я могу поместить много изображений в массив, а затем использовать его в функции.

Это мой третий день изучения JavaScript.

Заранее спасибо.

var cvs = document.getElementById('canvas');
var ctx = cvs.getContext('2d');

//load images

var bird = new Image();
var bg = new Image();
var fg = new Image();
var pipeNorth = new Image();
var pipeSouth = new Image();

//images directions 
bg.src = "assets/bg.png";
bird.src = "assets/bird.png";
fg.src = "assets/fg.png";
pipeNorth.src = "assets/pipeNorth.png";
pipeSouth.src = "assets/pipeSouth.png";

var heightnum = 80;
var myHeight = pipeSouth.height+heightnum;
var bX = 10;
var bY = 150;
var gravity = 0.5;

// Key Control :D
 document.addEventListener("keydown",moveUP)
function moveUP(){
bY -= 20;
}
//pipe coordinates

var pipe = [];

pipe[0] = {
  x : cvs.width,
  y : 0
}
//draw images 


//Background img
  bg.onload = function back(){
    ctx.drawImage(bg,0,0);

  }
  //pipe north
  pipeNorth.onload = function tubo(){

    for(var i = 0; i < pipe.length; i++){

    ctx.drawImage(pipeNorth,pipe[i].x,pipe[i].y);
    pipe[i].x--;
    }
  }

  pipeSouth.onload = function tuba(){
    ctx.drawImage(pipeSouth,pipe[i].x,pipe[i].y+myHeight);

  }



  bird.onload = function pajaro(){
    ctx.drawImage(bird,bX,bY);
    bY += gravity;

    requestAnimationFrame(pajaro);  
  } 


  fg.onload = function flor(){
    ctx.drawImage(fg,0,cvs.height - fg.height);

  }




moveUP();
   back();
   tuba();
   pajaro();
   flor();

1 Ответ

0 голосов
/ 23 октября 2019

Это можно сделать с помощью Promise.all. Мы дадим новое обещание для каждого изображения, которое мы хотим загрузить, разрешая при вызове onload. Как только Promise.all разрешится, мы можем вызвать нашу функцию initialize и продолжить с нашей логикой. Это позволяет избежать условий гонки, когда основной игровой цикл requestAnimationFrame вызывается из bird.onload, но возможно, что конвейерные сущности и т. Д. Еще не загружены.

Вот минимальный полный пример:

const imageUrls = [
  "http://placekitten.com/90/100",
  "http://placekitten.com/90/130",
  "http://placekitten.com/90/160",      
  "http://placekitten.com/90/190",
];

const initialize = images => {

  // images are loaded here and we can go about our business
  
  const canvas = document.createElement("canvas");
  document.body.appendChild(canvas);
  canvas.width = 400;
  canvas.height = 200;
  const ctx = canvas.getContext("2d");

  Object.values(images).forEach((e, i) =>
    ctx.drawImage(e, i * 100, 0)
  );
};

Promise.all(imageUrls.map(e =>
  new Promise((resolve, reject) => {
    const img = new Image();
    img.src = e;
    img.onload = () => resolve(img);
    img.onerror = reject;
  })
)).then(initialize);

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

var foo = ...
var bar = ...
var baz = ...
var qux = ...
foo.src = ...
bar.src = ...
baz.src = ...
qux.src = ...
foo.onload = ...
bar.onload = ...
baz.onload = ...
qux.onload = ...

чрезвычайно сложен в управлении и масштабировании. Если вы решите добавить еще одну вещь в игру, то для ее учета необходимо переписать код, и игровая логика станет очень мокрой . Ошибки становятся трудно обнаружить и устранить. Кроме того, если нам нужно конкретное изображение, мы бы предпочли обращаться к нему, например, images.bird, а не images[1], сохраняя семантику отдельных переменных, но давая нам возможность циклически проходить через объект и вызывать * 1025 каждой сущности. * функция, например.

Все это мотивирует объект для агрегирования игровых объектов. Некоторая информация, которую мы хотели бы иметь для каждой сущности, могла бы включать, например, текущую позицию сущности, статус «мертв / жив», функции для перемещения и рендеринга и т. Д.

Это также хорошая идея иметь какую-тоотдельного объекта необработанных данных, который содержит все начальное игровое состояние (обычно это будет внешний файл JSON).

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

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

const entityData = [
  {
    name: "foo", 
    path: "http://placekitten.com/80/80",
    x: 0,
    y: 0
  },
  {
    name: "baz", 
    path: "http://placekitten.com/80/150",
    x: 0,
    y: 90
  },
  {
    name: "quux", 
    path: "http://placekitten.com/100/130",
    x: 90,
    y: 110
  },
  {
    name: "corge", 
    path: "http://placekitten.com/200/240",
    x: 200,
    y: 0
  },
  {
    name: "bar",
    path: "http://placekitten.com/100/100",
    x: 90,
    y: 0
  }
  /* you can add more properties and functions 
     (movement, etc) to each entity
     ... try adding more entities ...
  */
];

const entities = entityData.reduce((a, e) => {
  const img = new Image();
  img.src = e.path;
  a[e.name] = {...e, image: img};
  return a;
}, {});

const initialize = () => {
  const canvas = document.createElement("canvas");
  document.body.appendChild(canvas);
  canvas.width = innerWidth;
  canvas.height = innerHeight;
  const ctx = canvas.getContext("2d");

  for (const key of Object.keys(entities)) {
    entities[key].alpha = Math.random();
  }
  
  (function render () {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
  
    Object.values(entities).forEach(e => {
      ctx.globalAlpha = Math.abs(Math.sin(e.alpha += 0.005));
      ctx.drawImage(e.image, e.x, e.y);
      ctx.globalAlpha = 1;
    });
    
    requestAnimationFrame(render);
  })();
};

Promise.all(Object.values(entities).map(e =>
  new Promise((resolve, reject) => {
    e.image.onload = resolve;
    e.image.onerror = reject;
  })
)).then(initialize);
...