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

TurboSprite:.NET Framework 2.0 上的简单 2D 精灵引擎

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.96/5 (24投票s)

2008年5月26日

BSD

11分钟阅读

viewsIcon

78593

downloadIcon

1875

.NET 2.0 上一个简单、面向对象的 2D 精灵引擎。

引言

TurboSprite 是一组组件,可为您的 .NET 应用程序提供完整的 2D 动画精灵引擎。“精灵”是游戏或其他支持动画的应用程序中的可移动对象。在 TurboSprite 中,您可以使用几种不同类型的精灵,并创建自己的派生类型来展现您需要的确切外观和行为。TurboSprite 完全用 C# 托管代码编写。主要组件是

  • SpriteSurface - 精灵动画发生的视觉画布。
  • Sprite - 一个抽象基类,用于封装精灵。TurboSprite 包含几个具体的派生精灵类,例如 BitmapSpritePolygonSpriteAnimatedBitmapSprite
  • SpriteEngine - 添加到 SpriteEngine 的精灵将出现在 SpriteSurface 上。SpriteEngine 会通知您精灵之间的碰撞。SpriteEngine 的派生类允许您定义它们所包含的精灵的不同移动能力。TurboSprite 中包含的 DestinationSpriteMover 派生类允许您以不同的速度将精灵移动到目标点。

背景

TurboSprite 最初是在 20 世纪 90 年代中期作为一个用 Borland Delphi 编写的 Windows 精灵引擎开始的。当时,在 Windows 中生成响应式动画非常困难,TurboSprite 依赖于特殊的汇编语言编码函数直接绘制到图像缓冲区内存。现在计算机速度更快,TurboSprite 的最新版本利用 GDI+ 的绘图能力来执行渲染。这使得我能够完全用 C# 托管代码开发该包。对于简单的 2D 游戏和其他应用程序,性能非常可接受!TurboSprite for .NET 保留了原始库的简单、面向对象的设计模式。

TurboSprite 组件

SpriteSurface

SpriteSurface 是精灵移动和其他动画效果发生的可见动画表面。将 SpriteSurface 放置在窗体上后,设置 DesiredFPS 属性以确定您希望实现的动画每秒帧数。在代码中,将 Active 属性设置为 true 来开始动画。您可以随时通过检查 ActualFPS 属性来检查 SpriteSurface 的实际动画速率。

您可以使用多种不同的方式通过 SpriteSurface 产生动画。一种方法是使用 SpriteEngines 和 Sprites(见下文)。当您将 SpriteEngines 连接到 SpriteSurface 时,SpriteEngines 中包含的精灵将在 SpriteSurface 上渲染和处理。

SpriteSurface 还提供事件和属性,您可以在动画周期的各个点处理它们。其中一些事件使用 PaintEventArgs,因此您可以使用事件中包含的 Graphics 对象在表面上绘图。请记住,下面事件的顺序在每个动画周期中都会发生。您基本上在每个周期中绘制完整的帧。正是每一帧中绘制内容的细微变化可以产生动画效果。

  • BeforeAnimationCycle 事件在每个动画帧之前发生。
  • 如果将 AutoBlank 属性设置为 true,则在每个动画周期中发生的第一件事就是使用 AutoBlankColor 属性清除表面。
  • BeforeSpriteRender 事件在任何精灵渲染到表面之前发生,但在 AutoBlank 处理之后。使用 Graphics 对象在每个动画帧中执行任何自定义渲染到表面的操作。
  • AfterSpriteRender 事件在精灵渲染之后发生,并允许您使用传递的 Graphics 对象绘制到表面。

SpriteSurface 具有像任何其他 Windows Forms 控件一样的 WidthHeight 属性,但它还有一个“虚拟”尺寸的概念,即控件的可见部分是完整虚拟区域的视口。要启用此功能,请将 UseVirtualSize 属性设置为 true,并使用 VirtualSize 属性设置虚拟尺寸。当虚拟尺寸模式生效时,您使用 OffsetXOffsetY 属性来读取和设置到虚拟空间中的左上角的偏移量。使用虚拟尺寸时,请记住精灵的 X 和 Y 坐标是以完整的虚拟空间而不是 SpriteSurface 的可见部分来表示的。

SpriteSurface 中有许多属性可以显示一个选择光标。如果 CursorVisibletrue,则光标将显示为矩形,无论您在表面上单击何处。光标的外观由 CursorColorCursorWidth 属性控制,其位置由 CursorXCursorY 属性控制。

此外,如果 SelectionBandVisibletrue,则当您在 SpriteSurface 上拖动鼠标时,将出现一个选择框。选择框的颜色由 SelectionBandColor 属性控制。当用户绘制选择框时,SpriteSurface 会触发 RangeSelected 事件。

Wraparound 属性决定当精灵对象移出 SpriteSurface 的虚拟尺寸时会发生什么。如果 Wraparoundfalse,则允许精灵移出实际尺寸,只是不进行渲染。如果为 true,则超出表面一侧的精灵将在另一侧出现,从而有效地环绕表面。

Sprite

Sprite 类定义了一个可以移动并在 SpriteSurface 上渲染的对象。Sprite 是一个抽象类,它定义了一个 Position 属性、一个 WidthHeight,以及一些对所有精灵通用的其他属性。TurboSprite 包含许多具体的派生精灵类

  • BitmapSprite 使用 Bitmap 来渲染自身。
  • AnimatedBitmapSprite 继承自 BitmapSprite,并允许您指定一组 Bitmap,这些 Bitmap 定义了用于渲染精灵的动画帧。
  • PolygonSprite 根据您定义的一组点来渲染自身,并支持绕其轴旋转。
  • ParticleExplosionSprite 生成基于粒子的爆炸,允许您控制大小、速度和颜色范围。
  • ShockwaveSprite 生成由向外辐射的圆组成的爆炸效果。
  • StarFieldSprite 重现了经典的街机游戏星场超空间效果。

鼓励您将这些用作示例,并创建自己的 Sprite 派生类。

Sprite 类包含两个重要的方法,值得在此进一步讨论

  • Process 方法是虚拟的,如果您愿意,可以在具体的派生类中重写它。TurboSprite 在每个动画帧之前调用精灵的 Process 方法,无论精灵是否在虚拟区域的可见部分内。在这里,您可以执行任何您希望在每个动画帧中应用于精灵的处理。这可以包括粒子分散、递减生命计数器等。
  • Render 方法是抽象的,因此必须在派生类中实现。它包含实际将精灵绘制到 SpriteSurface 上的代码。Render 提供了一个 Graphics 对象,您应该使用它来渲染精灵。

渲染精灵时,使用精灵的 Location,减去 SpriteSurface 当前的 OffsetXOffsetY,以确定在 Graphics 对象上的绘制位置。下面的示例是 BitmapSpriteRender 方法,它使用 Bitmap 来渲染自身。请注意,代码在渲染时还减去了 Bitmap 大小的一半,确保 Bitmap 以精灵的实际位置为中心。

//Render it on the sprite surface
protected internal override void Render(Graphics g)
{
   g.DrawImage(Bitmap, X - WidthHalf - Surface.OffsetX, 
               Y - HeightHalf - Surface.OffsetY);
}

XYSprite 类的属性,让您可以访问其位置。如上所示,Sprite 类通过 Surface 属性提供对其 SpriteSurface 的访问。

Sprite 派生类中,告诉 TurboSprite 精灵的大小很重要。TurboSprite 使用此信息以及精灵的 Position 来确定是否需要在每个动画帧中渲染它。精灵的 Shape 属性(一个 RectangleF)应该在 Sprite 派生类中分配,以定义其大小。Shape 属性的设置应与其位置无关。例如,如果一个精灵的大小为 20x20 像素,那么其 Shape 属性应设置为新的 RectangleF (-10, -10, 10, 10);这将精灵的形状以其位置为中心。您应该设置 Shape 属性,使其大致对应于您在 Render 方法中绘制的图像。

下面我们看到 BitmapSprite 在分配 Bitmap 时设置了 Shape 属性

//The bitmap used to render the sprite
public Bitmap Bitmap
{
   get
   {
      return _bitmap;
   }
   set
   {
      _bitmap = value;
      if (_bitmap != null)
        Shape = new RectangleF(-_bitmap.Width / 2, -_bitmap.Height / 2, 
                               _bitmap.Width, _bitmap.Height);
   }
}

TurboSprite 支持点击精灵并通过 SpriteClicked 事件通知客户端。您并不总是希望精灵的可见绘制区域来确定精灵被点击的区域。因此,Sprite 类还有一个 ClickShape 属性,如果您希望点击区域与 Shape 不同,可以设置它。默认情况下,ClickShape 假定 Shape 属性的设置值。

Sprite 类还定义了一些描述旋转的属性,包括 FacingAngleSpinSpinSpeed。如果 Spin 设置为 SpinType.ClockWiseSpinType.CounterClockwise,则 TurboSprite 会在每个动画周期自动调整精灵的 FacingAngle。由派生的 Sprite 类来利用 FacingAngle 来渲染精灵。要查看一个实现此功能的示例,请检查 PolygonSprite 类的代码。PolygonSprite 维护一个点的内部列表,这些点定义了精灵的多边形形状。在 Process 方法中,点根据精灵的 FacingAngle 进行旋转。

//Process the sprite on each animation cycle - handle rotation
protected internal override void Process()
{
   //Process rotation of shape
   if (FacingAngle != _lastAngle)
   {
      float sin = Sprite.Sin(FacingAngle);
      float cos = Sprite.Cos(FacingAngle);
      _lastAngle = FacingAngle;
      for (int p = 0; p < _points.Length; p++)
      {
         _points[p].X = _unrotated[p].X * cos - _unrotated[p].Y * sin;
         _points[p].Y = _unrotated[p].Y * cos + _unrotated[p].X * sin;
      }

      //This causes Shape to be correctly recalculated
      Points = _points;
   }
}

顺便说一句,Sprite 类创建了正弦和余弦值的静态查找表,这减少了动画周期中的计算开销,而 PolygonSprite 在上面的代码中使用了这些查找表。

SpriteEngine

要使您的精灵出现在 SpriteSurface 上,它们必须添加到 SpriteEngine 中。SpriteEngine 是一个非可视组件,用于管理精灵移动和碰撞检测。SpriteEngine 有一个 Surface 属性,应将其设置为其精灵将要绘制的 SpriteSurface。您可以使用连接到同一 SpriteSurface 的多个 SpriteEngine,为同一表面上的不同精灵组提供不同的行为。

SpriteEnginePriority 属性决定了当多个 SpriteEngine 连接到 SpriteSurface 时精灵的渲染顺序。

精灵之间的碰撞检测通过设置 SpriteEngineDetectCollisionSelfDetectCollisionFlag 属性来处理。如果 DetectCollisionSelftrue,则 SpriteEngine 中的精灵将检测它们之间的碰撞。当使用多个 SpriteEngine 时,DetectCollisionFlag 值相同的 SpriteEngine 将检测它们之间的碰撞。当检测到精灵碰撞时,SpriteSurface 会触发 SpriteCollision 事件,并将发生碰撞的两个 Sprite 对象传递给您。

移动精灵

除非有办法让精灵动起来,否则它们几乎一文不值!在 TurboSprite 中,这由派生自 SpriteEngine 的组件处理。SpriteEngine 提供了一个定义精灵移动的设计模式。派生组件可以利用此模式来创建不同的精灵移动方式。TurboSprite 包含一个这样的派生组件,即 SpriteEngineDestination 组件,它以特定速度将精灵移动到特定目标。

这里的设计利用了 Sprite 类中的一个特殊标签属性,称为 MovementDataSpriteEngine 派生组件可以根据需要将任何对象分配给此属性,以跟踪和/或控制精灵的移动。设置此值的地方是 SpriteEngineInitializeSprite 方法,该方法应在派生类中重写。SpriteEngineDestination 重写此方法以创建 DestinationMover 对象实例,并将其分配给 SpriteMovementData 属性。

//Create a DestinationMove object and attach it to the sprite
protected override void InitializeSprite(SCG.TurboSprite.Sprite sprite)
{
   sprite.MovementData = new DestinationMover(sprite);
}

SpriteEngineDestination 提供了一个名为 GetMover 的公共方法,该方法将 DestinationMover 的实例返回给客户端,用于特定的 Sprite 对象。DestinationMover 类本身包含描述精灵速度、目的地以及精灵到达目的地后是否应停止移动的属性。因此,客户端代码可以通过设置 DestinationMover 对象的 SpeedDestination 属性来使精灵移动。

TurboSprite 在调用 SpriteEngineMoveSprite 方法时执行移动逻辑。这发生在每个动画周期中。SpriteEngineDestination 重写了 MoveSprite 方法并采用了其自定义移动逻辑。有关精灵移动设计模式的更详细了解,请参阅 SpriteEngineDestination 的代码及其注释。虽然这可能显得繁琐,但它为将来可以添加的不同移动行为提供了灵活性。

TurboSprite 演示应用程序

源代码中包含一个完整的演示应用程序,它演示了如何创建精灵、将它们添加到 SpriteEngines 以及移动它们。它还涉及 TurboSprite 的一些其他优点,例如 GamePieceBitmapFactory 组件,该组件允许您轻松访问包含在单个较大 Bitmap 中的单个精灵图形,甚至可以将其着色为任何特定颜色。

这是当单击“位图精灵”按钮时执行的代码。它创建一个 BitmapSprite 实例,并将其投入到 SpriteSurface 中。

private void btnAddSprite_Click(object sender, EventArgs e)
{
   //Create a BitmapSprite
   BitmapSprite s = new BitmapSprite((Bitmap)picGlyph.Image);
   s.Bitmap.MakeTransparent(Color.Black);

   //Center it on the SpriteSurface
   s.Position = new Point(surface.Width / 2, surface.Height / 2);

   //Add it to the SpriteEngine
   engineDest.AddSprite(s);

   //Set its speed and destination
   DestinationMover dm = engineDest.GetMover(s);
   dm.Speed = rnd.Next(10) + 1;
   dm.Destination = new Point(rnd.Next(surface.Width), rnd.Next(surface.Height));
   dm.StopAtDestination = false; 
}

TurboSprite 实战

TurboSprite 是我免费的实时策略游戏 Solar Vengeance 中使用的动画引擎。Solar Vengeance 使用了 TurboSprite 的许多功能,其中一些功能包含在此包中但本文未涉及。

  • 使用许多自定义 Sprite 派生类来渲染星舰和星系。
  • 使用 AnimatedBitmapSprite 类来渲染虫洞。
  • 使用 ParticleExplosionSpriteShockwaveSprites 来渲染爆炸。
  • 使用 SquareGridSpriteSurface 作为主表面。这是一个派生自基本 SpriteSurface 的组件,但提供了额外功能,允许您使用方形网格。
  • 使用 SpriteEngineDestination 在游戏场地上移动精灵。

访问 Silicon Commander Games 网站,了解更多关于 TurboSprite、Solar Vengeance 以及我们开源的 .NET 多人游戏和聊天组件包 PrismServer 的信息。

© . All rights reserved.