您如何处理子画面?为什么它们在较老的游戏中得到如此广泛的使用? [英] How do you work with sprites, and why were they used so widely in older games?

查看:94
本文介绍了您如何处理子画面?为什么它们在较老的游戏中得到如此广泛的使用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我和我的朋友决定一起创建一个平台游戏作为我们的计算机科学项目. 我目前在p5.js库中使用图像函数在图像之间交换并创建用于行走,跳跃等的动画. 我查看了以前在Sega Mega Drive和NES等系统上进行2d游戏的方式,并了解当时在2D空间中用于图形处理的几乎所有东西都是精灵.

My friend and I decided to create a platformer together as our computer science project. I am currently using an image function in p5.js library to swap between images and create animations for walking, jumping, etc. I looked up how it was done before for 2d games on systems such as Sega Mega Drive and NES, and understood that at the time pretty much all that was used for graphics in 2D space was sprites.

经过更多研究,我发现精灵被制作成一个大型图像文件,上面带有许多不同的帧动画,并且像NES这样的系统甚至能够将这些图像部分向后和上下翻转!我什至读过您可以将相同的精灵重新着色为不同的调色板!

After more research, I found out that sprites were made into a large image file with a lot of different frame animations on it, and systems like NES were capable of even flipping these parts of images backward and upside-down! I even read that you could recolor the same sprite to a different color palette!

我查找了p5.js库,该库具有sprite功能,但是当我四处查看它们的工作方式时,它们始终仅用于制作彩色正方形,而没有显示如何将它们与将所有图像存储在其中的图像文件一起使用.

I looked up with p5.js library has sprite capabilities but when I look around for how they work they are always only used to make colored squares without showing how to use them with image files that would store all the images in it.

我的问题是这些:

  • 根据定义,子画面是否具有执行所有功能的功能,例如更改子画面的调色板,将其向后或上下颠倒等?
  • 与使用图像显示功能和仅使用png相比,使用sprites有什么优势吗?
  • 为什么它们在较旧的硬件中如此广泛地使用,而今天仍在现代复古风格的游戏(Showelknight,Dead Cells等)中使用?

推荐答案

要简要回答您的Sprite工作表(也称为纹理图集)问题:

To answer your sprite sheet (also known as texture atlases) question briefly:

根据定义,子画面是否具有执行所有功能的能力,例如更改子画面的调色板,将其向后或上下颠倒等?

Do sprites by definition have the capabilities to do all the things like changing the color palette of a sprite, turning it backward or upside-down, etc?

不是,您仍然必须手动对其进行编程. (NES提供了帮助说明,p5.js目前不作为p5.Image AFAIK的一部分具有90度翻转/旋转功能,但是您可以作弊"并使用

Nope, you would have to still manually program that that. (The NES had helper instructions, p5.js doesn't currently have flip/rotate90 degrees functions as part of p5.Image AFAIK, however you could "cheat" and use a PGraphics buffer to draw into applying transformations (translate()/rotate()/scale() to achieve flipping and rotation)

与使用图像显示功能和仅使用png相比,使用sprites有什么优势吗?

Is there any advantage to using sprites as opposed to using image display functions and just using png?

您将为sprite工作表分配一次内存,然后在以后需要复制帧时简单地引用该区域(与数组中的许多独立图像相反,多次加载/解码资产多次) .每个角色/游戏对象具有更多帧,并且能够有效打包像素的游戏对象更多,实际上节省了RAM,使您可以将其用于更多有趣的游戏机制和效果,而不仅仅是原始资产.

You would allocate memory for a sprite sheet once then simply reference to areas of that that later as frames need to be copied over (as opposed to many many independent images in a arrays, having load/decode the asset many times over). With more frames per character/game object and more game objects being able to efficiently pack pixels really saves on RAM allow you to use it for more fun gameplay mechanics and effects instead of just raw assets.

为什么它们在较旧的硬件中如此广泛地使用,而在当今的复古风格游戏(Showelknight,Dead Cells等)中仍然使用它们?

Why is it that they were so widely used in older hardware, and are they still used today in modern retro-styled games(Showelknight, Dead Cells, etc)?

那是硬件的局限性,因此必须尽可能节省资产,以便能够通过严格的控制/游戏机制和故事来吸引观众.今天,它们仍用于3D视频游戏和实时图形:GPU需要2种纹理的幂.即使是同一回事,即使是现代游戏也仍然包含应用于3D模型的2D纹理.

Back then it was the constraint of the hardware so it was essential to economise on assets as much as possible to be able capture the audience with tight controls/gameplay mechanics and story. They're still used today for 3D video games and realtime graphics: the GPU required power of 2 textures. Even though it's quite the same thing, even modern games still pack 2D textures that are applied to 3D models.

除视频游戏外,精灵表还可以在网络上找到另一种用途. StackExchange网站图标页面 这样做的原因相似但又不同:

Beyond video games sprite sheets have found yet another use on the web. A right in front of us example is the StackExchange favicon spritesheet The reason in this is similar, yet different:

  • 相似,因为它仍然是一种优化
  • 有所不同,因为我们可以轻松加载每个单独的图标,这意味着每个单独的图标都会发出多个单独的HTTP请求(初始化连接,等待服务器的确认,获取数据,缓存数据).

执行单个请求并更轻松地使用CSS为正确的图标显示一个图像的各个部分,效率更高.

It's more efficient to do a single request and easily use CSS to display sections of one image for the right icon.

请注意,由于元图标是主要站点的灰度版本,并且具有灰度CSS过滤器,因此可以对电子表格进行进一步优化,但是这可能会使整体代码库更难以阅读和管理,并具有灵活性具有不一定是原始灰度副本的元图标的问题.这说明了他们正在优化请求数量,而不一定是文件大小和内存分配.

Notice that spreadsheet could've been optimised further as the meta icons are grayscale version of the main sites and there is a grayscale css filter, however that might make the overall code base a little harder to read and manage and allow the flexibility of having a meta icon that isn't necessarily a grayscale copy of the original. This is illustrating the point that they're optimising the number of requests, not necessarily file size and memory allocation.

对于您自己的游戏,您将在保持尽可能严格的优化与使代码库尽可能灵活之间找到一个很好的平衡.

For your own game it will up to you to find this fine balance between keeping it as tightly optimised as possible versus having your code base as flexible as it can.

回到p5.js,只需使用2张图片即可:加载的Sprite工作表和分配给复制()将像素插入其中.

Back to p5.js it would be a matter of using 2 images: a loaded sprite sheet and a separate smaller image allocated to copy() sprite pixels into.

这里一个非常简单的示例,显示了几帧马里奥车雪碧:

Here's a very simple example display a few frames of a Mario sprite:

这里的前文参考也是代码:

Fore reference here is the code as well:

您可以在下面运行它:

// 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() 使用您自己的Sprite工作表.

I'm using a base64 encoded string to avoid CORS issues, but you should be able to use preload() and loadImage() to use your own sprite sheet.

关于NES游戏,我建议您查看编写NES游戏!借助Assembly !! 我们如何将NES游戏适配到40 KB .它们既是令人印象深刻的技术成就,又在可视化平台上的精灵表和调色板限制方面做得很好.

In terms of NES games I recommend you check out Writing NES Games! With Assembly!! and How we fit an NES game into 40 Kilobytes. They're both impressive technical achievements and do a very good job at visualising sprite sheet and palette limitations on the platform.

您将不必像之前看到的那样经历这些障碍并理解二进制/字节就可以在p5.js中使用,但是了解这些较早的约束以构建高效的游戏很有趣.

You won't have to go through these hurdles and understanding binary/bytes to be able to use in p5.js as you could see earlier, but it's interesting to understand these older constraints to build efficient games.

在软件方面,有多种选择. 即使我不认可我也可以推荐Texture Packer. 您可以立即在线试用一个简单的Web应用版本: SpriteSheetPacker ,他们拥有几个愚蠢的情报类动画: SpriteSheets-电影第1部分 Sprite Sheets-The Movie Pt. 2-效果

In terms of software, there are multiple options out there. Even though I'm not endorsed I can recommend Texture Packer. There's a simple web app version you can try online now: SpriteSheetPacker and they have a couple of silly informercial like animations: SpriteSheets - The Movie Part 1 and Sprite Sheets - The Movie Pt. 2 - Performance

在动作脚本时代,有几个非常好的以像素为中心的游戏引擎: Flixel (用于原始的 Canabalt )和 HaxeFlixel PixelJS phaser

Back in actionscript days there were a couple of really good pixel centred game engines: Flixel(used for the original Canabalt) and FlashPunk. There are HaXe ports available: such as HaxeFlixel and HaxePunk as well as other native JS ones (e.g. PixelJS, phaser, ImpactJS, etc.).

最近,有趣的是使用2D WebGL引擎(例如 PixiJS )的PixelArt风格的游戏.尽管在游戏机制方面非常商业化且非常简单,但这是臭数字工作室:Miu Miu Twist

Recently it's been interesting to see PixelArt style games using 2D WebGL engines such as PixiJS. Although very commercial and simple in terms of game mechanics, here's a nicely rendered game by Stink Digital Studios: Miu Miu Twist

p5.js非常适合完全理解一些基本概念,这些概念对于加载/处理资产,处理像素,处理输入等至关重要. 就像一个范围广泛的库一样,因此只需记住它就可以针对游戏进行优化.很棒的开始!

p5.js is excellent to fully understand some of the basics notions which are crucial in terms of loading/handling assets, working with pixels, handling input, etc. as is a fairly broadly encompassing library so bare in mind it might be optimised for games alone. Great way start though!

这篇关于您如何处理子画面?为什么它们在较老的游戏中得到如此广泛的使用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆