DEV Community

Aceld
Aceld

Posted on • Updated on

11. MMO Online Game AOI Algorithm

[Zinx]

<1.Building Basic Services with Zinx Framework>
<2. Zinx-V0.2 Simple Connection Encapsulation and Binding with Business>
<3.Design and Implementation of the Zinx Framework's Routing Module>
<4.Zinx Global Configuration>
<5.Zinx Message Encapsulation Module Design and Implementation>
<6.Design and Implementation of Zinx Multi-Router Mode>
<7. Building Zinx's Read-Write Separation Model>
<8.Zinx Message Queue and Task Worker Pool Design and Implementation>
<9. Zinx Connection Management and Property Setting>

[Zinx Application - MMO Game Case Study]

<10. Application Case Study using the Zinx Framework>
<11. MMO Online Game AOI Algorithm>
<12.Data Transmission Protocol: Protocol Buffers>
<13. MMO Game Server Application Protocol>
<14. Building the Project and User Login>
<15. World Chat System Implementation>
<16. Online Location Information Synchronization>
<17. Moving position and non-crossing grid AOI broadcasting>
<18. Player Logout>
<19. Movement and AOI Broadcast Across Grids>


MMO-GAME Source Code

https://github.com/aceld/zinx/tree/master/zinx_app_demo/mmo_game


Before diving into the current project case, this section will provide an introduction to the basic map algorithm used in MMO games.

The AOI (Area Of Interest) algorithm in games can be considered a fundamental core, as many game logics are driven by AOI entry and exit events. Additionally, numerous network synchronization data stem from AOI entry and exit events. Thus, a robust AOI algorithm and optimization based on it are crucial for enhancing game performance.

To achieve this, it is necessary to establish an AOI for each player. When an object's state changes, the information needs to be broadcast to all players. Players within the coverage of the AOI will receive this broadcast message and respond accordingly.

The desired functionality for the current game demo's AOI is to broadcast messages to nearby players when the state of players or NPCs on the server changes. When a player enters an NPC's alert zone, the AOI module sends a message to the NPC, which then responds with appropriate AI actions.

Next, let's proceed with the project build for the Zinx application server demo. Start by creating the "mmo_game" directory as the main project directory for the server-side game application.

11.1 Network-Based Implementation of AOI Algorithm

Before delving into the implementation of the AOI algorithm, let's start by creating a 2D map to visualize the concept of AOI, as shown in Figure 11.1.

Figure 11.1

Figure 11.1

We will divide the current coordinate system into 20 equally sized cells. The x-axis and y-axis coordinates range from 0 to 250. Each axis is divided into 5 sections, with each section considered a cell along that axis. This results in a total of 25 cell IDs in the 2D coordinate system, formed by combining the x and y axis indices.

Let's define some variables and formulas used in the calculations:

(1) The dimensions of the AOI map, with a total size of 250x250:

w for the width of the x-axis
l for the length of the y-axis

w = 250, l = 250
Enter fullscreen mode Exit fullscreen mode

(2) The number of cells on the x-axis:

nx = 5
Enter fullscreen mode Exit fullscreen mode

(3) The number of cells on the y-axis:

ny = 5
Enter fullscreen mode Exit fullscreen mode

(4) The width of each cell along the x-axis:

dx = w / nx
(i.e., dx = 250 / 5 = 50)
Enter fullscreen mode Exit fullscreen mode

(5) The length of each cell along the y-axis:

dy = l / ny
(i.e., dy = 250 / 5 = 50)
Enter fullscreen mode Exit fullscreen mode

(6) The x-axis index of a cell:idx with a range of 0 to 4
(7) The y-axis index of a cell:idy with a range of 0 to 4

(8) The cell ID:

id = idy * nx + idx
Enter fullscreen mode Exit fullscreen mode

From the above formulas, you can determine the cell ID based on the cell's coordinates.

(9) The coordinates of a cell:

x-axis coordinate index: idx = id % nx
y-axis coordinate index: idy = id / nx
Enter fullscreen mode Exit fullscreen mode

It is essential to carefully read and perform basic calculations based on Figure 11.1 to understand the provided formulas. This will facilitate comprehension when implementing the subsequent code logic.

Please note that the aforementioned formulas should be referenced against Figure 11.1. Readers are encouraged to read through and perform simple calculations based on the figure to better grasp the concepts and prepare for the upcoming code implementation.

11.2 Implementing AOI Grid Structure

In this section, we will implement the data structure, algorithms, and interfaces related to the AOI grid, based on the formulas provided in the previous section. We will organize the AOI module within a "core" module. To begin, create a "core" folder in the "mmo_game" directory and create a "grid.go" file. This file will contain the implementation of the AOI grid-related algorithms. The code is as follows:

// mmo_game/core/grid.go

package core

import "sync"

/*
   A grid class within a map
*/
type Grid struct {
    GID       int             // Grid ID
    MinX      int             // Left boundary coordinate of the grid
    MaxX      int             // Right boundary coordinate of the grid
    MinY      int             // Upper boundary coordinate of the grid
    MaxY      int             // Lower boundary coordinate of the grid
    playerIDs map[int]bool    // IDs of players or objects in the current grid
    pIDLock   sync.RWMutex    // Lock to protect the playerIDs map
}

// Initialize a grid
func NewGrid(gID, minX, maxX, minY, maxY int) *Grid {
    return &Grid{
        GID:       gID,
        MinX:      minX,
        MaxX:      maxX,
        MinY:      minY,
        MaxY:      maxY,
        playerIDs: make(map[int]bool),
    }
}
Enter fullscreen mode Exit fullscreen mode

The provided code defines the Grid data structure. Each Grid represents a cell, and a map can be divided into multiple such grids. The MinX, MaxX, MinY, and MaxY values represent the boundaries of each grid. The playerIDs map keeps track of the IDs of players or objects present within the grid. The pIDLock is a concurrent read-write lock that protects the playerIDs map. The GID represents the grid's unique identifier.

Please note that the playerIDs map uses the IDs of players or objects as keys within the map data structure. This code serves as the foundation for implementing the AOI grid structure and is essential for the upcoming steps in the AOI algorithm implementation.

The Grid class needs to possess several abilities:

(1)Adding a player to the current Grid cell, method named Add(), implemented as follows:

//mmo_game/core/grid.go

// Add a player to the current grid
func (g *Grid) Add(playerID int) {
    g.pIDLock.Lock()
    defer g.pIDLock.Unlock()

    g.playerIDs[playerID] = true
}
Enter fullscreen mode Exit fullscreen mode

(2) Removing a player from the current Grid cell, method named Remove(), implemented as follows:

//mmo_game/core/grid.go

// Remove a player from the grid
func (g *Grid) Remove(playerID int) {
    g.pIDLock.Lock()
    defer g.pIDLock.Unlock()

    delete(g.playerIDs, playerID)
}
Enter fullscreen mode Exit fullscreen mode

(3) Retrieving all player IDs stored in the current grid, method named GetPlayerIDs(), returning a slice of player IDs, implemented as follows:

//mmo_game/core/grid.go

// Get all players in the current grid
func (g *Grid) GetPlayerIDs() (playerIDs []int) {
    g.pIDLock.RLock()
    defer g.pIDLock.RUnlock()

    for k := range g.playerIDs {
        playerIDs = append(playerIDs, k)
    }

    return
}
Enter fullscreen mode Exit fullscreen mode

(4) Logging method for printing the current grid's self-parameter information, method named String(), implemented as follows:

//mmo_game/core/grid.go

// Print information method
func (g *Grid) String() string {
    return fmt.Sprintf("Grid id: %d, minX:%d, maxX:%d, minY:%d, maxY:%d, playerIDs:%v",
        g.GID, g.MinX, g.MaxX, g.MinY, g.MaxY, g.playerIDs)
}
Enter fullscreen mode Exit fullscreen mode

11.3 Implementing AOI Management Module

If a Grid is considered an element, then many Grid cells can compose an AOI (Area of Interest) region. This section defines the AOI management module that provides the capability to manage all grids. With the AOI management module, created Grid objects can be added for management.

Create a file named aoi.go in the mmo_game/core directory. This file primarily implements functionalities related to the AOI management module. Define the data structure for the AOI manager in the following code:

// mmo_game/core/aoi.go
package core

/*
   AOI Management Module
*/
type AOIManager struct {
    MinX  int           // Area's left boundary coordinate
    MaxX  int           // Area's right boundary coordinate
    CntsX int           // Number of grids in the x-direction
    MinY  int           // Area's upper boundary coordinate
    MaxY  int           // Area's lower boundary coordinate
    CntsY int           // Number of grids in the y-direction
    grids map[int]*Grid // All grids present in the current area, key = grid ID, value = grid object
}

/*
   Initialize an AOI area
*/
func NewAOIManager(minX, maxX, cntsX, minY, maxY, cntsY int) *AOIManager {
    aoiMgr := &AOIManager{
        MinX:  minX,
        MaxX:  maxX,
        CntsX: cntsX,
        MinY:  minY,
        MaxY:  maxY,
        CntsY: cntsY,
        grids: make(map[int]*Grid),
    }

    // Initialize all grids in the AOI initialization area
    for y := 0; y < cntsY; y++ {
        for x := 0; x < cntsX; x++ {
            // Calculate grid ID
            // Grid number: id = idy *nx + idx (using grid coordinates to get grid number)
            gid := y*cntsX + x

            // Initialize a grid and place it in the AOI map, key is the current grid's ID
            aoiMgr.grids[gid] = NewGrid(gid,
                aoiMgr.MinX+x*aoiMgr.gridWidth(),
                aoiMgr.MinX+(x+1)*aoiMgr.gridWidth(),
                aoiMgr.MinY+y*aoiMgr.gridLength(),
                aoiMgr.MinY+(y+1)*aoiMgr.gridLength())
        }
    }

    return aoiMgr
}
Enter fullscreen mode Exit fullscreen mode

The AOIManager still contains boundary coordinates such as MinX, MinY, MaxX, and MaxY, as well as CntsX and CntsY representing the number of grid cells on the x and y axes. The member attribute grids manages which grids are present in the current AOIManager area using a map. The keys are the `IDs of each grid, and the values are the grid objects.

The NewAOIManager() method is used to initialize an AOI area and returns an AOIManager object. During initialization, the AOIManager creates and calculates grid IDs and boundaries for all grids in the area. It creates a total of CntsX * CntsY grids, calculates each grid's ID and boundaries using the formula mentioned earlier, and adds the created grids to the grids collection.

The AOIManager area grid management module also includes other methods, such as obtaining the width and length of each grid in the x-axis and printing the coordinates and information of all grids in the current area.

The code implementation is as follows:
`go
// mmo_game/core/aoi.go

// Get the width of each grid in the x-axis direction
func (m *AOIManager) gridWidth() int {
return (m.MaxX - m.MinX) / m.CntsX
}

// Get the length of each grid in the x-axis direction
func (m *AOIManager) gridLength() int {
return (m.MaxY - m.MinY) / m.CntsY
}

// String() method to print information
func (m *AOIManager) String() string {
s := fmt.Sprintf("AOIManager:\nminX:%d, maxX:%d, cntsX:%d, minY:%d, maxY:%d, cntsY:%d\n Grids in AOI Manager:\n",
m.MinX, m.MaxX, m.CntsX, m.MinY, m.MaxY, m.CntsY)
for _, grid := range m.grids {
s += fmt.Sprintln(grid)
}

    return s
Enter fullscreen mode Exit fullscreen mode

}
`
The above code creates an AOI management module, which can be understood as a 2D rectangular map containing multiple grids. The NewAOIManager() constructor method divides the map into multiple smaller grids and initializes their coordinates. The calculation method is straightforward and involves basic geometry calculations.

11.4 Calculating the Nine-Grid

An essential concept in the AOI algorithm is the "nine-grid," which involves determining all the grids adjacent to a given grid. Since a Grid is a quadrilateral, if the grid is not located on the boundary of the AOI area, the combination of itself with the eight other surrounding grids results in a set of nine grids. The concept of the nine-grid is primarily applied in MMO games to trigger a series of logic when a player moves from one area to another. This includes tasks such as displaying the player's field of view, transitioning maps, and determining skill ranges. Therefore, the ability to calculate the nine-grid for each grid in the AOI area is a crucial functionality that the AOIManager must possess.

11.4.1 Finding the Neighboring Grids Based on Grid ID

When determining the neighboring grids based on a given grid ID, several scenarios need to be considered:

(1) When the center grid is not on the boundary of the AOI area, the surrounding nine grids can be fully populated, as shown in Figure 11.3.

Figure 11.3

Figure 11.3

(2) When the center grid is located at the four corners of the AOI area, as illustrated in Figure 11.4.

Figure 11.4

Figure 11.4

(3) When the center grid is located on the AOI boundary (not at the corners), the surrounding grids are missing one column or one row, as shown in Figure 11.5.

Figure 11.5

Figure 11.5
All three cases can be solved using a unified algorithm. The approach involves dynamically drawing vectors. To determine the surrounding nine grids of a particular Grid, you can first check whether there are grids on the left and right sides of the same row as the current Grid. Then, perform the same check for the row above and below. This way, by performing two loops of checks, you can determine the neighboring grids around the current Grid, as illustrated in Figure 11.6.

Figure 11.6

Figure 11.6

In the case depicted in Figure 11.6, with the central node being the Grid with GID 16, the process begins by traversing the row where the Grid 16 is located to identify the grids to its left and right. This results in a set of Grids with IDs 15, 16, and 17. Next, the column where Grid 15 is situated is examined to identify any grids above and below it, leading to a set of Grids with IDs 10, 15, and 20. Similar logic is applied to Grids 11, 16, and 21, as well as Grids 12, 17, and 22. The resulting set of grids, including Grid 16` itself, forms the complete nine-grid neighborhood.

For situations where the central grid lies at a corner of the AOI region, as shown in Figure 11.7, the same logic applies.

Figure 11.7

Figure 11.7

If a grid is located at a corner of the AOI region, the algorithm remains unchanged, except that during the first vector traversal, there will be some portion that cannot be included. For example, when dealing with a grid with GID 0 and determining its nine-grid neighborhood, the initial step involves checking if there are grids to its left and right, resulting in a set containing IDs 0 and 1. Next, based on grids 0 and 1, a check is performed to determine whether there are grids above and below them, leading to sets 0, 5, and 1, 6, respectively. The combination of these two iterations represents the nine-grid neighborhood of grid 0. Similar considerations apply to other corner cases.

The final scenario involves the central grid being located on the boundary of the AOI region. In this situation, the same approach described above can be employed to determine the nine-grid region. For instance, if we want to find the nine-grid neighborhood of grid 5, we first check if there are grids to its left and right, resulting in the set 5, 6. Then, based on grids 5 and 6, we respectively determine if there are grids above and below them, resulting in sets 0, 5, 10, and 1, 6, 11. Ultimately, all the grids that are traversed during this process constitute the nine-grid neighborhood of the central grid with GID 5.

Figure 11.8

The algorithm to determine the surrounding grids as described above is implemented in the function GetSurroundGridsByGid()and is defined in the following code:

//mmo_game/core/aoi.go

// Get the surrounding nine grids information based on the given grid's GID
func (m *AOIManager) GetSurroundGridsByGid(gID int) (grids []*Grid) {
    // Check if the gID exists
    if _, ok := m.grids[gID]; !ok {
        return
    }

    // Add the current gid to the list of grids
    grids = append(grids, m.grids[gID])

    // Get the X-axis index of the current grid based on gid
    idx := gID % m.CntsX

    // Check if there's a grid to the left of the current idx
    if idx > 0 {
        grids = append(grids, m.grids[gID-1])
    }
    // Check if there's a grid to the right of the current idx
    if idx < m.CntsX-1 {
        grids = append(grids, m.grids[gID+1])
    }

    // Extract all grids from the current x-axis for further traversal,
    // and determine whether each grid has a grid above and below it

    // Get the collection of grid ids in the current x-axis
    gidsX := make([]int, 0, len(grids))
    for _, v := range grids {
        gidsX = append(gidsX, v.GID)
    }

    // Traverse the grids on the x-axis
    for _, v := range gidsX {
        // Calculate in which row the grid is located
        idy := v / m.CntsX

        // Check if there's a grid above the current idy
        if idy > 0 {
            grids = append(grids, m.grids[v-m.CntsX])
        }
        // Check if there's a grid below the current idy
        if idy < m.CntsY-1 {
            grids = append(grids, m.grids[v+m.CntsX])
        }
    }

    return
}
Enter fullscreen mode Exit fullscreen mode

When checking for left-right and top-bottom adjacent grids in the code, it's important to consider boundary points. If the boundary conditions are not met, it indicates that there are no grids in that particular direction.

11.4.2 Determining the Nine Grids Based on Coordinates

Another scenario is when a player knows only their own coordinates. In this case, how can we determine which players are present in the surrounding nine grids of the player's AOI? To achieve this, we need to design an interface that calculates the players in the neighboring nine grids based on coordinates.

The overall approach is to first calculate the grid ID to which the current player's coordinates belong. Then, based on the obtained grid ID, we can retrieve information about the surrounding nine grids. This process is illustrated in Figure 11.9.

Figure 11.9

Figure 11.9

Obtaining the Current Grid ID Based on Player Coordinates is done through the interface GetGIDByPos(), as implemented in the following code:

//mmo_game/core/aoi.go

// Get the corresponding grid ID based on x and y coordinates
func (m *AOIManager) GetGIDByPos(x, y float32) int {
    gx := (int(x) - m.MinX) / m.gridWidth()
    gy := (int(y) - m.MinY) / m.gridLength()

    return gy * m.CntsX + gx
}
Enter fullscreen mode Exit fullscreen mode

Additionally, the interface to get all player IDs within the surrounding nine grids based on player's x and y coordinates is defined as follows:

//mmo_game/core/aoi.go

// Get all PlayerIDs within the surrounding nine grids based on x and y coordinates
func (m *AOIManager) GetPIDsByPos(x, y float32) (playerIDs []int) {
    // Get the grid ID to which the coordinates belong
    gID := m.GetGIDByPos(x, y)

    // Get information about the surrounding nine grids based on grid ID
    grids := m.GetSurroundGridsByGid(gID)
    for _, v := range grids {
        playerIDs = append(playerIDs, v.GetPlayerIDs()...)
        fmt.Printf("===> grid ID : %d, pids : %v  ====", v.GID, v.GetPlayerIDs())
    }

    return
}
Enter fullscreen mode Exit fullscreen mode

The approach of this interface is to first call GetGIDByPos() to determine the grid to which the coordinates belong, obtaining the GID. Next, by invoking the function GetSurroundGridsByGid() which retrieves information about the surrounding nine grids, it obtains the grid collection. Finally, it extracts all player ID information from each grid in the collection.

11.5 AOI Region Grid Addition and Deletion Operations

In this section, we will implement the grid-related addition and removal operations within the AOIManager. The implementation of these methods is relatively straightforward, as shown in the following code:

//mmo_game/core/aoi.go

// Get all player IDs within a current grid based on GID
func (m *AOIManager) GetPidsByGid(gID int) (playerIDs []int) {
    playerIDs = m.grids[gID].GetPlayerIDs()
    return
}

// Remove a player ID from a grid
func (m *AOIManager) RemovePidFromGrid(pID, gID int) {
    m.grids[gID].Remove(pID)
}

// Add a player ID to a grid
func (m *AOIManager) AddPidToGrid(pID, gID int) {
    m.grids[gID].Add(pID)
}

// Add a player to a grid based on x and y coordinates
func (m *AOIManager) AddToGridByPos(pID int, x, y float32) {
    gID := m.GetGIDByPos(x, y)
    grid := m.grids[gID]
    grid.Add(pID)
}

// Remove a player from the corresponding grid based on x and y coordinates
func (m *AOIManager) RemoveFromGridByPos(pID int, x, y float32) {
    gID := m.GetGIDByPos(x, y)
    grid := m.grids[gID]
    grid.Remove(pID)
}
Enter fullscreen mode Exit fullscreen mode

11.6 Unit Testing of the AOI Module

After implementing all the functionalities related to AOI, it's important to perform unit tests to verify the capabilities provided by the AOI module. Create a unit testing file named aoi_test.go in the mmo_game/core directory and implement two test cases: TestNewAOIManager() and TestAOIManagerSuroundGridsByGid(). Here's the code:

//mmo_game/core/aoi_test.go

package core

import (
    "fmt"
    "testing"
)

func TestNewAOIManager(t *testing.T) {
    aoiMgr := NewAOIManager(100, 300, 4, 200, 450, 5)
    fmt.Println(aoiMgr)
}

func TestAOIManagerSuroundGridsByGid(t *testing.T) {
    aoiMgr := NewAOIManager(0, 250, 5, 0, 250, 5)

    for k, _ := range aoiMgr.grids {
        // Get the surrounding grids for the current grid
        grids := aoiMgr.GetSurroundGridsByGid(k)
        // Get all the IDs of the surrounding grids
        fmt.Println("gid : ", k, " grids len = ", len(grids))
        gIDs := make([]int, 0, len(grids))
        for _, grid := range grids {
            gIDs = append(gIDs, grid.GID)
        }
        fmt.Printf("grid ID: %d, surrounding grid IDs are %v\n", k, gIDs)
    }
}

Enter fullscreen mode Exit fullscreen mode

The test TestNewAOIManager() is used to validate the construction of AOI (Area of Interest) regions and provides a detailed output of the information after AOI region construction. The test results are as follows:

AOIManager:
minX: 100, maxX: 300, cntsX: 4, minY: 200, maxY: 450, cntsY: 5
Grids in AOI Manager:
Grid id: 1, minX: 150, maxX: 200, minY: 200, maxY: 250, playerIDs: map[]
Grid id: 5, minX: 150, maxX: 200, minY: 250, maxY: 300, playerIDs: map[]
Grid id: 6, minX: 200, maxX: 250, minY: 250, maxY: 300, playerIDs: map[]
Grid id: 12, minX: 100, maxX: 150, minY: 350, maxY: 400, playerIDs: map[]
Grid id: 19, minX: 250, maxX: 300, minY: 400, maxY: 450, playerIDs: map[]
Grid id: 7, minX: 250, maxX: 300, minY: 250, maxY: 300, playerIDs: map[]
Grid id: 8, minX: 100, maxX: 150, minY: 300, maxY: 350, playerIDs: map[]
Grid id: 10, minX: 200, maxX: 250, minY: 300, maxY: 350, playerIDs: map[]
Grid id: 11, minX: 250, maxX: 300, minY: 300, maxY: 350, playerIDs: map[]
Grid id: 15, minX: 250, maxX: 300, minY: 350, maxY: 400, playerIDs: map[]
Grid id: 18, minX: 200, maxX: 250, minY: 400, maxY: 450, playerIDs: map[]
Grid id: 0, minX: 100, maxX: 150, minY: 200, maxY: 250, playerIDs: map[]
Grid id: 3, minX: 250, maxX: 300, minY: 200, maxY: 250, playerIDs: map[]
Grid id: 4, minX: 100, maxX: 150, minY: 250, maxY: 300, playerIDs: map[]
Grid id: 14, minX: 200, maxX: 250, minY: 350, maxY: 400, playerIDs: map[]
Grid id: 16, minX: 100, maxX: 150, minY: 400, maxY: 450, playerIDs: map[]
Grid id: 2, minX: 200, maxX: 250, minY: 200, maxY: 250, playerIDs: map[]
Grid id: 9, minX: 150, maxX: 200, minY: 300, maxY: 350, playerIDs: map[]
Grid id: 13, minX: 150, maxX: 200, minY: 350, maxY: 400, playerIDs: map[]
Grid id: 17, minX: 150, maxX: 200, minY: 400, maxY: 450, playerIDs: map[]
Enter fullscreen mode Exit fullscreen mode

The test TestAOIManagerSuroundGridsByGid() validates whether the results of the surrounding grid calculations for the nine-grid system are correct. The execution results are as follows:

gid :  3  grids len =  6
grid ID: 3, surrounding grid IDs are [3 2 4 8 7 9]
gid :  5  grids len =  6
grid ID: 5, surrounding grid IDs are [5 6 0 10 1 11]
gid :  6  grids len =  9
grid ID: 6, surrounding grid IDs are [6 5 7 1 11 0 10 2 12]
gid :  11  grids len =  9
grid ID: 11, surrounding grid IDs are [11 10 12 6 16 5 15 7 17]
gid :  18  grids len =  9
grid ID: 18, surrounding grid IDs are [18 17 19 13 23 12 22 14 24]
gid :  2  grids len =  6
grid ID: 2, surrounding grid IDs are [2 1 3 7 6 8]
gid :  4  grids len =  4
grid ID: 4, surrounding grid IDs are [4 3 9 8]
gid :  7  grids len =  9
grid ID: 7, surrounding grid IDs are [7 6 8 2 12 1 11 3 13]
gid :  8  grids len =  9
grid ID: 8, surrounding grid IDs are [8 7 9 3 13 2 12 4 14]
gid :  19  grids len =  6
grid ID: 19, surrounding grid IDs are [19 18 14 24 13 23]
gid :  22  grids len =  6
grid ID: 22, surrounding grid IDs are [22 21 23 17 16 18]
gid :  0  grids len =  4
grid ID: 0, surrounding grid IDs are [0 1 5 6]
gid :  1  grids len =  6
grid ID: 1, surrounding grid IDs are [1 0 2 6 5 7]
gid :  13  grids len =  9
grid ID: 13, surrounding grid IDs are [13 12 14 8 18 7 17 9 19]
gid :  14  grids len =  6
grid ID: 14, surrounding grid IDs are [14 13 9 19 8 18]
gid :  16  grids len =  9
grid ID: 16, surrounding grid IDs are [16 15 17 11 21 10 20 12 22]
gid :  17  grids len =  9
grid ID: 17, surrounding grid IDs are [17 16 18 12 22 11 21 13 23]
gid :  23  grids len =  6
grid ID: 23, surrounding grid IDs are [23 22 24 18 17 19]
gid :  24  grids len =  4
grid ID: 24, surrounding grid IDs are [24 23 19 18]
gid :  9  grids len =  6
grid ID: 9, surrounding grid IDs are [9 8 4 14 3 13]
gid :  10  grids len =  6
grid ID: 10, surrounding grid IDs are [10 11 5 15 6 16]
gid :  12  grids len =  9
grid ID: 12, surrounding grid IDs are [12 11 13 7 17 6 16 8 18]
gid :  15  grids len =  6
grid ID: 15, surrounding grid IDs are [15 16 10 20 11 21]
gid :  20  grids len =  4
grid ID: 20, surrounding grid IDs are [20 21 15 16]
gid :  21  grids len =  6
grid ID: 21, surrounding grid IDs are [21 20 22 16 15 17]
PASS
ok      zinx/zinx_app_demo/mmo_game/core    0.002s
Enter fullscreen mode Exit fullscreen mode

The results of TestAOIManagerSuroundGridsByGid() match the structure shown in Case and Figure 11.2. The validation confirms that the nine-grid calculation results align with the depicted structure in the diagram.


MMO-GAME Source Code

https://github.com/aceld/zinx/tree/master/zinx_app_demo/mmo_game


[Zinx]

<1.Building Basic Services with Zinx Framework>
<2. Zinx-V0.2 Simple Connection Encapsulation and Binding with Business>
<3.Design and Implementation of the Zinx Framework's Routing Module>
<4.Zinx Global Configuration>
<5.Zinx Message Encapsulation Module Design and Implementation>
<6.Design and Implementation of Zinx Multi-Router Mode>
<7. Building Zinx's Read-Write Separation Model>
<8.Zinx Message Queue and Task Worker Pool Design and Implementation>
<9. Zinx Connection Management and Property Setting>

[Zinx Application - MMO Game Case Study]

<10. Application Case Study using the Zinx Framework>
<11. MMO Online Game AOI Algorithm>
<12.Data Transmission Protocol: Protocol Buffers>
<13. MMO Game Server Application Protocol>
<14. Building the Project and User Login>
<15. World Chat System Implementation>
<16. Online Location Information Synchronization>
<17. Moving position and non-crossing grid AOI broadcasting>
<18. Player Logout>
<19. Movement and AOI Broadcast Across Grids>


Author:
discord: https://discord.gg/xQ8Xxfyfcz
zinx: https://github.com/aceld/zinx
github: https://github.com/aceld
aceld's home: https://yuque.com/aceld

Top comments (0)