Следуя за туритарием с тем же самым точным кодом, но получая эту ошибку на консоли при попытке нарисовать изображение на холсте:
SpriteSheet.js: 30 Неподготовлено (в обещании) TypeError: Не удалось выполнить 'drawImage' в 'CanvasRenderingContext2D': предоставленное значение не относится к типу '(CSSImageValue или HTMLImageElement или SVGImageElement или HTMLVideoElement или HTMLCanvasElement или ImageBitmap или OffscreenCanvas)
Не знаю, почему это происходит? Первое изображение хорошо нарисовано на холсте, но второе не появляется и не получает эту ошибку. Используя обещания, чтобы все изображения были загружены перед их использованием, не так ли? Он отлично работает, если я изменяю конец файла script.js на drawBackground(level.backgrounds[1], context, sprites);
script.js
import SpriteSheet from './SpriteSheet.js';
import {loadImage, loadLevel} from './loaders.js';
function drawBackground(background, context, sprites) {
background.ranges.forEach(([x1, x2, y1, y2]) => {
for (let x = x1; x < x2; ++x) {
for (let y = y1; y < y2; ++y) {
sprites.drawTile(background.tile, context, x, y);
}
}
});
}
function loadBackgroundSprites() {
return loadImage('SEA01.png').then(image => {
const sprites = new SpriteSheet(image, 16, 16);
sprites.define('ocean', 0, 0);
return sprites;
});
return loadImage('ground.png').then(image=> {
const sprites = new SpriteSheet(image, 16, 16);
sprites.define('ground', 12, 0);
});
}
const canvas = document.getElementById('gamearea');
const context = canvas.getContext('2d');
Promise.all([
loadBackgroundSprites(),
loadLevel('1-1')
]).then(([sprites,level]) => {
console.log(level);
drawBackground(level.backgrounds[0], context, sprites);
});
loader.js
export function loadImage(url) {
return new Promise(resolve => {
const image = new Image();
image.addEventListener('load', () => {
resolve(image);
});
image.src = url;
});
}
export function loadLevel(name) {
return fetch('/levels/${name}.json')
.then(r => r.json());
}
spritesheet.js
export default class SpriteSheet {
constructor(image, w = 16, h = 16) {
this.image = image;
this.width = w;
this.height = h;
this.tiles = new Map();
}
define(name, x, y) {
const buffer = document.createElement('canvas');
buffer.height = this.height;
buffer.width = this.width;
buffer
.getContext('2d')
.drawImage(
this.image,
this.width * x,
this.height * y,
this.width,
this.height,
0,
0,
this.width,
this.height);
this.tiles.set(name, buffer);
}
draw(name, context, x, y) {
const buffer = this.tiles.get(name);
context.drawImage(buffer, x, y);
}
drawTile(name, context, x, y) {
this.draw(name, context, x * this.width, y * this.height);
}
}
1-1.json
{
"backgrounds": [
{
"tile": "ocean",
"ranges": [
[
0, 50,
0, 25
]
]
},
{
"tile": "ground",
"ranges": [
[
18, 25,
10, 15
]
]
}
]
}
Заранее спасибо.
РЕДАКТИРОВАТЬ:
Я обновил свой код для загрузки изображений в разные функции, но получаю ту же ошибку:
script.js
import SpriteSheet from './SpriteSheet.js';
import {loadImage, loadLevel} from './loaders.js';
const canvas = document.getElementById('gamearea');
const context = canvas.getContext('2d');
function drawBackground(background, context, sprites) {
background.ranges.forEach(([x1, x2, y1, y2]) => {
for (let x = x1; x < x2; ++x) {
for (let y = y1; y < y2; ++y) {
sprites.drawTile(background.tile, context, x, y);
}
}
});
}
function drawMario(background, context, mario) {
background.ranges.forEach(([x1, x2, y1, y2]) => {
for (let x = x1; x < x2; ++x) {
for (let y = y1; y < y2; ++y) {
mario.drawTile(background.tile, context, x, y);
}
}
});
}
function loadBackgroundSprites() {
return loadImage('SEA01.png').then(image => {
const sprites = new SpriteSheet(image, 16, 16);
sprites.define('ocean', 0, 0);
return sprites;
});
}
function loadDirtSprite() {
return loadImage('ground.png').then(image => {
const mario = new SpriteSheet(image, 16, 16);
mario.define('ground', 12, 0);
return mario;
});
}
Promise.all([
loadBackgroundSprites(),
loadLevel('1-1'),
loadDirtSprite()
]).then(([sprites, level, mario]) => {
level.backgrounds.forEach(background => {
drawBackground(background, context, sprites, mario);
});
mario.draw('ground', context, 1, 16);
});
заменил ответ:
Ошибка №1.
Упомянутый комментарий в вопросе, исходный код имеет ошибку в loadBackgroundSprites
:
function loadBackgroundSprites() {
return loadImage('SEA01.png').then(image => {
const sprites = new SpriteSheet(image, 16, 16);
sprites.define('ocean', 0, 0);
return sprites;
}); // <-- bad indent
return loadImage('ground.png').then(image=> {
const sprites = new SpriteSheet(image, 16, 16);
sprites.define('ground', 12, 0);
});
}
Строка в середине, обозначенная как "плохой отступ", - это то, где оператор возврата запускался в начале конца loadBackgroundSprites
и выполнение возвращается вызывающему. Оператор return
после строки "bad indent" никогда не выполняет и генерирует консольное сообщение.
недостижимый код после оператора возврата [Подробнее]
Поскольку предупреждение не является фатальным, функция возвращается без ошибок.
Первое исправление для этой ошибки - удалить недостижимый код. Требуется два дополнительных исправления, как описано в следующей ошибке.
loadImage
Ошибка №2.
Экземпляры класса SpriteSheet
содержат одно изображение, переданное в качестве аргумента конструктору. Это изображение должно содержать массив прямоугольных плит с фиксированной шириной и высотой, соответствующих 2-м и 3-м значениям параметров, используемых в вызове конструктора. Это не то, что вы делаете.
Фактически, определение фрагмента в экземпляре SpriteSheet
копирует одну плиту из образа листа спрайта в новый объект с именем Canvas для использования с помощью функций рисования.
Предлагаемое средство для работы со списками спрайтов, как было разработано, состоит в том, чтобы создать изображение листа спрайтов с фоном, которое содержит по крайней мере две 16х16 плитки для океана и земли. Их позиции в изображении листа спрайта (подсчет плиток слева и поперек) определяют, как определение фрагмента должно быть закодировано в loadBackgroundSprites
.
Для этого решения удалите ссылки на ссылки на dirtSprite
. И удалите ссылки на изображения "SEA01.png" и "ground.png" - loadBackgroundSprites
должен загрузить URL-адрес для комбинированного листа спрайтов, содержащего обе плитки.
В цепочке Promise.all
используйте версию forEach
для визуализации спрайтов в соответствии с файлом json:
Promise.all([
loadBackgroundSprites(),
loadLevel('1-1')
]).then(([sprites,level]) => {
console.log(level);
level.backgrounds.forEach(background => {
drawBackground(background, context, sprites)
});
ground
? Обе функции loadDirtSprite()
определяют основу для объекта спрайтов. Почему же тогда он не работает?
Я знаю, что это было довольно давно. Но когда вы создали новое обещание в модуле loader.js - обязательно добавьте .catch((err) => console.log(err))
Также после обновления, если вы все еще видите проблему, попробуйте Ctrl + Shift + R, чтобы обновить статический кеш http, который все еще может кэшировать ваш старый код. Если это поможет, дайте мне знать!
loader.js
export function loadImage(url) {
return new Promise(resolve => {
const image = new Image();
image.addEventListener('load', () => {
resolve(image);
});
image.src = url;
}).catch((err) => console.log(err)); <--------ADD THIS
}
function loadBackgroundSprites
имеет два оператора возврата, второй из которых генерирует предупреждение «недоступный код» (код для загрузки и создания «наземного» спрайта не выполняется). Это связано с проблемой? Вы получаете другие ошибки консоли?this.tiles.get('ground')
вернет undefined, потому чтоloadImage('ground.png')
никогда не будет вызываться.