我和我的朋友决定一起创建一个平台游戏作为我们的计算机科学项目。我目前正在使用p5.js库中的图像功能来交换图像并为步行,跳跃等创建动画。我查看了之前在Sega Mega Drive和NES等系统上进行2D游戏的过程,并了解到当时几乎所有用于2D空间中的图形的都是精灵。
经过更多的研究,我发现精灵被制作成一个大的图像文件,上面有很多不同的帧动画,像NES这样的系统甚至可以向后和向下翻转这些图像部分!我甚至读过你可以将同一个精灵重新着色到不同的调色板!
我查了一下p5.js库有sprite功能,但是当我四处寻找它们的工作方式时,它们总是只用于制作彩色方块而不显示如何将它们用于存储所有图像的图像文件。
我的问题是这些:
要简单地回答你的精灵表(也称为纹理地图集)问题:
translate()/rotate()/scale()
来实现翻转和回转)当时它是硬件的约束因此,尽可能节省资产以通过严格的控制/游戏机制和故事捕捉观众是至关重要的。它们至今仍用于3D视频游戏和实时图形:GPU需要2个纹理的力量。即使它是完全相同的东西,即使现代游戏仍然包装应用于3D模型的2D纹理。
除了视频游戏之外,精灵表还发现了网络上的另一种用途。在我们面前的一个例子是StackExchange favicon spritesheet这个原因是相似但不同的:
执行单个请求并使用CSS显示正确图标的一个图像的部分更有效。
请注意,电子表格可以进一步优化,因为元图标是主要网站的灰度版本,并且有一个灰度级css过滤器,但这可能会使整个代码库更难以阅读和管理,并允许具有元图标不一定是原始的灰度副本。这表明他们正在优化请求数量,而不一定是文件大小和内存分配。
对于您自己的游戏,您可以在尽可能保持最佳优化与尽可能灵活的代码库之间找到这种精细平衡。
回到p5.js,这将是一个使用2个图像的问题:一个加载的精灵表和分配给copy()精灵像素的单独的较小图像。
Here是一个非常简单的例子,显示了一些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()
and loadImage()
来使用你自己的精灵表。
在NES游戏方面,我建议你看看Writing NES Games! With Assembly!!和How we fit an NES game into 40 Kilobytes。它们都是令人印象深刻的技术成就,并且在平台上可视化精灵表和调色板限制方面做得非常出色。
您将不必经历这些障碍并理解二进制/字节以便能够在前面看到的p5.js中使用,但理解这些旧约束以构建高效游戏很有趣。
在软件方面,有多种选择。即使我没有得到认可,我也可以推荐Texture Packer。有一个简单的网络应用程序版本你可以在网上尝试:SpriteSheetPacker和他们有一些愚蠢的informercial像动画:SpriteSheets - The Movie Part 1和Sprite Sheets - The Movie Pt. 2 - Performance
回到actionscript时代,有几个非常好的以像素为中心的游戏引擎:Flixel(用于原始的Canabalt)和FlashPunk。有可用的HaXe端口:例如HaxeFlixel和HaxePunk以及其他本地JS端口(例如PixelJS,phaser,ImpactJS等)。
最近看到使用像PixiJS这样的2D WebGL引擎的PixelArt风格游戏很有意思。虽然在游戏机制方面非常商业化和简单,但这是Stink Digital Studios: Miu Miu Twist精心渲染的游戏
p5.js非常适合完全理解一些基本概念,这些概念在加载/处理资产,处理像素,处理输入等方面至关重要,因为它是一个相当广泛的库,所以在考虑它时可能会针对游戏进行优化单独。好的方式开始!