.NET的Sprite编辑器(译文)
By robot-v1.0
本文链接 https://www.kyfws.com/games/sprite-editor-for-net-zh/
版权声明 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
- 45 分钟阅读 - 22177 个词 阅读量 0.NET的Sprite编辑器(译文)
原文地址:https://www.codeproject.com/Articles/54131/Sprite-Editor-for-NET
原文作者:Christ Kennedy
译文由本站 robot-v1.0 翻译
前言
Build, edit, and animate your own sprites.
建立,编辑和设置自己的精灵动画.
- 下载Sprite类-25.6 KB(Download Sprite class - 25.6 KB)
- 下载Sprite编辑器-107 KB(Download Sprite Editor - 107 KB)
- 下载精灵-3.73 MB(Download Sprites - 3.73 MB)
- 下载Sprite演示-62.8 KB(Download Sprite demo - 62.8 KB)
- 下载图形编辑器-64.5 KB(Download Graphics Editor - 64.5 KB)
- 下载暗夜魔王-2.09 MB(Download Night Stalker - 2.09 MB)
什么是精灵?(What is a sprite?)
如果您在字典或互联网上查找" sprite",您将获得对仙女,高级龙与地下城和龙的引用,甚至还有点小精灵的灰尘.当您单击将您带到此处的链接时,可能就是您的想法,但是本文旨在使2D图形更加灵活.在本文中,sprite是要包含在射击游戏中的角色,并且此Sprite Editor程序及其classSprite允许您从互联网或任何地方获取图像,并将其分割为分开肢体组件,然后用柔性铰链将它们重新连接,使您可以分别旋转每个肢体.然后,您将拥有一个可以编程和制作动画的图形人偶.(If you look up “sprite” in a dictionary or on the internet, you’ll get references to fairies, Advanced Dungeons and Dragons, and maybe even a bit of pixie dust. That may be what you were thinking when you clicked on the link that brought you here, but this article is about making 2D graphics a little bit more flexible. A sprite, in this article, is a character you want to include in your shoot’em up video game, and this Sprite Editor program, along with its classSprite, allows you to take an image off the internet or wherever, cut it up into separate limb components, and then reattach these with flexible hinges that let you rotate each limb separately. You then have yourself a graphics puppet you can program and animate.)
怎么运行的(How it works)
精灵由四肢组成.每个肢体都有其基础图像,然后以16个不同角度(彼此相距22.5°)进行复制,并通过铰链将其彼此连接.有一个主肢具有一个或多个从属肢体,每个从属肢体可能有也可能没有自己的一个或多个从属肢体.(A sprite is composed of limbs. Each limb has its base image, which is then copied at 16 different angles, 22.5° apart from each other, and are attached one to the other via hinges. There is one master-limb which has one or more slave limbs, and each slave limb may or may not have one or more slave limbs of its own.) 首先,这听起来可能令人困惑,但是这个概念并不难.而且,与其尝试用二十个单词或更短的时间来解释整个项目的机制,要么不考虑将其扩展为冗长的论文,让我们看看是否可以逐步引导您完成自己的精灵的过程.然后,我将在后面详细介绍链轮的接线方式.(This might sound confusing to start with, but the concept isn’t that difficult. And, instead of trying to explain the mechanism of the whole project in twenty words or less, or considering expanding the idea into a lengthy dissertation, let’s see if we can step you through the process of making your own sprite. Then, I’ll tell you about how the sprockets are wired in more detail later.)
使用编辑器(Using the editor)
认识我们的新朋友脾气暴躁的蓝精灵.他不是很健谈,现在,他所知道的唯一方法就是四处走走并抱怨.不用担心,因为我知道一旦掌握了该程序,您就能让他做各种各样的事情.但是首先,我将引导您完成将下面的图像实际转换为上面的精灵的过程.(Meet our new friend Grumpy Smurf. He’s not very chatty, and right now, all he knows how to do is walk around and grumble. No worries, because I know you’ll be able to make him do all sorts of things once you get the hang of this program. But first, I will walk you through the process of actually turning the image below into the sprite above.)
您可以在此图像中看到制作精灵所需的一切.但是,您可能已经注意到他的左手被隐藏了,因此,如果您已经下载了(You can see in this image that you have all you need to make a sprite. However, you might have noticed that his left hand is concealed, and so if you’ve already downloaded the)**Sprites.zip(Sprites.zip)**此页面链接到的文件,并查看了(file this page links to, and have taken a look at the images in the)**蓝精灵(Smurf)**目录中,您会看到Grumpy的左手已被编辑.一点蓝色,一点黑色,对任何用鼠标的人来说都是没有弹性的,还有一点耐心.(directory, you’ll see that Grumpy’s left hand has been edited. A bit of blue, a bit of black, no real stretch for anyone with a mouse, and a bit of patience.) 由于您已经拥有了所有图像,因此需要开始使用,并且您已经启动了C#IDE并在其中运行(Since you’ve already got all the images you need to get started, and you’ve got your C# IDE up and running with the)**SpriteEditor.cs(SpriteEditor.cs)**源代码,您就可以创建第一个精灵了.要做的第一件事是在菜单上选择"文件",然后单击"新建Sprite",然后,出现"创建新Sprite"表单,其中包含Sprite名称和Master-Limb名称的默认值,例如以及上面写有"点击加载图片"的图片.在第一个焦点文本框中,只需键入单词" Smurf"或"脾气暴躁的蓝精灵",或为新的精灵指定的任何宠物名称,然后跳至下一个文本框,并键入精灵的" Master Limb"名称,在这种情况下," torso"是一个易于使用的描述性名称.然后,点击图片,找到前往(source code, you’re ready to create your first sprite. The first thing you do is select File on the menu and then click ‘New Sprite’, and when you do, the ‘Create New Sprite’ form will appear with the default values for the sprite’s name and the sprite’s Master-Limb’s name, as well as an image with the words ‘Click on image to load’ written on it. In the first focused text box, just type the word ‘Smurf’ or ‘Grumpy Smurf’ or whatever pet-name you have for your new sprite, and then tab to the next text box and type the name of your sprite’s ‘Master Limb’, in this case, ‘torso’ is an easy and descriptive name to use. Then, click on the image and find your way to the)**蓝精灵(smurf)**目录中(directory in the)**sprites.zip(sprites.zip)**您下载并提取的文件.在此处,选择"(file which you downloaded and extracted. From there, you select the ‘)**躯干(torso.bmp)**文件,完成后,“创建新的Sprite"表单应如下所示:(’ file, and when you’re done, your ‘Create New Sprite’ form should look like this:)
您可以选择使精灵旋转或不旋转.您最好保持检查状态.添加肢体后,您将获得相同的选项,对于大多数肢体,您将希望允许旋转.至于"始终垂直"的概念,假设您的手臂上有一把枪手.如果它们都旋转并且您的精灵正在向他身后的某人射击,则旋转后的图像将倒置显示.对于枪手(甚至是手臂),您可以决定图像应始终是垂直的.这意味着它会旋转,但是镜像会在另一侧出现,使枪支图像保持垂直,而枪支将旋转到上下位置.(You can choose to make your sprite rotate or not. You may as well keep it checked; you’ll get the same options later when you add limbs, and for most of them, you’ll want to allow rotation. As for the ‘always vertical’ concept, suppose you had a gun hand attached to an arm. If they both rotate and your sprite is shooting at someone behind him, then the rotated image will appear upside down. In the case of the gun hand (and maybe even the arm), you can decide that the image should always be vertical. This means it will rotate, but in such a way that a mirror image will appear on the other side, keeping the gun image vertical where it would have rotated to an upside down position.) …但是那比我现在想给你的还要详细.让我们继续前进.(… but that’s more detail than I wanted to give you just yet. Let’s move on.) 至此,这就是我们所拥有的.(At this point, this is what we have.)
接下来,我们要添加一些肢体:头部,手臂和腿.但是首先,我们需要转到"选定的肢体"组合框,并选择刚创建的精灵的主肢名称:“躯干”.随着(So next, we want to add some limbs: head, arms, and legs. But first, we need to go to the ‘Selected Limb’ combo box and select the name of the master limb we just created our sprite with: ‘torso’. With the)**躯干(torso)**肢体作为"选择肢体”,我们现在可以继续向Smurf精灵添加新的肢体.为此,我们转到"肢体"菜单,然后单击"创建新体"选项,默认的"创建新肢体"表单将显示主肢体的图像和名称,以及相同的"单击"要加载的图片"图片,以及两个默认的预设复选框和空白文本框.但是请不要害怕,脾气暴躁总是有点困难,所以不必担心.首先,输入您的精灵新肢体的名称.我们将首先添加头部,因此我们将这个新肢体称为"(limb as the Selected-Limb, we can now go ahead and add a new limb to our Smurf sprite. To do this, we go to the ‘Limbs’ menu and click on the ‘Create new’ option, and a default ‘Create New Limb’ form will appear with the Master Limb’s image and name on it, along with that same ‘Click on image to load’ picture, and two default pre-set check boxes and blank textboxes. But don’t be scared, Grumpy’s always a little difficult, so there’s no need to get worried. First, type the name of your sprite’s new limb. We’ll add the head first, so we call this new limb ‘)头(head)'.跳至下一个文本框,输入新肢体铰链的名称.最好为肢体和铰链使用直观且合乎逻辑的名称.在这种情况下,以后要记住的最简单的方法是说"(’. Tabbing to the next text-box, type the name of the new limb’s hinge. It is best if you use intuitive and logical names for your limbs and hinges. In this case, the easiest thing to remember for later would be to say that the ‘)头(head)'(四肢)连接到(’ (limb) is attached to the)躯干(torso)(四肢)((limb) by the ‘)颈部(neck)'(铰链),因此我们将与(’ (hinge), so we call the hinge that ties the)**头(head)**到(to the)躯干(torso)'(')颈部(neck)';我们保留复选框不变,然后单击"确定"按钮.('; we keep the check boxes as they are, and click the OK button.)
您可以在下面的这张图片中看到(You can see in this image below that with the)**头(head)**正好位于(centered exactly over the)躯干(torso),我们再也看不到(, we can no longer see the)**躯干(torso)**都!但是,这种方式存在一些问题(at all! But, there are a few things wrong with the way the)**头(head)**现在是固定的,所以我们必须设置铰链参数,然后设置正确的Grumpy(is fixed right now, so we’ll have to set the hinge parameters and right Grumpy’s)头(head),否则他将不会忘记.现在是查看屏幕上某些选项的好时机.您可能已经注意到图像左侧的那些按钮,尽管目前文本框中的值已初始化为零,但您可能会知道它们的含义.正如我之前提到的,每个肢体都有一个或多个从属肢体,并且只有一个主肢体.在脾气暴躁的情况下,他的高手是他的(, or he’ll get a kink he won’t forget. Now’s a good time to look at some of the options on the screen. You probably noticed those buttons on the left of the image, and though the values in the text boxes are initialized to zero for now, you might have a clue what they’re about. As I mentioned before, each limb has one or more slave limbs, and there’s only one master-limb. In Grumpy’s case, his master-limb is his)躯干(torso),现在他只有一条肢体,他的(, and right now, he only has one limb, his)头(head).是的,脾气暴躁的人用肚子想!但是,谁没有呢?(. Yes, Grumpy thinks with his stomach! But, who doesn’t, right?)
为了使铰链固定(In order for the hinge to hold the)**头(head)**对,我们必须将铰链放置在相对于主肢的某个位置(right, we have to position the hinge somewhere relative to the master limb)**躯干(torso)**的中心.此位置固定在中心,并随图像旋转,所有铰链都适用:铰链的主位置是相对于铰链主肢的中心和旋转.(’s center. This location is fixed to the center, and rotates with the image, and this is true for all hinges: the hinge’s master location is relative to the hinge’s master limb’s center and rotation.) 尽管精灵只有一个(Though the sprite has only one)肢体大师(Master Limb),每个奴隶肢体都有自己的主人(, each slave limb has its own master which)可能会或可能不会(may or may not be)精灵的主要肢体.比Grumpy更清晰的关节精灵可能具有(the sprite’s master limb. A more fully articulated sprite than Grumpy here may have a)**手(hand)**肢体附着于(limb attached to a)**前臂(forearm)**肢体(limb by a)**腕(wrist)**铰链和(hinge, and the)**前臂(forearm)**肢体附着在(limb attached to the)**臂(arm)**四肢(limb by an)**弯头(elbow)**铰链,然后(hinge, and in turn, the)**臂(arm)**肢体在逻辑上将附着在(limb would logically be attached to the)**躯干(torso)**由(by the)**肩(shoulder)**合页.假设在这个假设的例子中(hinge. Suppose in this hypothetical example that the)**躯干(torso)**是精灵的主要肢体.然后,尽管(is the sprite’s master limb. Then, though the)**臂(arm)**的主肢与子画面的主肢相同(’s master limb is the same as the sprite’s master limb, the)躯干(torso),(, the)**臂(arm)**也是(is also the)**前臂(forearm)**的主肢.的(’s master limb. The)**臂(arm)**既是(is then both one of the)**躯干(torso)**的奴隶肢体和(’s slave limbs and the)**前臂(forearm)**的主肢,即使(’s master limb, even though the)**臂(arm)**是(is)不(not)精灵的主要肢体.(the sprite’s master limb.) 每个铰链都有两个径向坐标和一个角度.由于铰链将从属肢体连接到其主肢,因此描述这两个肢体之间连接位置的弧度称为(Each hinge has two radial coordinates and an angle. Since the hinges connect a slave limb to its master, the radians that describe where the connections between these two limbs are made are called)**大师拉德(master-rad)**和(and)奴隶拉德(slave-rad).铰链的主弧度是铰链与铰链主肢中心之间的角度和与铰链主肢中心的距离.并且,铰链的从动角是铰链与从动臂的中心之间的角度以及与铰链的从动臂中心的距离.(. The hinge’s master-rad is the hinge’s angle from and distance to the center of the hinge’s master limb. And, the hinge’s slave-rad is the hinge’s angle from and distance to the center of the hinge’s slave limb.) 这听起来可能比实际复杂.我将更详细地解释图像在屏幕上的位置,但现在让我们来看看Grumpy的(This probably sounds more complicated than it actually is. I’ll explain further on in more detail how the images are positioned on the screen, but for now, let’s just get Grumpy’s)**头(head)**对.首先,我们将(right. First, we’ll set the)**颈部(neck)**的大师我们可以以图形方式或硬性方式执行此操作,然后将值直接写入文本框.要以图形方式设置"选定的铰链"的主辐射,请按下图中圈出的按钮.(’s master-rad. We can do this either graphically or the hard-core way, and write the values directly into the textboxes. To set the Selected-Hinge’s master-rad graphically, press the button circled in the image below.)
查看屏幕左侧的分组框的名称,您会知道我们正在设置将"(Looking at the name of the group boxes on the left of the screen, you’ll know that we’re setting the radial-coordinates that position the ‘)颈部(neck)‘将’(’ hinge which connects the ‘)躯干(torso)‘到(’ to the)头(head).自"(. Since the ‘)**躯干(torso)**不仅是这种精灵,而且是铰链,(’ is the master limb, not only of this sprite, but also of this hinge, the)**躯干(torso)**出现在上方,并且"(appears above and the ‘)**头(head)**在此铰链的参数配置下.的(’ below on this hinge’s parameter configuration. The)**颈部(neck)**铰链的(hinge’s)**躯干(torso)**径向坐标是铰链相对于铰链中心的位置(radial coordinate is the position of the hinge relative to the center of the)**躯干(torso)**的图像和铰链的(’s image, and the hinge’s)**头(head)**径向坐标相对于(radial coordinate is located relative to the center of the)**头(head)**的图像.因此,当我们点击"鼠标设置"按钮时,(’s image. So, when we click on the ‘Mouse Set’ button for the)**颈部(neck)**铰链的(hinge’s)**躯干(torso)**主辐射坐标,带有图像的形式(master-rad coordinate, a form with an image of the)躯干(torso),就像下面图片中的图片一样,将出现在屏幕上.(, like the one in the image below, will appear on your screen.)
左右移动鼠标将显示给您矢量,径向矢量将在设置后描述该矢量,并设置其位置.(Moving the mouse around will show you the vector which the radial coordinate will describe once it has been set, and to set the position of the)**颈部(neck)**相对于其主肢中心的铰链,只需单击图像(hinge relative to its master limb’s center, just click the image where the)**颈部(neck)**铰链继续(hinge goes on the)躯干(torso).您可以在下面的图像中看到,在进行屏幕截图时,鼠标位于从图像中心绘制的线条的顶部,这可能是您要放置Grumpy的位置(. You can see in the image below that the mouse was at the top edge of the line that is drawn from the image’s center when the screen capture was taken, and that’s probably where you’ll want to put Grumpy’s)**颈部(neck)**太.当您单击所需的表单时(too. When you click on the form where you want the)**颈部(neck)**继续," Master-rad坐标图形设置表格"将消失.(to go, the “Master-rad coordinate graphic set form” will disappear.)
现在好了.那是更好的事,我不想劝阻你,但这是不完全正确的.如果你这样丢下脾气,脾气暴躁的脖子会变得很酸.您知道,如果不帮助他,您可能会遇到工会麻烦,因此,最好继续前进,摆脱这种纠结.您现在会注意到,主辐射坐标值,(Well, now. That’s ~better~, and I don’t want to discourage you, but that’s just not quite right. Grumpy’s neck is going to get awful sore if you leave him like that. You know you could get union trouble if you don’t help him out, so you’d better get a move on and get rid of that kink. You’ll notice now that the master-rad coordinate values, the)**颈部(neck)**铰链相对于(hinge’s position relative to the)躯干(torso),不再设为零.与其像我们刚才那样以图形方式放置Grumpy的脖子,不如做的是更改文本框中的值并使您的手变脏一点.(, are no longer set to zero. Instead of graphically positioning Grumpy’s neck like we just did, what you can do is change the values in the textbox and get your hands dirty a little playing around with it.) 好,现在,让我们设置(OK, for now, let’s set the)**头(head)**相对于的rad坐标(’s rad coordinate relative to the)颈部(neck).的(. The)**头(head)**在这种情况下是从属肢体,其铰链从属体坐标位于标记为’(is the slave-limb in this case, and its hinge slave coordinates are in the lower groupbox labeled ‘)头(head)'.设置这些值的概念几乎与我们刚才所做的相同:单击下面带圆圈的按钮.(’. Setting these values is a concept almost identical to what we just did: click on the button circled below.)
因此,我们要在此处执行的操作是在头部图像上选择颈部要去的位置.只需单击正确位置的图像,该表格就会消失.(So, what we want to do here is select a spot on the head image where the neck goes. Just click on the image at the right spot, and this form will disappear.)
好的,您明白了这一点.您可以看到Grumpy的头现在位置正确,但是他仍然有问题,这就是问题所在(OK, you’re getting the hang of this. You can see that Grumpy’s head is now in the right place, but he’s still got a problem, and this is where things get)有趣(interesting).(.)
如果单击下面图像中带圆圈的按钮,则可以在主窗体中将鼠标移到图像上,并且您会看到Grumpy点头(您可以通过按Alt-A选择此按钮;这将派上用场当您稍后创建动画时).由于三角函数的诸神在三点钟处置零,因此,如果您首先从指向右边的图像开始,控制肢体与屏幕的夹角是最简单,最直观的方法.在这种情况下,脾气暴躁的(If you click on the button circled in the image below, you can move the mouse over the image in the main form, and you will see Grumpy nod his head (you can select this button by pressing Alt-A; this will come in handy when you’re creating animations later). Because the gods of trigonometry put zero at 3 O’clock, it is easiest and most intuitive to control the angle which a limb makes with the screen if you’ve started with images that point to the right in the first place. In this case, Grumpy’s)**头(head)**肢体的主要形象使他面朝上,(limb’s main image had him facing upwards, and the)颈部(neck)(主rad的位置((location of master rad for the)**头(head)**的铰链)指向右侧.(’s hinge) is directed to the right.)
如果将鼠标移到屏幕底部附近,您会发现他的头正好在Grumpy喜欢的地方.好吧,不.实际上,脾气暴躁的人并没有真正喜欢什么,但是至少那样,他可以避免走路时踩到脚趾,因为他真的很讨厌踩到脚趾.(If you move the mouse to a location near the bottom of the screen, you’ll see that his head will be just about where Grumpy likes it. Well, no. Actually, Grumpy doesn’t really ~like~ anything, but at least that way, he can avoid stubbing his toes when he walks because he really hates stubbing his toes.)
好.现在,您只需单击屏幕即可保持良好状态,让我们继续此精灵的其余部分.我想您知道这个主意,因此让我们看看您是否可以猜测我们如何添加另一个分支.给脾气暴躁的左撇子-(OK. Now, you just click on the screen to leave good enough alone, and let’s move on to the rest of this sprite. I think you get the idea, so let’s see if you can guess how we add another limb. To give Grumpy a left-)臂(arm),首先转到肢体组合框并选择(, first go to the limb-combo box and select)躯干(torso).您可能已经注意到,当鼠标靠近时,脾气暴躁的右眼会出现一个小圆圈.如果将鼠标移到脾气暴躁的人的中心附近(. You might have noticed a small circle in Grumpy’s right-eye when the mouse is near. If you move the mouse close to the center of Grumpy’s)**头(head)**图片,然后圆圈将再次出现,并通过单击其中的屏幕,您将选择他的(image, then the circle will appear again, and by clicking the screen there, you’ll have selected his)头(head).您不能使用master-limb来做到这一点,所以您要做的就是从组合框中选择它.拥有"(. You can’t do that with the master-limb, so what you’ll have to do is just pick it out of the combo box instead. Once you have the ‘)躯干(torso)",然后点击"创建新肢体"菜单选项,然后添加肢体"(’ selected, you click on the ‘Create New Limbs’ menu option and add the limb ‘)手臂(远)(arm(far))‘以铰链名称’(’ with the hinge name ‘)肩膀(远)(shoulder(far))'(您可以随意命名,但是稍后要查看它的镜像时,远和近可能比左右更容易使用).(’ (you can name it whatever you like, but far and near might be easier to use than left and right when you’re looking at a mirror image of it later on).)
现在,您可以完成设置肩部铰链弧度坐标的过程.(Now, you can go through the process of setting the rad-coordinates for the shoulder hinge.)
在这一点上,我们有一个不同的问题.的(At this point, we have a different problem. The)**左臂(left arm)**需要在远方(needs to be on the far side of the)躯干(torso),但是程序正在绘制(, but the program is drawing the)躯干(torso),然后添加(, then adding the)**头(head)**并绘制(and drawing the)**左臂(left arm)**持续(last),这意味着(, which means the)**左臂(left arm)**被画在其他所有东西之上.(is drawn on top of everything else.) 我们怎么解决这个问题?!(How ever shall we fix this?!?) 别担心.只需单击"肢体-编辑绘制顺序"菜单选项,就会出现一个新的列表框.(No worries. Just click on the Limbs - Edit Draw Sequence menu option, and a new list box will appear.)
此列表框向您显示每次鼠标移至精灵上方时将最终图像显示在屏幕上之前绘制肢体的顺序.您可以通过在列表中单击要上移或下移的肢体,然后按键盘上的上/下键来四处移动,来轻松地轻松编辑订单,或者也可以使用鼠标滚轮.因此,继续并点击(This list box shows you the order in which the limbs will be drawn before the final image is put on the screen every time the mouse moves over your sprite. You can edit the order easily enough by clicking on the limb you want to move up or down in the list and then moving it around by pressing the up/down keys on your keyboard, or alternately, you can use the mouse-wheel. So, go ahead and click on the)**手臂(远)(‘arm(far))**在此列表框中输入"肢体",然后用鼠标滚轮或按键将其移到顶部,您将看到屏幕上的图像将得到相应的校正.(’ limb’s entry in this listbox, and then mouse-wheel or key-press your way to the top with it, and you’ll see the image on the screen will be corrected accordingly.)
好的.你有图像.您有编辑器.来吧!没有人可以再次将Humpty-Dumpty重新组合在一起,但也许您可以在这里组装ol’Grumpy.(Alright. You have the images. You have the editor. Get to it! No one could put Humpty-Dumpty back together again, but maybe you can assemble ol' Grumpy here.)
动画(Animation)
如果您不能设置精灵的动画,那有什么意义呢?不多.您不妨拍照.我有一些老项目,它们具有很多的播放深度和一些很酷的功能,但是我那时(两个月前!)使用的动画并不比简笔画更好.因此,我着手制作此Sprite Editor.有了它,您现在也可以为自己的精灵设置动画,并为您的项目增添活力.为此,您首先需要创建一个新的"配置".事后看来,我认为"动画"是个更好的名字,但是当我建立一种将所有肢体的铰链角度值存储到数组序列中的方式时,我将它们称为配置,然后可以调用它们来设置铰接四肢的角度并将其图像置于预定的"配置"中.(What’s the point of having a sprite if you can’t animate it? Not much. You might as well just take a picture. I’ve got a few old projects that have a lot of depth of play and some cool features, but the animation I was using back then (two months ago!) was no better than stick figures. So, I set out to make this Sprite Editor. And with it, you too can now animate your own sprites and put a bit of life into your projects. To do this, you first need to create a new ‘Configuration’. In hindsight, I think ‘Animation’ would have been a better name for this, but I called them Configurations when I was setting up a way to store hinge angle values for all the limbs into sequences of arrays which could then be recalled to set the hinge angles of the limbs and place their images into pre-determined ‘configurations’.) 但实际上,它们是动画.(But really, they’re animations.) 让我们开始吧.没有时间浪费.只需继续并单击"配置"下拉菜单上的"新建"菜单项,然后继续吧!(Let’s get to it. No time to waste. Just go ahead and click that ‘Create New’ menu item on the ‘Configurations’ drop-down menu, and get a move on!) 创建新配置后,另一个组框将出现在自启动以来一直出现在屏幕上的铰链参数组框下方.(When you’ve created a new Configuration, another group-box will appear below the Hinge parameters groupbox that’s been on the screen since start-up.)
上图显示了标有"配置参数"的组框.而且,这里没有什么令人恐惧的.首先,您将看到新配置的名称为"新配置",并且还将注意到相同的默认名称出现在屏幕顶部标记为"所选配置"的最右侧组合框中.因此,您要做的第一件事就是将该配置的名称更改为" walk".然后,按Enter键以记录更改.您可以随时更改配置的名称,只是要确保不要给任何配置使用相同的名称,否则会遇到麻烦.(The image above shows the groupbox labeled ‘Configuration Parameters’. And, there’s nothing frightening here. First, you’ll see that the new configuration’s name is ‘New Configuration’, and you’ll also notice that the same default name appears in the right-most combo box at the top of the screen labeled ‘Selected Configuration’. So, the first thing you’ll want to do is change the name of this configuration to ‘walk’. Then, press Enter to record the change. You can change the configuration’s name at any time, just be sure not to give any of your configurations the same name as another one, or you’ll get into trouble.)
在配置名称下方,您还有另一个"绘制序列"列表框.这样做的原因是,您可能不希望为给定的配置绘制特定的肢体.假设您希望ol’Grumpy眨眼.您可以让他站在那儿,闭上双眼,眨眨眼,眨眨眼.然后,将这个新头连接到旧头上,并仅在希望他眨眼时才将其移动,方法是将眨眼头移到绘制顺序中.如果您查看杰西卡兔子(Jessica Rabbit)的精灵和一些"站立"序列,则其中一个会使她的远手伸下并划伤她的近脚.她唯一能做到的就是通过单独的动画配置,将远手靠近近足,以刮擦脚踝.(*Below the name of the configuration, you've got another Draw Sequence listbox. The reason for this is that you may want to not-draw a certain limb for a given configuration. Let's say, you want ol'Grumpy to blink. You could have him stand there and blink by having an extra image of his head with his eyes closed. Then, you attach this new head to the old head, and only draw it when you want him to blink, by moving the blink-head into the draw sequence. If you look at Jessica Rabbit's sprite and some of her 'stand' sequences, one of them has her far-hand reach down and scratch her near-foot. The only way she could do that is by having a separate animation-configuration which places the far hand closer to the near foot in order for her to scratch her ankle.*) 现在还不需要打扰您,以后您将有足够的时间进行实验.现在,假设我们要在这里为我们的朋友Grumpy做一个简单的步行序列.步行动画可能是最常见的动画,YouTube上有一些视频向您展示如何绘制它们.该程序可让您轻松随意地进行动画实验,因此创建新配置并为其命名后的操作(*No need to bother you with that just yet, you'll have plenty of time to experiment later. For now, let's say we want to make a simple walk-sequence for our friend Grumpy here. The walking animation is probably the most common, and there are a few videos on YouTube to show you how to draw them. This program makes it easy to experiment with animation at will, so what you do once you've created your new configuration and name it*)**步行(*walk*)**就是说,您将四肢设置到了Grumpy在我们要生成的图像的循环序列开始处所处的位置.为此,您可以将鼠标移到要移动的肢体上,当看到其图像中心的圆圈出现时,用鼠标单击屏幕(有时图像彼此叠置,您可能不得不求助于此)从屏幕左上方的组合框中选择所需的肢体),然后按铰链参数框中的"鼠标设置:角度"按钮(或按Alt-A),然后移动选择周围的肢体,直到找到所需的位置.然后,再次单击屏幕,它将在此处设置.(*is, you set up the limbs into a position that Grumpy will be in at the start of the cyclic sequence of images that we're (you're!) going to generate. To do this, you can move the mouse over the limb you want to move, click the screen with the mouse when you see the circle at the center of its image appear (sometimes images are on top of each other and you may have to resort to selecting the limb you want from the combo-box at the top left of the screen), and then you press the 'mouse set : angle' button in the hinge-parameters box (or press Alt-A), and you move the selected limb around until you've got it where you want. Then, click the screen again, and it'll be set there.*) 但是动画序列需要不止一个图像,因此您必须随行添加更多图像.这就是下面的按钮"添加"和"删除"(*But an animation sequence takes more than one image, so you'll have to add more as you go. And that's what the buttons 'add' and 'del' below*)**配置参数的图纸列表顺序(*Configurations Parameter's Draw List Sequence*)**是给.因此,您只需单击"添加"按钮,新的"步骤"将添加到您的序列中.然后,您可以使用"动画"切换按钮循环浏览每个步骤,也可以使用"步骤"按钮一次"逐步"浏览它们.该按钮的名称与铰链角度值的配置数组名称相同,铰链角度值描述了精灵的位置.因此,配置中的"步数"反映了动画中的图像数.(*are for. So, you just click the 'add' button, and a new 'step' will be added to your sequence. You can then cycle through each step using the 'animation' toggle button, or 'step' through them one at a time using the 'step' button. The name of this button is the same as the name of the configuration's array of the hinge angle values which describe the position of your sprite. So, the number of 'steps' in your configurations reflects the number of images in your animation.*) 您只需要将肢体放置在每个步骤所需的任何-配置-中,然后修改动画,直到获得所需的效果即可.(*You just need to place the limbs in whatever -configuration- you want for each step, and tinker with your animation until you get what you like.*) 组装好精灵后,创建新动画比技术知识需要更多的艺术技能,尽管在互联网上有一些很好的链接可以帮助您,但这实际上是您可以从实践中学到的东西.试一试,看看您会想到什么.(*Once you have your sprite assembled, creating a new animation requires more artistic skill than technical knowledge, and that's just something you learn with practice, though there are a few good links on the internet that can help you with this. Play around with it and see what you come up with.*) 的(*The*)
NumberUpDown" del"键旁边的对象可让您选择要编辑的所选配置中的哪个步骤,而" step"按钮的作用类似于菜单上的"向上"箭头(*object next to the 'del' key lets you choose which step in the selected configuration you're editing, and while the 'step' button acts like the 'up' arrow on the*)
NumberUpDown` 对象,“步进"按钮也将循环到零,而"向上"箭头则不循环.(object, the ‘step’ button also cycles around to zero while the ‘up’ arrow does not.)
您需要在这里了解一些有趣的事情.您可以选择使用屏幕底部的水平滚动条旋转整个精灵.如果要让精灵在动画序列中进行翻转,则必须以空白图像作为主肢开始您的精灵.通过(There are a couple of interesting things you need to know here. You have the option of rotating the entire sprite using the horizontal-scroll bar at the bottom of the screen. If you want your sprite to do flips in your animation sequences, then you’ll have to start your sprite off with a blank image as your master-limb. By)**空白图片(blank image)**在这里,我的意思是一个很小的图像,它隐藏在另一个图像后面,2x2像素是完美的.在脾气暴躁的情况下,您可以将此2x2位图用作脾气暴躁的master-limb,然后使用默认参数将” master-hinge"连接到(here, I mean an image so small it hides behind another image, 2x2 pixels is perfect. In the case of Grumpy, you use this 2x2 bitmap as Grumpty’s master-limb, and then have a ‘master-hinge’ with default parameters connecting it to his)躯干(torso),然后您就可以旋转他.如果希望动画向上/向下移动而不实际更改代码中精灵的位置,则需要额外的一个来创建水力效果.(, and then you’ll be able to rotate him. You’d need an extra one to create a hydraulics effect if you want the animation to move up/down without actually changing the sprite’s location in your code.)
另外,您可以翻转图像以获得原始配置的镜像.如果您是惰性类型,这会使事情变得容易得多.我知道如果我脾气暴躁的人朝着正确的方向走,我就不会再费心教他如何走路,因为我们不需要看到他的另一端,所以我只想说"脾气暴躁的人,右",然后我让用户误以为他通过使用"水平"镜子将图像翻转来向左走.方便,只要您不必解释为什么他的两面都一样.只需使用Sprite-Editor上的Flip-H和Flip-V复选框即可查看其外观.请注意,这仅适用于配置.(Also, you can flip the image around to get a mirror image of your original configuration. This makes things a lot easier if you’re the lazy type. I know if I have Grumpy walking in the right direction, I’m not going to bother teaching him how to walk another way, because we don’t need to see the far-side of him, so I just say ‘Grumpy walk-right’, and then I fool my user into believing he’s walking left by using a ‘horizontal’ mirror which flips the image around. Handy, so long as you don’t have to explain why he’s the same on both sides. Just use the Flip-H and Flip-V check boxes on the Sprite-Editor to see what this looks like. Mind you, this only works for configurations.)
东西(Something)很重要(very important)这是"使用缓存图像"复选框.在编辑精灵时,没有真正的理由使用缓存的图像.(here is the ‘use cache image’ check box. There is no real reason for you to use cached images while you’re editing your sprite.)
首先让我解释一下"缓存的图像".加载精灵时,它将从文件中获取其基础图像,然后为每个肢体生成16个旋转图像,而不会创建将它们组合在一起的任何输出图像.然后,当一个配置被调用到屏幕上时,精灵在再次创建之前,测试它的缓存以查看它是否已经为该特定配置/步骤/镜像收集了适当的肢体图像.如果它在缓存中有一个,并且调用函数没有强迫它创建一个新的,则不会创建一个新的.它只会发送一份已存储在内存中的副本.当精灵在屏幕上跳舞并进行其他一百件事时,这非常适合最终用户使用,并且您在提高速度方面处于领先地位,并尽一切可能使事情更快地工作.但是,当您进行编辑时,您希望看到所做的更改,因此,尽管该复选框已存在^没有真正的理由让您使用它(Let me explain ‘cached images’ first. When your sprite loads, it has its base images from the file, and then generates the 16 rotated images for each limb without creating any output images of them combined together. Then, when a configuration is called to the screen, the sprite tests its cache to see if it has gathered the appropriate limb images already for this particular configuration/step/mirror, before creating it again. If it has one in cache and it’s not forced by the calling function to create a new one, it won’t create a new one. It’ll just send a copy of the one it has stored in memory. This is great for end-use when you’ve got your sprite dancing around the screen with a hundred other things going on, and you’re cutting edges for speed, and doing what you can to get things working faster. But, while you’re editing, you want to see the changes you’ve made, so, though that check-box is there … there is no real reason for you to use it)在编辑过程中(during the editing process).(.)
使用代码(Using the code)
一旦创建并保存了精灵,将他显示在屏幕上就非常容易.这是我为演示该项目的输出而编写的课程.(Once you have your sprite built and saved, putting him on the screen is really very easy. Here’s the class I wrote to demonstrate this project’s output.) classSmurf
有个(has a) loc Point
,旅行" delta-X"值,(, a travel ‘delta-X’ value, a) configStep
整数变量,镜像和子画面.当…的时候 ‘(integer variable, a mirror, and a sprite. When the ‘) animate(ref Bitmap bmp)
‘函数被调用,位置偏移(’ function is called, the location is shifted by) intDX
直到到达屏幕边缘为止(until it reaches the edge of the screen, at which point,) intDX
取反,并设置镜像值以反映此更改(无双关),最后,通过调用将子画面放到屏幕上(is negated and the mirror value is set to reflect this change (no pun intended), and finally, the sprite is put to the screen with a call to) classSprite
的功能(’s function) PutConfigurationToScreen
.(.)
public class classSmurf
{
public Point loc;
public int intDx = 25;
public Sprite.classSprite mySprite;
Sprite.enuMirror mirror = Sprite.enuMirror.none;
int intConfigStep = 0;
public enum enuSmurf_Configurations
{
walk,
_numCon
}
public classSmurf()
{
Sprite.classSpriteMaker cLibSpriteMaker =
new Sprite.classSpriteMaker();
string strDir = System.IO.Directory.GetCurrentDirectory();
strDir = strDir.ToUpper();
strDir = strDir.Replace("BIN\\DEBUG", "RESOURCES\");
strDir = strDir.Replace("BIN\\RELEASE", "RESOURCES\");
mySprite= cLibSpriteMaker.loadSprite(strDir + "Smurf (small).spr");
}
public void animate(ref Bitmap bmp)
{
loc.X += intDx;
loc.Y = bmp.Height - 100;
if (loc.X > bmp.Width || loc.X < 0)
intDx *= -1;
mirror = intDx > 0 ? Sprite.enuMirror.none :
Sprite.enuMirror.horizontal;
mySprite.putConfigurationOnScreen(
ref bmp, // image on which to draw sprite
(int)enuSmurf_Configurations.walk,
intConfigStep, // step
loc, // loc of smurf's center (torso)
0, // sprite rotation in radians
1.0, // size factor
mirror, // mirror type
false); // force draw image
/// increment the configuration step
// and loop to zero when reached limit
intConfigStep = (intConfigStep + 1) %
mySprite.Configurations[
(int)enuSmurf_Configurations.walk].steps.Length;
}
}
Sprite Editor的Configuration菜单中有一个" Reorder Configurations"项,可让您对配置进行重新排序(The Sprite Editor’s Configuration menu has a ‘Reorder Configurations’ item which allows you to reorder the configurations)和(and)创建可以在项目中使用的示例枚举列表.将其从Sprite-Editor剪切粘贴到您的代码中,就可以使用Sprite了.当您的精灵只有一个配置时,您不会感到困惑,因为它始终使用配置索引" 0",但是当您拥有一个具有很多不同配置的精灵时,将它们保持在可管理的顺序中并且创建准确的枚举列表可能会带来问题,因此您会发现此功能非常方便.(create a sample enum list you can use in your project. Cut’n-paste this from the Sprite-Editor to your code, and you’re all set to use your sprite. When you have a sprite that has only one configuration, you can’t get confused because it’ll always be using configuration index ‘0’, but when you have a sprite with a lot of different configurations, then keeping them in a manageable order and creating an accurate enum list can pose problems, so you’ll find this feature quite handy.) 考虑下面从杰西卡`兔子(Jessica Rabbit)精灵获取的示例:(Consider the example below taken from the Jessica Rabbit sprite:)
public enum enuJessica_Rabbit_Configurations
{
Walk_new,
Shoot_Straight,
Shoot_High,
Shoot_Low,
Shoot_Up,
Shoot_Down,
standStill,
Stand_flick_far_hair,
Stand_hold_gun_up,
Stand_flick_near_hair_with_gun,
Stand_fix_far_shoe,
Stand_fix_near_shoe,
Stand_scratch_far_calf_with_gun,
stand_scratch_head,
stand_swipe_near_arm_with_far_hand,
go_to_sleep,
wake_up,
_numCon
}
classSprite
还包括一个(also includes a) PutSpriteOnScreen()
功能类似于(function which acts in a way similar to) PutConfigurationOnScreen()
确实可以,但是动态改变铰链角度所涉及的复杂性比起初想象的要困难,因此您可能希望坚持使用预定义的配置.(does, but the complications involved in altering the hinge angles on the fly are more difficult than at first imagined, so you might want to stick to pre-defined configurations.)
碰撞检测可能是您要考虑的问题.不幸的是,这个项目并没有走太远.但是,您可以使用"(Collision detection may be something you want to consider. Unfortunately, this project does not go very far with that; however, what you can do is use the ‘) getLimbPos()
‘功能.此功能以多种方式超载,因此您有很多选择可以调用它.而且,它所做的就是为您提供您要的肢体相对于主肢的位置.调用该函数时,您必须告诉它什么角度和镜像,或者让它假设angle =0和mirror =none,然后(’ function. This function is over-loaded several ways so you have a lot of options to call it. And, what it does is give you the location of the limb you asked for relative to the master-limb. When you call the function, you have to either tell it what angle and mirror, or let it assume angle=0 and mirror=none, and the) Point
您获得的值是相对于主肢位置的.请注意,当您致电(value you get is located relative to the position of the master limb. Note that when you call) PutConfigurationToScreen()
(要么((or) PutSpriteToScreen()
),您可以将此功能发送到屏幕上您要放置的点,并且该位置与主肢位置的中心相同.因此,如果您将精灵放置在(453,148)处,且结果为(), you send this function the point on the screen you want to put it, and that location is the same as the center of the master-limb’s location. So, if you put the sprite at (453, 148) and the result of) getLimbPos()
是(47,52),则您要寻找的肢体在(500,200).(is (47, 52), then the limb you’re looking for is at (500, 200).)
这是我的一个例子(Here’s an example out of my) 暗夜魔王(Night-Stalker) 项目,此类中的精灵名称为(project, the sprite’s name in this class is) Sprite
,最后放置到屏幕上的配置由其公共变量type指向(, and the last configuration put to the screen is pointed to by its public variable of type) classSpriteConfiguration
称为"(called ‘) conCurrent
‘(当前配置-(’ (current configuration, -)不(not)- 一种(- a)**不变(constant)**输入前缀,您会发现"(type prefix, you’ll find ‘)欣(hin)’,'(’, ‘)林(lim)‘和’(’, and ‘)骗局(con)‘通常用作(’ are used regularly as prefixes in the) Sprite
命名空间):(namespace):)
Point ptGunHand = Sprite.conCurrent.getLimbPos("gunhand(near)",
mirror,
intConfigStep,
0);
Point ptBulletLoc = new Point(ptLoc.X + ptGunHand.X,
ptLoc.Y + ptGunHand.Y);
很容易,对吧?(Easy enough, right?)
此特定调用中的第四个参数(The fourth parameter in this particular call to) getLimbPos()
是表示0-15之间方向的整数值,其中0是东方.这些与存储在每个肢体的基本图像数组中的图像匹配,并在检索通过以下方式存储的缓存图像时用作该数组的索引:配置,镜像,阶梯和方向(精灵旋转0-15).尽管您可以指定任意角度,但是这些角度会在代码中运行,然后进行校准以适合从原始基础图像旋转的16个图像的限制.更改项目以为每个肢体生成32\100或任意数量的所需图像,对于任何决心这样做的人来说都不是完全无法企及的,而且您会以更平滑的动画获得更好的输出,但代价是负担增加时间和增加的内存需求,当今大多数计算机都可以处理.(is an integer value representing a direction between 0-15, where 0 is east. These match the images stored in each limb’s base image array, and are used as that array’s indices when retrieving the cache image stored by: configuration, mirror, step, and direction (of sprite rotation 0-15). Though you can specify any angle you like, these angles are run through the code and then calibrated to fit in with the limit of 16 images rotated from the original base images. Changing the project to generate 32, 100, or whatever number of images you like for each limb is not altogether out of reach for anyone who is determined to do so, and you’d get better output with smoother animation at the cost of increased load time and increased memory requirements, which most computers today can handle.)
所以在这一点上,为子画面设置动画只是调用(So at this point, animating your sprite is just a matter of calling) PutConfigurationToScreen()
以及自己跟踪位置以及要使用的配置和配置步骤.(and keeping track of the location yourself, along with which configuration and configuration step to use.)
放置,动作和步骤.(Place, action, and step.)
但是它如何工作?(But how does it work?)
您会看到,脾气暴躁是一个相对简单的精灵,有五个分支,所有分支都附着在同一主分支上,但是如果您将其他一些精灵加载到(You can see that Grumpy is a relatively simple sprite with five limbs, all attached to the same master limb, but if you load some of the other sprites in the)**sprites.zip(sprites.zip)**文件,您会发现这些Sprite所具有的复杂性确实没有任何限制.而且,由于在运行时生成的图像存储在Sprite的缓存中,因此,一旦将图像组合到任何新的输出位图中,就不会降低动画的速度.(file, you’ll see that there really is no limit to the complexity these sprites can have. And, since the images which are generated at run time are stored in the sprite’s cache, animation isn’t slowed down by the process of assembling the images into any new output bitmaps once they’re already cached.) 但是它如何工作?(But how does it work?)
旋转原始图像(Rotate original image)
程序在加载精灵时要做的第一件事是获取每个肢体的原始基础图像并将其旋转三遍,然后将这四个图像存储在一个数组中,其中(The first thing the program does when it loads a sprite is take each limb’s original base image and rotate it three times and then store these four images in an array where) image[0]
是原始的,(is the original,) image[1]
与image [0]相同,但旋转了22.5°,(is the same as image[0] but rotated 22.5°,) image[2]
是相同的(is the same as) image[0]
但旋转了45°,并且(but rotated 45°, and) image[3]
是相同的(is the same as) image[0]
但旋转了22.5°(but rotated 22.5° more than) image[2]
与原始角度成67.5°(to 67.5° from the original.)
您可以在下图中看到三个不同的位图1\2和3:(You can see the three different bitmaps 1, 2, and 3, in the image below:)
这是在(This is done in) classSpriteLimb
的功能(’s function) generateBaseFourImagesFromOriginal()
.使用.NET的本机翻转和旋转其余16张图像(. The rest of the 16 images are flipped and rotated using .NET’s native) Bitmap
功能’(function ‘) RotateFlip()
‘在同一个班级的其余12个方向中均被调用’(’ which is called for each of the remaining 12 directions in the same class’) getLimbImageFromArray_ByDir()
功能.(function.)
在下一个图像中(在文本中进一步向下),您将看到Grumpy的右臂和躯干被装箱.这些框可帮助显示每个肢体的位图在粘贴到屏幕上之前粘贴到最终图像的位置.方便放置每个肢体的计算的方法是使用位图的中心作为所有计算的参考点.肢体可能会旋转,并且图像大小可能会随着肢体可能采取的每个可能的角度而波动,但是图像的中心仍然是铰链所在的支点,随后放置了奴隶肢体.为此,在生成旋转图像时需要计算适当的图像尺寸.(In the next image (further down in the text), you’ll see Grumpy’s right arm and torso are boxed. These boxes are there to help show where each limb’s bitmap was pasted onto the final image before it was put to the screen. What facilitates the calculations involved in placing each limb is the use of the center of the bitmap as the point of reference for all calculations. The limb may rotate, and the image size may fluctuate with each possible angle which the limb may take, but the center of the image remains the fulcrum about which the hinges are located and slave limbs are subsequently placed. To do this, the appropriate image sizes need to be calculated when generating the rotated images.)
旋转影像(Rotating images)
实际的22.5度旋转全部在函数中完成:(The actual 22.5 degree rotations are all done in the function:)
Bitmap rotate(Bitmap bmpInput,
double dblAngleRotation,
ref classMath cLibMath)
((() classMath
此处引用的内容包含在(referenced here is included in the) Sprite
命名空间.)(namespace.))
这个(This) rotate()
该函数的工作方式是首先计算输出图像可能需要的最大可能大小,然后创建一个临时位图,以确保输出图像适合其中.然后,它扫描原始基础图像上的每个像素,并使用cos/sin trig函数一次旋转一个像素,然后将旋转位置的原始像素粘贴到新的位图上.为了跟踪所需的大小,它保留了两个(function works by first calculating the maximum possible size that may be needed for the output image, and it creates a temporary bitmap into which the output image is guaranteed to fit. Then, it scans each pixel on the original base-image, and rotates them one at a time using the cos/sin trig functions, and pastes the original pixel at a rotated location onto the new bitmap. To keep track of the required size, it retains two) Point
称为"(variables called ‘) ptTL
‘和’(’ and a ‘) ptBR
“,它们会跟踪距原稿最远的左上角和最右下角,以定义包含输出图像的最小矩形.并且,当扫描完成时,所需的大小就会知道.(’, which keep track of the farthest Top-Left corner and the farthest Bottom-Right corner from the original, which defines the smallest rectangle which contains the output image. And, when the scan is complete, the required size is known.)
但是…(But …)
旋转的图像中经常有孔.(there are often holes in the rotated image.)
这些空洞是由于像素位置和整数的量化性质所致,这些整数是通过对浮点值进行四舍五入而得出的,该浮点值涉及旋转方案中涉及的三角方程式返回给您.在尝试了几种不同的算法来修复这些孔之后,我发现一种方法是扫描所得图像,再次查找孔,然后计算孔周围的颜色,看看是否可以就什么达成共识该孔应采用的颜色.由于在任何孔附近有8个像素,因此如果至少有2个相同,那就足够了.较高的值可能更安全,但是我发现这似乎很好.当我撰写本文并查看代码以回想起我所确定的价值时,我很难相信(These holes are due to the quantized nature of pixel locations and the whole-numbers that are derived by rounding the floating point values which the trigonometric equations involved in the rotation scheme spew back at you. After trying a few different algorithms to fix these holes, I found that one way to do this is to scan the resultant image, again looking for holes, and then count the colors around the holes and see if we can get a consensus as to what color this hole should take. Since there are 8 pixels immediately around any hole, if a minimum of 2 are the same, that’s just good enough. A higher value might be safer, but I’ve found that this seems to be fine. As I write this article and look at the code to recall what value I had settled on, I find it difficult to believe that)2!(2!)这是足够的,因为如果没有此测试,通常会产生创建多米诺骨牌效果的错误,该效果会填充到旋转图像中裸露的整个区域.但是,如果代码显示2,则该代码必须正确,因为它运行良好,没有理由更改它.(was enough, because without this test, there were often errors that created a domino effect which filled in entire regions of the rotated image where the original was left bare. But, if the code says 2, then the code must be right, because it’s been working well and there’s no reason to change it.)
但是,如果孔周围的所有像素均具有不同的颜色(如JPEG或GIF经常会发生这种情况),则在将孔设置为此处发现的颜色之前,通过反向触发并回头看基础图像来检查原始像素. .这种方法更确定,但速度慢得多.(However, if all the pixels around the hole are of a different color, as often may happen with JPEGs or GIFs, the original pixel is examined by doing the reverse trig and looking back at the base image before setting this hole to the color found there. This method is more certain, but much slower.)
放置四肢(Placing the limbs)
功能:(The function:)
public udtBmpAndPtTLInfo getImageOnBitmap(double DisplaySize,
enuMirror mirror,
classSpriteLimb[] limDrawSequence)
返回此处定义的结构类型的元素:(returns an element of the structure type defined here:)
public struct udtBmpAndPtTLInfo
{
public Bitmap bmp;
public Point ptTL;
public udtConfigStepLimbPosition[] limbPositions;
}
返回的位图是可以保存此配置的图像的最小位图.(The returned bitmap is the smallest bitmap that can hold the image of this configuration at the) DisplaySize
必需的,而(required, while the) Point
告诉调用函数图像的中心位置,以便可以将其放在屏幕上的正确位置.这是必需的,因为精灵的肢体可以在任何方向上延伸,从而使输出图像在一侧或另一侧变大,并且您仍然需要知道主肢的中心在哪里.的(tells the calling function where the center of the image is so that it can be placed on the screen at the correct location. This is needed because the sprite’s limbs can be extended in any direction, making the output image larger on one side or the other, and you still need to know where the master limb’s center is. The) GetLimbPosition()
上面提到的功能利用了(function mentioned above makes use of the) limbPositions
可以通过将它们与映像一起存储在配置步骤的缓存中来看到,因为这是缓存中保留的实际数据结构.(seen here by storing them in the configuration step’s cache along with the image, as this is the actual data structure which is held in the cache.)
为了将每个肢体放置在正确的位置,该函数首先初始化返回值(In order to place each limb at its correct location, the function first initializes the return value’s) ptTL
到旋转的主肢图像中心.然后,一旦通过此操作放置了主肢((to the rotated master limb image’s center. Then, once it has placed the master limb by doing this (recall that) ptTL
描述相对于返回的位图的主肢位置),它调用一个递归函数,该函数接收"主肢”(该特定函数调用将其视为主肢的任何肢),放置此"主肢"的从属肢,并跟踪该过程所需的最小输出位图,然后递归遍历每个位图,直到达到每个肢体的末端并返回每个调用.(describes the master’s limb position with respect to the returned bitmap), it calls a recursion function that receives a ‘master-limb’ (any limb it treats as master for that specific function call), places this ‘master-limb’s’ slave limbs, and keeps track of the minimum output bitmap required in the process, then recurses through each of them until the extremities of each limb are reached and each call has returned.)
一个示例可以为不熟悉递归的人阐明此过程:首先,将对Grumpy躯干的引用发送到该函数.然后,躯干调用在每个从属肢体中循环,并针对每个躯干,在给定躯干的位置和角度的情况下,将它们(从属肢体)放置在其铰链角放置它们的位置,然后递归到每个这些肢体中.如果Grumpy一直拿着棍棒,那么涉及棍棒手的递归将放置棍棒并再次调用自身,将棍棒发送为"奴隶",没有奴隶,根本不做任何事情就掉下来了.对于更复杂的精灵,躯干从"手臂"开始为其每个奴隶递归,然后手臂从"前臂"开始通过其每个奴隶递归,并且前臂对"手",然后手重复"枪支"的处理过程,如果您想弄清楚问题,则枪支递归"消音器".而且,当到达消音器并且没有从动件时,程序流将退回原路:枪,手,前臂,手臂和躯干,此时躯干循环到另一条手臂,依此类推. ,直到躯干的所有奴隶肢体及其所有奴隶肢体都已放置.(An example may clarify this process for those not familiar with recursion: first, a reference to Grumpy’s torso is sent to the function. The torso call then cycles through each of its slave limbs, and for each one, given the torso’s position and angle, places them (the slave limbs) at the location which their hinge-angle puts them, and then recurses into each of these limbs. Had Grumpy been carrying a stick, then the recursion involving the stick-hand would place the stick and call itself again, sending the stick as the ‘master limb’, which has no slave and simply falls out without doing anything. In the case of a more complex sprite, the torso recurses for each of its slaves, starting with the ‘arm’, then the arm recurses through each of its slaves, starting with the ‘forearm’, and the forearm does the same with the ‘hand’, and the hand repeats the process for the ‘gun’, and if you want to belabor the issue, the gun then recurses for the ‘silencer’. And, when the silencer is reached, and it has no slave, then the program flow falls back the way it came: gun, hand, forearm, arm, and torso, where the torso now cycles through to the other arm, and so forth, until all of the torso’s slave limbs and all of their slave limbs have been placed.)
此时,图像仍然不在位图上,这是因为我们无法将它们正确地放置在位图上(At this point, the images are still not on a bitmap, and that’s because we can’t place them on a bitmap in the correct) DrawSequence
直到我们知道他们都去了哪里.如果我们想在躯干之前画手,那么我们需要知道手在哪里,但是直到知道前臂在哪里以及手臂在哪里才知道.因此,既然我们知道了,我们就继续按照顺序绘制序列中的每个分支进行扫描,并使用(until we know where they all go. If we want to draw the hand before the torso, then we’ll need to know where the hand goes, but we don’t know that until we know where the forearm goes and where the arm belongs. So, now that we know, we just go ahead and scan through each limb in the ordered Draw Sequence, and use a call to) graphics.DrawImage()
对于每个肢体,将每个肢体的正确角度图像放置在预先计算的位置.(for each limb, placing the correct angled image of each limb at the pre-calculated location.)
下图可能有助于描述奴隶肢体在屏幕上的放置方式.首先,每个肢体的主铰链相对于主肢的中心和角度定位.请记住,我们正在处理径向坐标,并且原始图像的中心始终位于旋转图像的中心,因此,如果铰链的主径向坐标为pi且连接到铰链主端的肢体为同样以pi弧度旋转,则铰链位于距主肢图像中心2 pi弧度一定距离处(该距离以像素为单位,并保持在铰链的主弧度坐标中,然后乘以的(The image below may help describe how the slave limbs are placed on the screen. First, each limb’s master hinge is positioned relative to the master limb’s center and angle. Remember that we’re dealing with radial coordinates, and the center of the original image is always at the center of the rotated image so that if the hinge’s master-radial coordinate is pi and if the limb attached to the master’s end of the hinge is also rotated by pi radians, then the hinge is located a certain distance away from the center of the master limb’s image at an angle of 2pi radians (the distance being measured in pixels, and kept in the hinge’s master rad-coordinate, then multiplied by the) drawSizeFactor
该函数接收作为输入参数).将铰链放置在主肢上之后,我们可以通过将主肢的角度与铰链角相加来计算从属肢的角度,从而计算出从属肢的位置,然后将从属中心置于距铰链位置如此多的像素处等于新角度加上铰链从动rad坐标的角度的角度.这与将所有三个角度相加相同:主肢的角度,铰链的从属rad角度和铰链的"旋转"角度(或"构形"角度).这里的英语听起来有些扭曲,我很难解释得更清楚,但是如果您看一下代码并为自己画一幅画,可能会得到一个更好的主意.(the function receives as an input parameter). Once we have placed the hinge on the master limb, then we can calculate the slave limb’s location by adding the master limb’s angle with the hinge angle to get the slave’s angle, and then place the slave’s center so many pixels away from the hinge position at an angle equal to the new angle plus the hinge’s slave rad-coordinate’s angle. This is the same as adding all three angles: the master limb’s angle, the hinge’s slave rad angle, and the hinge’s ~swivel~ angle (or Configuration angle). The English here sounds twisted, and I find it difficult to explain more clearly, but if you look at the code and draw yourself a picture, you might get a better idea.)
你能说"枚举"吗?(Can you say ‘enumeration’?)
但是,如果您不知道如何在最终产品中称呼所有这些,那么这一切有什么意义呢?进行这样的项目时,部分问题是必须易于访问配置而不会造成混乱.您可以随时使用(But, what’s the point of all this if you can’t figure out how to call it all in your end-product? Part of the problem in making a project like this is that the configurations have to be accessed easily and without confusion. You can always use the) getConfigurationByName()
只要您想做某事就可以使用,然后继续操作,但是您还可以使用Sprite Editor的"重新排序配置"菜单选项,如下所示:(function whenever you want to do something, and if that suits you, then go ahead, but you can also use the Sprite Editor’s ‘reorder configurations’ menu option, which looks like this:)
如果您的精灵具有类似的配置,那么重新排列它们的不同配置以使它们都匹配可能会更容易,但是如果您没有首先想到的话,那么您会陷入混乱之中稍后在没有此方便功能的情况下执行此操作.脾气暴躁的只有一种配置,如右图所示(If you your sprites have similar configurations, then it may be easier for you to reorder their different configurations so that they all match, but if you hadn’t thought of that first, then you’d be in a bit of a mess trying to do this later without this handy feature. Grumpy’s only got one configuration, as you can see on the right above the)**好(Ok)**按钮,但如果他有更多按钮,则可以使用上述相同的鼠标滚轮/按键界面(参考"绘制序列"),此列表框可以按您希望的任何顺序对配置列表进行重新排序,然后仅对所有精灵使用一种枚举类型.(button, but if he had more, you’d be able to use the same mouse-wheel/key-press interface described above (in reference to the Draw Sequences), with this listbox to reorder the lists of configurations in whatever order you like, and then use only one enum type for all your sprites.) 这里的"将枚举复制到剪贴板"按钮的作用与突出显示左侧文本框的内容然后按Ctrl-C相同,因此您可以将剪贴板上的副本粘贴到项目中.(The ‘copy enum to clipboard’ button here does the same thing as highlighting the contents of the left textbox and then pressing Ctrl-C, so that you have a copy on your clipboard ready to be pasted into your project.)
演示和图形编辑器(Demo and Graphics Editor)
本文附带的演示演示了如何使用Sprite,尽管在剪切图像并将其转换为Sprite时,Graphics Editor的泛洪填充(到边框)功能特别方便,但它并不是最好的图形编辑器,并且我建议除其他原因外使用它(The demo included with this article demonstrates how to make use of the sprites, and though the Graphics Editor’s flood fill (to border) feature is particularly handy when cutting up images and turning them into sprites, it is not the best graphics editor around, and I recommend using it for no other reason than this)**洪水填充(flood fill)**和(and the)**拉直(straighten)**特征.您可能需要在大多数工作中使用MS-Paint,然后将图像加载到此GraphicsEditor中仅用于进行拉直和填充(保存?),保存然后在MS-paint中继续.泛洪功能的工作方式如下:您可以选择一种颜色,就像在MS-Paint中一样,然后在选项面板上进行扫描,然后将鼠标移到油漆罐泛洪图标上,这是一个小图片框出现在它旁边,上面有一个红点.红点是洪水填充将设置其绘制区域的颜色,您可以从调色板中选择所需的颜色后,通过单击它来进行设置.然后,此图片框旁边显示的标签使您可以在"至边框"填充模式或"相同颜色"填充模式之间切换.两者可以很方便. “上色"泛洪填充与MS-Paint中的相同,仅是"上边界"泛洪填充以均匀的颜色填充整个区域,直到被预定的边框颜色停止为止.您可以通过以下方式设置模式:在标签显示为"切换到’到边框’模式"时,单击带点的图片框旁边的标签,然后单击带有点的图片框的边框并进行设置您之前选择的任何颜色.(feature. You may want to use MS-Paint for most of your work, and then load the image in this GraphicsEditor only to do your straightening and flood-fill(ing?), save and then continue in MS-paint. The way this flood-fill feature works is like this: you select a color much like you would in MS-Paint, and then by scanning over the options panel and moving the mouse to the paint can flood-fill icon, a small picture box appears beside it with a red dot on it. The red dot is the color which the flood-fill will set the region it paints to, and you set that by clicking it after having chosen the color you want from the palette. Then, the label that appears next to this picture box allows you to toggle between the ‘to border’ flood-fill mode or the ‘same color’ flood-fill mode. The two can be quite handy. The ‘to color’ flood fill is the same as in MS-Paint, and nothing more but the ‘to border’ flood-fill fills an entire region with a uniform color until it is stopped by a pre-determined border color. You can set the mode by clicking on the label next to the pic-box with the dot when the label reads “switch to ‘to border’ mode” and then clicking on the pic-box-with-the-dot’s border and setting it to whatever color you’ve previously selected.) 然后,在屏幕上单击要用点的颜色填充图像的屏幕,该颜色将一直散布直到达到边界颜色. MS-Paint没有此功能的原因可能是因为它很容易在泛洪边界上留下一个像素大小的孔,从而使泛洪能够通过并填充整个屏幕.您确实必须填补这些漏洞,但是只要"撤消"任何操作,您就可以"撤消"而无需进行纠正.(Then, you click on the screen where you want to flood the image with the color of the dot, and that color will spread until it reaches the border color. The reason MS-Paint does not have this is probably because it’s so easy to let a single pixel-sized hole in your flood-border which allows the flood to pass and fill the whole screen. You really have to plug those holes, but with ‘un-do’ anything, you do ‘do’ without wanting to be corrected.)
许可
本文以及所有相关的源代码和文件均已获得The Code Project Open License (CPOL)的许可。
C# Windows GDI+ Dev 新闻 翻译