本文是关于如何创建使用有限状态机(Finite State Machine, FSM)的代理的实用示例。在这篇文章中,将尝试解释如何使用WinForms和GDI+实现FSM代码。将探讨一个游戏环境,其中代理居住在一个名为WestWorld的Old West风格的金矿小镇。WestWorld有两个居民——一个名叫Miner Bob的金矿工人和他的妻子Amanda。
本教程的目标是向展示如何从头开始创建一个简单的游戏,不使用像XNA或DirectX这样的高级API,这些API为自动化了一半的过程。将只使用Windows表单和GDI+进行一些基本的绘图,并为了一点额外的方便,使用了几个表单的事件。
WestWorld使用了一个修改版的VB.NET版本的RogueSharp。地图生成创建了一个随机生成的独特地图。地图可能看起来与显示的地图不同。
RogueSharp是一个用C#编写的免费库,帮助roguelike开发者在他们的游戏中取得领先。RogueSharp提供了许多实用功能,用于处理地图生成、视野计算、路径查找、随机数生成等。它还包含了制作房间和洞穴地图的策略。
与RogueSharp的大多数交互都是基于
Map
的概念,这是一个矩形网格的
Cells
。每个
Map
中的
Cell
具有以下属性:
IsTransparent
:如果视野可以透过
Cell
,则为true。
IsWalkable
:如果玩家可以穿越
Cell
,则为true。
IsExplored
:如果玩家曾经有视线到
Cell
,则为true。
IsInFov
:如果
Cell
当前在玩家的视野中,则为true。
要实例化一个新的
Map
,可以使用其构造函数,该构造函数接受宽度和高度,并创建一个具有这些尺寸的新地图,并将所有
Cells
的属性设置为false。
注意
Map
类上的
ToString()
运算符被重写,以提供地图的简单视觉表示。可以为
ToString()
提供一个可选的
bool
参数,以指示是否要使用视野。如果没有提供参数,则默认为false。
使用以下符号:
创建地图的一个更有趣的方法是使用
Map
类的静态方法
Create
,它接受一个
IMapCreationStrategy
。RogueSharp提供了一些实现
IMapCreationStrategy
的简单类,但通过创建自己的类来实现策略,这是很容易扩展的。
对于RogueSharp的原始版本,可以在 上找到NuGet包。
为了使WestWorld动画化,简单地绘制一个图像,然后移动到下一帧,然后绘制序列中的下一个图像。因此,需要跟踪正在动画化的帧数、动画中的帧数、单个精灵的宽度和高度,以及转向的方向。为了动画化图像,在 (0, 32) 处绘制图像,然后在下一帧,在 (33, 65) 处绘制图像,然后重复。
VB代码示例:
Dim sizeOfSprites As Integer = 32
Dim scale As Double = 0.5F
Dim rectPosition As New System.Drawing.Rectangle(X * sizeOfSprites, Y * sizeOfSprites, _Width, _Height)
buffer.DrawImage(_Image, rectPosition, imageX, imageY, _Width, _Height, GraphicsUnit.Pixel)
图像太大,无法适应屏幕。因此,让将所有的精灵缩小到1/2大小,16 x 16像素。使用
Bitmap
类的
DrawImage
方法,可以根据其当前位置设置要绘制的精灵的比例。
要实际看到地图,需要在
World.vb
中的
Draw()
方法中添加一些代码。设置一个变量,对应于用于地板和墙单元格的精灵大小。如果使用了提供的图形,这些是64像素的正方形。这个值将用于计算绘制每个精灵的位置。接下来,调用新创建的
_map
上的
GetAllCells
()方法。这返回一个
IEnumerable
<cell>
,包含地图中的所有单元格,可以使用
For Each
循环遍历它们。当遍历每个
Cell
时,检查
IsWalkable
属性。这将决定否应该绘制地板。通过查看相应
Cell
的
X
和
Y
属性并将其乘以设置的变量来计算应该绘制精灵的位置。这就是实际调用
Bitmap
缓冲变量上的
Draw
方法并提供地板或墙壁纹理以及之前计算的位置的地方。
Public Sub Draw(ByVal g As Graphics)
Dim sizeOfSprites As Integer = 64
Dim scale As Double = 0.5F
Dim multiplier As Integer = CInt(sizeOfSprites * scale)
Dim myBrush As Brush = New SolidBrush(Color.Yellow)
For Each Cell As Cell In _map.GetAllCells
Dim Text As String = String.Format("X:{0}, Y:{1}", Cell.X, Cell.Y)
If Cell.IsWalkable And Cell.IsTransparent Then
Dim position As New System.Drawing.Point(CInt(Cell.X * multiplier), CInt(Cell.Y * multiplier))
g.DrawImage(_Grass, position)
Else
Dim position As New System.Drawing.Point(CInt(Cell.X * multiplier), CInt(Cell.Y * multiplier))
g.DrawImage(_Wall, position)
End If
Next
End Sub