65.9K
CodeProject 正在变化。 阅读更多。
Home

FormSprite:我爱你杰西卡!

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (10投票s)

2010年10月2日

CPOL

11分钟阅读

viewsIcon

64879

downloadIcon

769

使用 FormSprite 创建的杰西卡兔桌面宠物

jessica_screen_capture.PNG

什么是 Form Sprite?

还记得曾经在你屏幕上蹦蹦跳跳的绵羊吗?或者那些在 Windows 应用程序中用来激励你、当你看起来无所适从时出现的乐于助人的回形针?现在,你也可以使用一个名为 classSprite 的新子类 classSpriteForm 来创建你自己的可爱的爬行小东西来装点你的屏幕。你不需要下载 Sprite-Editor 就可以运行这个应用程序,但如果你想创建自己的精灵,那就必须下载。请参阅下面的参考。

位图区域

你听说过“位图区域”吗?它们通过创建位图中具有透明颜色的区域(由 (0,0) 处的位决定的颜色)来将你的按钮和窗体塑造成位图上图像的形状。那篇文章写于 2004 年,但仍然很酷,而且 bitmapRegions.cs 可以轻松地制作成一个 .dll,这样你就可以随时引用它。此项目利用它将动画角色图像转换为窗体的形状,该窗体会在屏幕上移动,就像角色精灵 strutting about 炫耀并显得很坏一样。

更新:PerPixelForm

关于“C# 中的逐像素 Alpha 混合”的文章不多,但它的作者留下了一些代码,它的效果比位图区域更好。据说位图区域已经过时,现在可以被古老的编码历史文物妥善安放了。另一位读者在下面的评论中指引我看到了这一点,所以我查看了一下,并将这个解决方案集成到了我新的 classSpriteForm 中,杰西卡看起来好多了。此外,还有一个额外的奖励是,我修复了一个bug,这个bug导致她脚疼,让她站在同一个地方,但现在不一样了,她正在 strutting about 炫耀她的道具。

FormSprite

如果你还没有阅读我的“.NET 精灵编辑器”文章,你应该去看看,因为那是你需要用来创建自己的精灵的东西。本质上,classSprite 和配套的精灵编辑器允许你将切成零散肢体的静止图像,然后在称为铰链的枢轴点重新连接这些肢体。通过以这种方式连接的精灵角色,编辑器随后用于像纸娃娃一样动画化角色,它们的肢体在关节处旋转。你将创建的图像序列记录到配置中,然后可以调用任何图像以任何配置并将其投射到屏幕上。

对于已经在使用 classSprite 的各位,你可能会对这个项目中的源代码包含对该类的升级感到兴趣。这里的新功能是通过调用函数来请求图像,而无需将其绘制到现有位图上。

public Bitmap getConfigurationImage(int intConfigurationIndex, 
                                    int intConfigurationStep, 
                                    enuMirror mirror, 
                                    bool bolForceDraw)

旧的 classSprite 迫使你为此做一些额外的工作。

至于新的类 FormSprite(在本篇文章中介绍,并且不是原始精灵项目的一部分),它利用上面提到的 perPixelAlphaBlend,允许程序员将动画角色放置在任何窗体之外的计算机屏幕上。该类继承自 System.Windows.Forms.Form,它是无边框的,并且在运行时加载到其中的精灵图像被用来塑形窗体,使其除了你正在屏幕上动画的角色之外,整个窗体都消失了。

它的主要功能是

public FormSprite()
{
  FormBorderStyle = FormBorderStyle.None;
  ShowInTaskbar = false;
  TopMost = true;
}
void drawMe()
public void drawMe(int ConfigurationIndex, 
                   int ConfigurationStep, 
                   Point ptLoc, 
                   enuMirror Mirror, 
                   double angle, 
                   double size)
public void loadSprite(string strFilename)

该类还包含几个控制角度、大小、位置、镜像、配置和配置步进的属性。每当你独立更改这些属性之一时,该类都会自动调用 drawMe() 函数,该函数将自动更新精灵的外观。你可以通过直接调用重载函数 drawMe() 并包含所有变量更改来避免这种情况。

drawMe(configIndex, configStep, ptNew, eMyMirror, 0, 1.0);

此函数设置与属性相关的值,然后调用 drawMe() 函数,该函数使用这些值来重置窗体的外观。

由于你的精灵在屏幕上行走、跳舞和战斗时,图像尺寸可能会有所不同,因此你不希望你的精灵图像的左上角位于上面示例中的 ptNew 位置。因此,FormSprite 中的 _pos 值会被调整以适应图像的大小。这是通过 udtBmpAndPtTLInfo 结构实现的,该结构告诉你从精灵图像的中心位置到 bitmap 左上角的距离。“精灵图像的中心”不是指位图的中心,而是指位图上某个特定精灵解剖部位的位置,例如心脏、胸部或骨盆。这在下面的 private 函数 PlaceMe() 中完成,因此你无需担心,只需知道 classSpriteFormPos 属性指的是精灵的 master-block 将放置在屏幕上的位置。

void PlaceMe()
{
udtBmpAndPtTLInfo udrBmpTLInfo = cSprite.Configurations[intConfiguration].steps
	[intStep].BmpCacheByMirrorType[(int)eMirror].dir[intDir];
int intGrabLimbIndex = 0;
if (cStationaryLimb != null)
{
    for (int intLimbCounter = 0; 
	intLimbCounter < udrBmpTLInfo.limbPositions.Length; intLimbCounter++)
    {
        if (cStationaryLimb == udrBmpTLInfo.limbPositions[intLimbCounter].limb)
        {
            intGrabLimbIndex = intLimbCounter;
            break;
        }
    }
    Left = _pos.X + udrBmpTLInfo.ptTL.X - 
	udrBmpTLInfo.limbPositions[intGrabLimbIndex].pt.X;
    Top = _pos.Y + udrBmpTLInfo.ptTL.Y - 
	udrBmpTLInfo.limbPositions[intGrabLimbIndex].pt.Y;
    return;
}

Left = _pos.X + udrBmpTLInfo.ptTL.X;
Top = _pos.Y + udrBmpTLInfo.ptTL.Y;
}

请注意 FormSprite 中的 limStationary。在某些情况下,你可能希望将 FormSprite 的所有动作都围绕除 MasterLimb 之外的一个肢体进行,当你这样做时,只需找到你想要的肢体并通过 cStationaryLimb 指针指向它。以杰西卡为例,此示例代码使用固定肢体来实现“抓取”例程,因此如果你单击并按住杰西卡的脚趾,她就会悬挂、扭动、踢打和抱怨,而她的脚趾会停留在你的鼠标光标下方,无论它移到哪里。这样,你就可以提起杰西卡并将她移动到屏幕的其他位置。

这是杰西卡被抓住脚时的快照。

grab_far_foot.PNG

你会发现她的远脚被粘在鼠标光标上,并会跟随鼠标光标在屏幕上的移动。

强制轮廓颜色

我收到的另一条评论建议,可以将精灵的图像绘制在黑色哑光背景上,这样在使其透明时,边缘可以具有指定的颜色。当我尝试时,我发现杰西卡发红的肤色和美丽的容貌被这个建议增加的暗边所破坏。然而,这个想法仍然很好,但不够通用。作为这次实验的结果,我在精灵类中添加了另一个名为 ColorOutline 的属性,如下所示。

SolidBrush brBack;
/// <summary>
/// set the color of sprite's outline.  default = white;
/// </summary>
public Color ColorOutline
{
  get
  {
    if (brBack != null)
      return brBack.Color;
    else
      return Color.White;
  }
  set
  {
    brBack = new SolidBrush(value);
  }
}

以及 classSprite 函数 getImageOnBitmap() 中可选的 fillRectangle() 调用

udrRetVal.bmp = new Bitmap(ptBR.X - udrRetVal.ptTL.X, ptBR.Y - udrRetVal.ptTL.Y);
using (Graphics g = Graphics.FromImage(udrRetVal.bmp))
{
if (brBack != null)
  g.FillRectangle(brBack, 
	new Rectangle(0, 0, udrRetVal.bmp.Width, udrRetVal.bmp.Height));

因此,如果你不设置你想要的特定颜色,唯一浪费的执行时间就是测试你指定颜色的存在性,而你仍然可以玩弄女孩的光彩。如果你知道我的意思,就心照不宣,眨眼吧。

classJessica

好吧,我说了我无聊,但不是无聊到要费劲画一个全新的精灵。我一直很忙,只是把这个项目东拼西凑地弄好了,想着要试试。这里是星期五的福利日,在贫民窟,我不想为了这个场合缝合一个新精灵,所以我抓起了我最喜欢的一个,我想她仍然很漂亮,足以展示她的舞姿(尽管我可能以后会教她一两支舞!)。

在之前的一篇文章中,我做了类似的事情,使用屏幕截图的电脑屏幕作为窗体背景,然后将其绘制到窗体上,以欺骗用户相信窗体不存在。但是由于闪烁太多,因为在再次捕获屏幕之前需要移除图像,所以我决定捕获精灵移动的屏幕的一小段,并将其附加到内存中存储的图像上,然后像这样移动。这比它需要的要复杂一些,而且写起来比这个版本要头疼得多,这个版本利用了 Per Pixel Alpha Blend 代码,省去了所有麻烦,因为 FormSprite 确实变得透明了,你的精灵可以在视频上行走,就像一集《吉尔莫女孩》一样,而不会弄脏罗里漂亮的笑容。煎饼!

一个 double 变量用于计算一个 dblSizeFactor,取决于杰西卡在屏幕上的位置,虽然从 A 点移动到 B 点所需的步数是从起始位置开始计算的,但每一步都由这个 dblSizeFactor 调整,所以,杰西卡真的不需要去任何地方是个好事,因为如果她从顶部开始向下移动,她在顶部时迈出的步伐很小,当她到达底部时,她就会错过她打算去的地方;如果她从底部开始向上移动,她就会达不到目标。但是,就像我说的,我只是无聊,而且她确实没有什么更好的地方可去,所以我不在乎她不在乎,我也不想问她。

忍者风格翻转投掷

你可以用鼠标抓住杰西卡并将她移动到任何你喜欢的地方,你甚至可以把她扔到任何地方。当你扔她时,她会做几个翻滚,然后以忍者风格的姿势落在她的“游乐区”边缘,无论你将她扔向哪个方向。

右键单击杰西卡

如果你右键单击杰西卡,会弹出一个菜单,与你右键单击任何其他窗体或桌面时出现的菜单类似。这些是杰西卡的选项。目前只有两个:

  1. 杀死杰西卡,这可能很不幸,但有时是必要的,而且由于她是一名训练有素的专业人士,她理解桌面宠物生涯的风险,杰西卡已为这一意外情况做好了准备,你不必担心她的困境,但请放心,她自愿走向自己的命运,知道这一切都是为了一个更好的事业。
  2. 定义游乐区。这让你告诉杰西卡待在屏幕的某个区域。

这是右键单击菜单的屏幕截图

Right_Click.PNG

为了定义杰西卡的“游乐区”,程序会设置一个包含一个完全停靠的 picturebox 的窗体,窗体的大小和位置覆盖整个屏幕。在此窗体被投射到屏幕之前,会捕获屏幕截图并存储在一个全局位图中,然后加载到 picturebox 中。MouseDownMouseUpMouseMove 事件会跟踪鼠标按钮被按下和释放的位置,而 mouseMove 事件触发的计时器会更新图像,显示用户拖动的黄色矩形。当鼠标释放时,窗体消失,杰西卡的移动计时器重新启用,她的“游乐区”被重新定义,所以她会重新部署到你之前发送她的地方,并避开你做那些你老板认为你做的重要工作。

define_PlayPen.PNG

添加扭动

如果你有 Sprite-Editor 副本并正在运行,你可以加载杰西卡的精灵,并在不需要编辑项目的情况下为精灵添加“扭动”功能。你必须确保所有配置都按顺序排列,这样像“挠胳膊”或“翻转远发”这样的怪异动作都排在“站立不动”和“行走”之前,然后你可以添加你的“扭动”配置,记住要遵循已建立的精确命名约定。

说到“命名约定”,关于这个和另一个文章中的变量和类命名约定,我反驳了任何试图说服我遵循微软标准的尝试,但已经接受了类名的更改,我将其从 classSpriteForm 改为更合适的 FormSprite。同样,同样的角度,我去更改了允许杰西卡围绕除 MasterBlock 之外的点扭动的肢体变量的名称,以前它类似于 cStationaryLimb,而不是我现在称之为 limStationary,这对于任何使用过 classSprite 窗体的人来说可能更容易阅读,其中所有肢体变量都有一个“lim”前缀。

你可以看看她去...

历史

  • 几周前,我随意做了这个。
  • 然后昨天(2010 年 10 月 12 日),我开始思考并使用逐像素 Alpha 混合使其变得更好。
  • 今晚(2010 年 10 月 13 日),她到处乱逛挡了我的路,我正在修补代码,我想出了一个抓取她并移动她的方法。
  • (2010 年 10 月 14 日)添加了 ColorOutline 属性。
  • (2010 年 10 月 18 日)添加了投掷、新菜单和游乐区。
© . All rights reserved.