From e34f7c922790fff1cfc2394f2c6d56c36a3947e6 Mon Sep 17 00:00:00 2001 From: aceld Date: Wed, 20 Feb 2019 17:40:35 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=9F=BA=E4=BA=8EZinx?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E5=BA=94=E7=94=A8=E6=A1=88=E4=BE=8Bmmo=5Fgam?= =?UTF-8?q?e,=20=E6=96=B0=E5=A2=9EAOI=E5=85=B4=E8=B6=A3=E7=82=B9=E6=A8=A1?= =?UTF-8?q?=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zinx_app_demo/mmo_game/README.md | 3 + zinx_app_demo/mmo_game/core/aoi.go | 171 ++++++++++++++++++++++++ zinx_app_demo/mmo_game/core/aoi_test.go | 27 ++++ zinx_app_demo/mmo_game/core/grid.go | 65 +++++++++ 4 files changed, 266 insertions(+) create mode 100644 zinx_app_demo/mmo_game/README.md create mode 100644 zinx_app_demo/mmo_game/core/aoi.go create mode 100644 zinx_app_demo/mmo_game/core/aoi_test.go create mode 100644 zinx_app_demo/mmo_game/core/grid.go diff --git a/zinx_app_demo/mmo_game/README.md b/zinx_app_demo/mmo_game/README.md new file mode 100644 index 0000000..afba845 --- /dev/null +++ b/zinx_app_demo/mmo_game/README.md @@ -0,0 +1,3 @@ +#基于Zinx的MMO多人在线游戏服务端代码示例 + + diff --git a/zinx_app_demo/mmo_game/core/aoi.go b/zinx_app_demo/mmo_game/core/aoi.go new file mode 100644 index 0000000..5208bbb --- /dev/null +++ b/zinx_app_demo/mmo_game/core/aoi.go @@ -0,0 +1,171 @@ +package core + +import "fmt" + +/* + AOI管理模块 +*/ +type AOIManager struct { + MinX int //区域左边界坐标 + MaxX int //区域右边界坐标 + CntsX int //x方向格子的数量 + MinY int //区域上边界坐标 + MaxY int //区域下边界坐标 + CntsY int //y方向的格子数量 + grids map[int]*Grid //当前区域中都有哪些格子,key=格子ID, value=格子对象 +} + +/* + 初始化一个AOI区域 +*/ +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), + } + + //给AOI初始化区域中所有的格子 + for y := 0; y < cntsY; y++ { + for x := 0; x < cntsX; x++ { + //计算格子ID + //格子编号:id = idy *nx + idx (利用格子坐标得到格子编号) + gid := y*cntsX + x + + //初始化一个格子放在AOI中的map里,key是当前格子的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 +} + +//得到每个格子在x轴方向的宽度 +func (m *AOIManager) gridWidth() int { + return (m.MaxX - m.MinX) / m.CntsX +} + +//得到每个格子在x轴方向的长度 +func (m *AOIManager) gridLength() int { + return (m.MaxY - m.MinY) / m.CntsY +} + +//打印信息方法 +func (m *AOIManager) String() string { + s := fmt.Sprintf("AOIManagr:\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 +} + +//根据格子的gID得到当前周边的九宫格信息 +func (m *AOIManager) GetSurroundGridsByGid(gID int) (grids []*Grid) { + //判断gID是否存在 + if _, ok := m.grids[gID]; !ok { + return + } + + //将当前gid添加到九宫格中 + grids = append(grids, m.grids[gID]) + + //根据gid得到当前格子所在的X轴编号 + idx := gID % m.CntsX + + //判断当前idx左边是否还有格子 + if idx > 0 { + grids = append(grids, m.grids[gID-1]) + } + //判断当前的idx右边是否还有格子 + if idx < m.CntsX - 1 { + grids = append(grids, m.grids[gID+1]) + } + + //将x轴当前的格子都取出,进行遍历,再分别得到每个格子的上下是否有格子 + + //得到当前x轴的格子id集合 + gidsX := make([]int, 0, len(grids)) + for _, v := range grids { + gidsX = append(gidsX, v.GID) + } + + //遍历x轴格子 + for _, v := range gidsX { + //计算该格子处于第几列 + idy := v / m.CntsX + + //判断当前的idy上边是否还有格子 + if idy > 0 { + grids = append(grids, m.grids[v-m.CntsX]) + } + //判断当前的idy下边是否还有格子 + if idy < m.CntsY - 1 { + grids = append(grids, m.grids[v+m.CntsX]) + } + } + + return +} + +//通过横纵坐标获取对应的格子ID +func (m *AOIManager) GetGidByPos(x, y float32) int { + gx := (int(x) - m.MinX) / m.gridWidth() + gy := (int(x) - m.MinY) / m.gridLength() + + return gy * m.CntsX + gx +} + +//通过横纵坐标得到周边九宫格内的全部PlayerIDs +func (m *AOIManager) GetPidsByPos(x, y float32) (playerIDs []int) { + //根据横纵坐标得到当前坐标属于哪个格子ID + gID := m.GetGidByPos(x, y) + + //根据格子ID得到周边九宫格的信息 + grids := m.GetSurroundGridsByGid(gID) + for _, v := range grids { + playerIDs = append(playerIDs, v.GetPlyerIDs()...) + fmt.Printf("===> grid ID : %d, pids : %v ====", v.GID, v.GetPlyerIDs()) + } + + return +} + +//通过GID获取当前格子的全部playerID +func (m *AOIManager) GetPidsByGid(gID int) (playerIDs []int) { + playerIDs = m.grids[gID].GetPlyerIDs() + return +} + +//移除一个格子中的PlayerID +func (m *AOIManager) RemovePidFromGrid(pID, gID int) { + m.grids[gID].Remove(pID) +} + +//添加一个PlayerID到一个格子中 +func (m *AOIManager) AddPidToGrid(pID, gID int) { + m.grids[gID].Add(pID) +} + +//通过横纵坐标添加一个Player到一个格子中 +func (m *AOIManager) AddToGridByPos(pID int, x, y float32) { + gID := m.GetGidByPos(x, y) + grid := m.grids[gID] + grid.Add(pID) +} + +//通过横纵坐标把一个Player从对应的格子中删除 +func (m *AOIManager) RemoveFromGridByPos(pID int, x, y float32) { + gID := m.GetGidByPos(x, y) + grid := m.grids[gID] + grid.Remove(pID) +} \ No newline at end of file diff --git a/zinx_app_demo/mmo_game/core/aoi_test.go b/zinx_app_demo/mmo_game/core/aoi_test.go new file mode 100644 index 0000000..438c25e --- /dev/null +++ b/zinx_app_demo/mmo_game/core/aoi_test.go @@ -0,0 +1,27 @@ +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 { + //得到当前格子周边的九宫格 + grids := aoiMgr.GetSurroundGridsByGid(k) + //得到九宫格所有的IDs + 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) + } +} \ No newline at end of file diff --git a/zinx_app_demo/mmo_game/core/grid.go b/zinx_app_demo/mmo_game/core/grid.go new file mode 100644 index 0000000..9a0f14d --- /dev/null +++ b/zinx_app_demo/mmo_game/core/grid.go @@ -0,0 +1,65 @@ +package core + +import ( + "fmt" + "sync" +) + +/* + 一个地图中的格子类 +*/ +type Grid struct { + GID int //格子ID + MinX int //格子左边界坐标 + MaxX int //格子右边界坐标 + MinY int //格子上边界坐标 + MaxY int //格子下边界坐标 + playerIDs map[int]bool //当前格子内的玩家或者物体成员ID + pIDLock sync.RWMutex //playerIDs的保护map的锁 +} + +//初始化一个格子 +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), + } +} + +//向当前格子中添加一个玩家 +func (g *Grid) Add(playerID int) { + g.pIDLock.Lock() + defer g.pIDLock.Unlock() + + g.playerIDs[playerID] = true +} + +//从格子中删除一个玩家 +func (g *Grid) Remove(playerID int) { + g.pIDLock.Lock() + defer g.pIDLock.Unlock() + + delete(g.playerIDs, playerID) +} + +//得到当前格子中所有的玩家 +func (g *Grid) GetPlyerIDs() (playerIDs []int) { + g.pIDLock.RLock() + defer g.pIDLock.RUnlock() + + for k, _ := range g.playerIDs { + playerIDs = append(playerIDs, k) + } + + return +} + +//打印信息方法 +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) +}