WestWorld - 第 4 部分





1.00/5 (1投票)
一个关于有限状态机 (FSM) 的演示,其中代理们居住在一个旧西部风格的淘金小镇,名为 West World。
注意:这是 CodeProject 文章系列《WestWorld
》的第 4 部分。要获取本文及第 1-3 部分的更多背景信息,请访问此链接 WestWorld。
引言
如何创建使用有限状态机的代理的实际示例。在本文中,我将尝试解释如何使用 WinForms 和 GDI+ 来实现 FSM 代码。我们将探讨一个游戏环境,其中代理们居住在一个旧西部风格的淘金小镇,名为 West World。WestWorld 有两个居民——一个名叫 Miner Bob 的金矿工和他妻子 Amanda。
WestWorld1
- FSM 基础WestWorld2
- 带有两个代理(矿工和妻子)的 FSMWestWorld3
- 包含代理间消息传递的 FSMWestWorld4
(本文) - 使用 RogueSharp 介绍 WindowsForms GDI+
背景
本教程的目的是向您展示如何从头开始创建一个简单的游戏,而无需 XNA 或 DirectX 等自动化一半过程的高级 API。我们将只使用 Windows Form 和 GDI+ 进行一些基本的绘图,并为了方便起见,使用 Form 的一些事件。
映射生成
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
。
使用的符号如下:
%
:Cell
不在视野范围内。.
:Cell
是透明的、可通行的,并且在视野范围内。s
:Cell
是可通行的并且在视野范围内(但不是透明的)。o
:Cell
是透明的并且在视野范围内(但不可通行)。#
:Cell
在视野范围内(但不是透明的或可通行的)。
创建地图的一个更有趣的方式是使用 Map
类中的 static
方法 Create
,它接受一个 IMapCreationStrategy
。RogueSharp 提供了一些简单的实现了 IMapCreationStrategy
的类,但通过创建您自己的实现该策略的类,可以轻松扩展。
对于 RogueSharp 的原始版本,可以在 https://nuget.net.cn/packages/RogueSharp 获取 NuGet 包。
精灵渲染
为了动画化 WestWorld
,我们只需绘制一个图像,然后移动到下一帧,再绘制序列中的下一个图像。因此,我们需要跟踪我们所在的动画帧、动画的总帧数、单个精灵的宽度和高度以及转向的方向。为了动画化我们的图像,我们在 (0, 32)
处绘制图像,然后在下一帧在 (33, 65)
处绘制图像,并重复此过程。
Dim sizeOfSprites As Integer = 32
Dim scale As Double = 0.5F
Dim rectPosition As New System.Drawing.Rectangle_
(New Point(X * sizeOfSprites, Y * sizeOfSprites)...
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
属性。这将决定是否应绘制地板。我们通过查看相应单元格的 X
和 Y
属性并乘以我们设置的变量来计算绘制精灵的位置。在这里,我们实际调用 Bitmap
buffer
变量上的 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