Чтобы кратко ответить на ваш спрайт (также известный как текстурный атлас):
Есть ли у спрайтов по определению возможность делать все, например, менять цветовую палитру спрайта, переворачивать его назад или вверх дном и т. Д.?
Нет, вам все равно придется вручную запрограммировать это. (У NES были вспомогательные инструкции, p5.js в настоящее время не имеет функций переворачивания / поворота на 90 градусов как часть p5.Image AFAIK, однако вы можете «обмануть» и использовать буфер PGraphics для использования в преобразованиях. (translate()/rotate()/scale()
для достижения переворачивания и вращения)
Есть ли какое-либо преимущество в использовании спрайтов по сравнению с использованием функций отображения изображений и просто использованием png?
Вы должны выделить память для листа спрайта один раз, а затем просто сослаться на области того, что позже, так как кадры должны быть скопированы (в отличие от множества независимых изображений в массивах, имеющих многократно загружать / декодировать ресурс) , Благодаря большему количеству кадров на персонажа / игрового объекта и большему количеству игровых объектов, позволяющих эффективно упаковать пиксели, реально экономится память, что позволяет использовать ее для более увлекательной игровой механики и эффектов вместо простых ресурсов.
Почему они так широко использовались в старом оборудовании и используются ли они до сих пор в современных играх в стиле ретро (Showelknight, Dead Cells и т. Д.)?
В то время это было ограничением аппаратного обеспечения, поэтому было необходимо максимально экономить на активах, чтобы иметь возможность захватывать аудиторию с жестким управлением / игровой механикой и сюжетом. Они до сих пор используются для 3D-видеоигр и графики в реальном времени: для графического процессора требовалось 2 текстуры. Несмотря на то, что это одно и то же, даже современные игры по-прежнему содержат 2D-текстуры, которые применяются к 3D-моделям.
Помимо спрайтов в видеоиграх нашли еще одно применение в Интернете.
Примером прямо перед нами является спрайт-лист избранного StackExchange
Причина в этом похожа, но другая:
- похоже, потому что это все еще оптимизация
- отличается тем, что мы можем легко загрузить каждый отдельный значок, что будет означать создание нескольких отдельных HTTP-запросов для каждого (инициализация соединения, ожидание подтверждения от сервера, получение данных, кэширование данных).
Более эффективно делать один запрос и легко использовать CSS для отображения разделов одного изображения для нужного значка.
Обратите внимание, что электронную таблицу можно было бы оптимизировать и дальше, так как мета-иконки - это версия основных сайтов в оттенках серого, а также имеется фильтр CSS в оттенках серого, однако это может сделать общую кодовую базу немного сложнее для чтения и управления, а также обеспечить гибкость. иметь мета-иконку, которая не обязательно является копией оригинала в оттенках серого. Это иллюстрирует тот факт, что они оптимизируют количество запросов, а не обязательно размер файла и распределение памяти.
Для вашей собственной игры вам нужно найти этот прекрасный баланс между сохранением ее максимально оптимизированной и максимально гибкой вашей кодовой базой.
Возвращаясь к p5.js, можно использовать 2 изображения: загруженный спрайт-лист и отдельное уменьшенное изображение, выделенное для copy () спрайтов пикселей в.
Здесь очень простой пример отображения нескольких кадров спрайта Mario:
Для справки здесь также указан код:
Вы можете запустить его ниже:
// full spritesheet
var spriteSheet;
// a sprite sampling from sprite sheet
var mario;
// 8 frames in the spritesheet
var numSprites = 8;
// each sprite in the sheet has this bounding box
var spriteWidth = 18;
var spriteHeight = 24;
// start frame
var spriteIndex = 1;
function setup(){
createCanvas(150,150);
frameRate(24);
noSmooth();
noFill();
spriteSheet = loadImage("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJAAAAAYCAYAAAAVpXQNAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQwIDc5LjE2MDQ1MSwgMjAxNy8wNS8wNi0wMTowODoyMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTggKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MzE1ODI1MkNDQ0MzMTFFOEJFNjA5ODI5Q0U0NzlGOEEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MzE1ODI1MkRDQ0MzMTFFOEJFNjA5ODI5Q0U0NzlGOEEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpFM0U1NkY3RkNDQTMxMUU4QkU2MDk4MjlDRTQ3OUY4QSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpFM0U1NkY4MENDQTMxMUU4QkU2MDk4MjlDRTQ3OUY4QSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PszND6MAAAXWSURBVHja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");
// create an image to draw a single sprite into
mario = createImage(spriteWidth,spriteHeight);
}
// set all pixels (R,G,B,A) to the same value (e.g. clear image with a colour)
function setAllPixels(image,brightness){
// prep. pixels for manipulation
image.loadPixels();
let numPixels = image.pixels.length;
// loop through all pixels (spriteWidth * spriteHeight * colourChannels(4))
for(let i = 0 ; i < numPixels; i++){
image.pixels[i] = brightness;
}
// commit value changes to image: updates it all in one go, more efficient than set()
image.updatePixels();
}
function draw(){
// clear frame
background(255);
// display the whole sprite sheet
image(spriteSheet,0,0);
// increment sprite index
spriteIndex++;
// reset sprite index if out of bounds
if(spriteIndex >= numSprites){
spriteIndex = 0;
}
// visualise sprite copy rect
rect(spriteIndex * spriteWidth,0,spriteWidth,spriteHeight);
// clear mario image
setAllPixels(mario,255);
// copy pixels from sprite sheet into sprite
// copy (source image, source coordinates(x,y,w,h), destination coordiantes (x,y,w,h) )
mario.copy(spriteSheet,
spriteIndex * spriteWidth,0,spriteWidth,spriteHeight,
0 ,0,spriteWidth,spriteHeight);
// display mario sprite
image(mario,mouseX,mouseY+spriteHeight);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.min.js"></script>
Я использую строку в кодировке base64, чтобы избежать проблем с CORS, но вы должны иметь возможность использовать preload()
и loadImage()
, чтобы использовать свой собственный спрайт.
С точки зрения игр NES, я рекомендую вам проверить Написание игр NES!С Assembly !! и Как мы помещаем игру NES в 40 килобайт .Они оба являются впечатляющими техническими достижениями и отлично справляются с визуализацией ограничений спрайт-листа и палитры на платформе.
Вам не нужно будет проходить через эти препятствия и понимать двоичные / байтовые символы, чтобы иметь возможность использовать в p5.js, как вы видели ранее,но интересно понять эти старые ограничения для создания эффективных игр.
С точки зрения программного обеспечения, существует множество вариантов.Хотя я не одобряю, я могу рекомендовать Texture Packer.Есть простая версия веб-приложения, которую вы можете попробовать онлайн: SpriteSheetPacker , и у них есть пара глупых рекламных роликов: SpriteSheets - Часть фильма 1 и Sprite Sheets - TheФильм Pt.2 - Производительность
В дни ActionScript существовало несколько действительно хороших игровых движков с центрированием пикселей: Flixel (используется для оригинальных Canabalt ) и FlashPunk .Доступны порты HaXe: например, HaxeFlixel и HaxePunk , а также другие собственные JS-порты (например, PixelJS , phaser , ImpactJS и т. Д.).
В последнее время было интересно увидеть игры в стиле PixelArt с использованием 2D-движков WebGL, таких как PixiJS.Несмотря на то, что она очень коммерческая и простая с точки зрения игровой механики, вот хорошо отрисованная игра от Stink Digital Studios: Miu Miu Twist
p5.js отлично подходит для полного понимания некоторых основных понятий, которые имеют решающее значение с точки зрениязагрузка / обработка ресурсов, работа с пикселями, обработка ввода и т. д., поскольку это довольно обширная библиотека, поэтому можно подумать, что она может быть оптимизирована только для игр.Отличный способ начать, хотя!