Как вы работаете со спрайтами и почему они так широко использовались в старых играх? - PullRequest
0 голосов
/ 07 апреля 2019

Мы с другом решили вместе создать платформер в качестве нашего проекта по информатике.В настоящее время я использую функцию изображения в библиотеке p5.js для переключения между изображениями и создания анимации для ходьбы, прыжков и т. Д. Я посмотрел, как это было сделано ранее для 2D-игр на таких системах, как Sega Mega Drive и NES, и понял, чтов то время практически все, что использовалось для графики в 2D-пространстве, были спрайты.

После дополнительных исследований я обнаружил, что спрайты были превращены в большой файл изображения с множеством различных кадровых анимаций, итакие системы, как NES, способны даже переворачивать эти части изображений назад и вверх ногами!Я даже читал, что вы можете перекрасить один и тот же спрайт в другую цветовую палитру!

Я искал в библиотеке p5.js возможности спрайтов, но когда я смотрю на то, как они работают, они всегда используются только для окрашиванияквадраты, не показывая, как использовать их с файлами изображений, в которых будут храниться все изображения.

Мои вопросы таковы:

  • Есть ли у спрайтов по определению возможности делать всетакие вещи, как изменение цветовой палитры спрайта, переворачивание его назад или вверх дном и т. д.
  • Есть ли преимущество в использовании спрайтов по сравнению с использованием функций отображения изображений и просто использованием png?
  • Почему они так широко использовались в старом оборудовании и используются ли они до сих пор в современных играх в стиле ретро (Showelknight, Dead Cells и т. Д.)?

1 Ответ

2 голосов
/ 07 апреля 2019

Чтобы кратко ответить на ваш спрайт (также известный как текстурный атлас):

  • Есть ли у спрайтов по определению возможность делать все, например, менять цветовую палитру спрайта, переворачивать его назад или вверх дном и т. Д.?

    Нет, вам все равно придется вручную запрограммировать это. (У 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:

Для справки здесь также указан код:

mario spritesheet

Вы можете запустить его ниже:

// 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 килобайт .Они оба являются впечатляющими техническими достижениями и отлично справляются с визуализацией ограничений спрайт-листа и палитры на платформе.

40KB NES Game youtube video screenshot 1

40KB NES Game youtube video screenshot 2

40KB NES Game youtube video screenshot 3

40KB NES Game youtube video screenshot 4

40KB NES Game youtube video screenshot 5

40KB NES Game youtube video screenshot 6

Вам не нужно будет проходить через эти препятствия и понимать двоичные / байтовые символы, чтобы иметь возможность использовать в p5.js, как вы видели ранее,но интересно понять эти старые ограничения для создания эффективных игр.

С точки зрения программного обеспечения, существует множество вариантов.Хотя я не одобряю, я могу рекомендовать Texture Packer.Есть простая версия веб-приложения, которую вы можете попробовать онлайн: SpriteSheetPacker , и у них есть пара глупых рекламных роликов: SpriteSheets - Часть фильма 1 и Sprite Sheets - TheФильм Pt.2 - Производительность

В дни ActionScript существовало несколько действительно хороших игровых движков с центрированием пикселей: Flixel (используется для оригинальных Canabalt ) и FlashPunk .Доступны порты HaXe: например, HaxeFlixel и HaxePunk , а также другие собственные JS-порты (например, PixelJS , phaser , ImpactJS и т. Д.).

Canabalt

В последнее время было интересно увидеть игры в стиле PixelArt с использованием 2D-движков WebGL, таких как PixiJS.Несмотря на то, что она очень коммерческая и простая с точки зрения игровой механики, вот хорошо отрисованная игра от Stink Digital Studios: Miu Miu Twist

miu miu twist preview 1

miu miu twist preview 2

miu miu twist preview 3

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

...