Talking about Hilly algorithm

First、roguelike Map introduction

Roguelike is an RPG game with a random map. The randomly generated map, props and other elements on each layer are the characteristics of this type of game. I won’t go into details about its specific introduction.
The following will introduce the use of hill algorithm (Hill Algorithm) to achieve random map generation.
Before that, let’s briefly explain the basic information of the map:

  1. The size of the map: 30×30 grids;
  2. Grid of the map: wall or open space, use IsFloor to indicate whether the grid is open space, see below for details.

Second, the general steps of the hill algorithm

The hill algorithm is an algorithm borrowed from random walking (a model implemented with Berlin noise). This algorithm assigns a “weight” to each element as a basis for generating random terrain.
The general steps are as follows:

  1. Randomly assign weights to the elements on the two-dimensional array (the weights start with 0, and a total of 5 weights from 0 to 4 are used in this article);
  2. Smooth weight;
  3. Hole (ie floor) repair;
  4. Eliminate isolated holes (ie walls);
  5. Detection and penetration of the cavity area;
  6. Tortuous supplement of full acupoints.
    See the specific steps below.

Third, initialize the map

In VB6, first define the grid of the map as follows:

Whether the grid is empty;
Weights;
Whether it is marked, the marking is used for regional and other processing;
Area number.

Code:

  Private Type tMap
      IsFloor As Boolean                                  
      Weight As Integer                                   
      Marked As Boolean                                   
      Area As Integer
  End Type

The map array Map is a two-dimensional array whose type is tMap.

In addition, it is stipulated that the outermost circle of the map must be a wall, so in fact, the randomly generated part of the map is a grid between 1-28.

Private Sub InitMap()
    For M = 0 To 29
        Map(M, 0).IsFloor = False
        Map(M, 0).Weight = 4
        Map(M, 0).Marked = False
        Map(M, 0).Area = -1

        Map(M, 29).IsFloor = False
        Map(M, 29).Weight = 4
        Map(M, 29).Marked = False
        Map(M, 29).Area = -1

        Map(0, M).IsFloor = False
        Map(0, M).Weight = 4
        Map(0, M).Marked = False
        Map(0, M).Area = -1

        Map(29, M).IsFloor = False
        Map(29, M).Weight = 4
        Map(29, M).Marked = False
        Map(29, M).Area = -1
    Next M

    For N = 1 To 28
        For M = 1 To 28
            Map(M, N).IsFloor = False
        Next M
    Next N
End Sub

Fourth, random weighting

Perform a random weighting on the grids on the map (excluding the four sides), that is, assign random numbers generated from 0 to 4 to 28×28 grids on the map.

Private Sub GenerateRandomMap()
    Randomize
    For N = 1 To 28
        For M = 1 To 28
            Map(M, N).Weight = Int(Rnd * 5)
        Next M
    Next N
End Sub

Fifth, smooth weight

There are generally several ways of smoothing weight:

1.The mean value sampling is smooth. The average of 9 grids including one week of the grid and itself is smoothed. This smoothing mode is relatively simple, but the smoothing effect is the most unsatisfactory.

2. Manhattan distance sampling is smooth. Take the average of the sample sum of the grid itself and the grid within the specified Manhattan distance. This smoothing mode takes into account the influence of the Manhattan distance on the target grid and is a better smoothing method. In this article, the smoothing method is adopted, and the Manhattan distance is 1.

3.The two-dimensional normal distribution is sampled and smoothed once (also called “one-time Gaussian smoothing”). This kind of smoothing is based on the Manhattan distance, using the normal distribution function for smoothing. The smoothing effect is better than the Manhattan distance sampling smoothing. However, because the function is too complex, this article does not use this method. If you are interested, you can use it. This way.

Code:

    Dim rMap(1 To 28, 1 To 28) As Integer
    For N = 1 To 28
        For M = 1 To 28
            rMap(M, N) = GetSum(M, N)
        Next M
    Next N

    For N = 1 To 28
        For M = 1 To 28
            Map(M, N).Weight = rMap(M, N) / 5
        Next M
    Next N

After smoothing the weights, these data should be binarized to realize the separation of walls and floors. Because the weight range is 0-4, the weight of (4-0)÷2=2 can be used as the critical point to convert the weight into terrain data.

Set the grid with a weight of 2 as an empty space, and set the other grids with a weight of not 2 as walls, and you can get the following code:

    For N = 1 To 28
        For M = 1 To 28
            If Map(M, N).Weight <> 2 Then
                Map(M, N).Weight = 4
                Map(M, N).IsFloor = False
            Else
                Map(M, N).Weight = 0
                Map(M, N).IsFloor = True
            End If
        Next M
    Next N

The following are the renderings before and after smoothing:
before:

after:

Sixth, hole repair

For an open space grid, when the surrounding grids are all walls, convert this grid to a wall to achieve the following effects:

□■□         □■□

■□■   →  ■■■

□■□         □■□

Code:

  For N = 1 To 28
      For M = 1 To 28
          If Map(M, N).IsFloor And Map(M - 1, N).IsFloor = False And Map(M + 1, N).IsFloor = False And Map(M, N - 1).IsFloor = False And Map(M, N + 1).IsFloor = False Then Map(M, N).IsFloor = False: Map(M, N).Weight = 4
      Next M
  Next N

The results are as follows:

Seventh, isolation and elimination

Elimination of isolated holes refers to the elimination of walls that are not closely related to walls in other locations on the map. It is stipulated in this article that when the number of wall grids around a wall grid is less than 2, the grid will be regarded as “isolated and full of holes”.

In order to ensure that there are no isolated full holes on the map, multiple iterations of elimination processing are required until the map shows a “stable” situation, that is, when all the grids on the map no longer change, then the next step can be entered.

The following is part of the code to eliminate the full hole in a single isolation:

    Dim rMap(1 To 28, 1 To 28) As Integer
    For N = 1 To 28
        For M = 1 To 28
            If Map(M, N).IsFloor = False And GetWallCount(M, N) < 2 Then
                mCheck = True
                rMap(M, N) = 0
            Else
                rMap(M, N) = Map(M, N).Weight
            End If
        Next M
    Next N

    For N = 1 To 28
        For M = 1 To 28
            Map(M, N).Weight = rMap(M, N)
            Map(M, N).IsFloor = Map(M, N).Weight = 0
        Next M
    Next N

The following is the first time the effect of isolation and elimination of acupuncture points:

The following is the effect of eliminating full cavities in a stable state:

8.Cavity area detection and penetration

In order to ensure that the player character can act in all open spaces on the map, it is necessary to ensure that all open spaces on the map are connected.

It is stipulated that each open space enclosed by walls on the map constitutes a set, which is called “Area”. There may be several areas on a map. The next thing to do is to ensure that only one area exists on the map. .

Get all the areas on the map by traversing the open space. As shown in the icon below, there are two areas on the map, one is a very large area made of green area 1, and the other is a smaller area 2 in the lower right corner of the map.:

In order to make the number of areas to 1, it is necessary to penetrate a smaller area. This article adopts the method of “random spreading corrosion”, that is, for each grid in a small area, the surrounding walls have a 75% probability of becoming empty and spreading. When an open space grid with a different number from the current area is detected, it stops spreading, so that the hole can be penetrated.

If there are more than 2 areas on the map, it is necessary to perform repeated operations to penetrate and check the number of areas until the number of areas is 1.

Part of the code for this step is as follows:

Private Sub ExpandArea(ByVal X As Integer, Y As Integer, AreaID As Integer)     
    If Map(X, Y).Marked Or X = 0 Or X = 29 Or Y = 0 Or Y = 29 Or Map(X, Y).Area = AreaID Then Exit Sub
    Map(X, Y).Marked = True
    Randomize
    Dim i As Integer, j As Integer
    For j = -1 To 1
        For i = -1 To 1
            If (i = 0 Or j = 0) Then                                     
                If (Rnd * 100 <= 75) And Map(X + i, Y + j).IsFloor = False And _
                    X + i > 0 And X + i < 29 And Y + j > 0 And Y + j < 29 Then  
                    Map(X + i, Y + j).IsFloor = True
                    Map(X + i, Y + j).Weight = 0
                    Map(X + i, Y + j).Area = Map(X, Y).Area
                    ExpandArea X + i, Y + j, AreaID
                End If
            End If
        Next i
    Next j
End Sub

Nine, full point tortuous supplement

The function of this step is to increase the tortuosity of the map and avoid the appearance of a large area of open space. In this paper, the method of calculating the Manhattan distance is used to generate the wall (full hole), that is, when the number of walls in the grid within the specified Manhattan distance (2 in this article) is 0, this grid is set as the wall.

The following is the relevant code:

    Dim rMap(3 To 26, 3 To 26) As Boolean
    For N = 3 To 26
        For M = 3 To 26
            If Map(M, N).IsFloor Then
    If GetFloorCount(M, N) = 13 Then '曼哈顿距离计算该距离该位置2格的格子为空地的计数
                    rMap(M, N) = False
                Else
                    rMap(M, N) = Map(M, N).IsFloor
                End If
            End If
        Next M
    Next N

    For N = 3 To 26
        For M = 3 To 26
            Map(M, N).IsFloor = rMap(M, N)
            Map(M, N).Weight = IIf(rMap(M, N), 0, 4)
        Next M
    Next N

The following is the rendering:

At this point, a decent Roguelike map is ready.


references

1、https://blog.csdn.net/u010669231/article/details/97051705

2、https://indienova.com/indie-game-development/procedural-content-generation-tile-based-random-cave-map/

3、https://blog.csdn.net/OvejeJ1/article/details/104668607/

Leave a Reply

Your email address will not be published. Required fields are marked *