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

WestWorld - 第 4 部分

starIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

1.00/5 (1投票)

2015 年 7 月 20 日

CPOL

4分钟阅读

viewsIcon

11229

downloadIcon

201

一个关于有限状态机 (FSM) 的演示,其中代理们居住在一个旧西部风格的淘金小镇,名为 West World。

注意:这是 CodeProject 文章系列《WestWorld》的第 4 部分。要获取本文及第 1-3 部分的更多背景信息,请访问此链接 WestWorld

WestWorld Screen Capture

引言

如何创建使用有限状态机的代理的实际示例。在本文中,我将尝试解释如何使用 WinForms 和 GDI+ 来实现 FSM 代码。我们将探讨一个游戏环境,其中代理们居住在一个旧西部风格的淘金小镇,名为 West World。WestWorld 有两个居民——一个名叫 Miner Bob 的金矿工和他妻子 Amanda。

  • WestWorld1 - FSM 基础
  • WestWorld2 - 带有两个代理(矿工和妻子)的 FSM
  • WestWorld3 - 包含代理间消息传递的 FSM
  • WestWorld4 (本文) - 使用 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 是透明的、可通行的,并且在视野范围内。
  • sCell 是可通行的并且在视野范围内(但不是透明的)。
  • oCell 是透明的并且在视野范围内(但不可通行)。
  • #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.vbDraw() 方法中添加一些代码。我们设置一个变量来对应我们用于地板和墙壁单元格的精灵的大小。如果您使用了提供的图形,这些是 64 像素的正方形。此值将用于计算每个精灵的绘制位置。接下来,我们调用新创建的 _map 上的 GetAllCells() 方法。这将返回一个包含地图中所有单元格的 IEnumerable<cell>,我们可以使用 For Each 循环对其进行迭代。在迭代每个 Cell 时,我们会检查 IsWalkable 属性。这将决定是否应绘制地板。我们通过查看相应单元格的 XY 属性并乘以我们设置的变量来计算绘制精灵的位置。在这里,我们实际调用 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

历史

致谢

© . All rights reserved.