Zinx下MMO游戏具体实现-01地图格子模块和AOI管理模块
AOI兴趣点算法
首先绘制一个2D的地图
1.实现格子类
1.1.创建一个地图中的格子类,并实现初始化函数
type Grid struct {
//格子ID
GID int
//格子的左边边界坐标
MinX int
//格子的右边边界的坐标
MaxX int
//格子的上边边界的坐标
MinY int
//格子的下边边界的坐标
MaxY int
//当前格子内 玩家/物体 成员的ID集合 map[玩家/物体ID]
playerIDs map[int] interface{}
//保护当前格子内容的map的锁
pIDLock sync.RWMutex
}
//初始化格子的方法
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]interface{}),
}
}
1.2.实现格子类的方法
//给格子添加一个玩家
func (g *Grid) Add(playerID int, player interface{}) {
g.pIDLock.Lock()
defer g.pIDLock.Unlock()
g.playerIDs[playerID] = player
}
//从格子中删除一个玩家
func (g *Grid) Remove(playerID int) {
g.pIDLock.Lock()
defer g.pIDLock.Unlock()
delete(g.playerIDs, playerID)
}
//得到当前格子所有的玩家ID
func (g *Grid) GetPlayerIDs() (playerIDs []int) {
g.pIDLock.RLock()
defer g.pIDLock.RUnlock()
//遍历map将key封装成一个slice 返回
for playerID,_ := range g.playerIDs {
playerIDs = append(playerIDs, playerID)
}
return
}
调试打印格子信息方法
//调试打印格子信息方法
func (g *Grid) String() string {
return fmt.Sprintf("Grid id : %d, minX:%d, maxX:%d , minY:%d, maxY:%d, playerIDs:%v\n",
g.GID, g.MinX, g.MaxX, g.MinY, g.MaxY, g.playerIDs)
}
2.实现AOI管理模块
2.1.创建AOIManager类并实现初始化
type AOIManager struct {
//区域的左边边界
MinX int
//区域的右边边界
MaxX int
//X轴方向格子的数量
CntsX int
//区域的上边边界
MinY int
//区域的下边边界
MaxY int
//Y轴方向的格子的数量
CntsY int
//整体区域(地图中)拥有哪些格子map:key格子ID,value:格子对象
grids map[int] *Grid
}
//得到每个格子在X轴方向的宽度
func (m *AOIManager) GridWidth() int {
return (m.MaxX-m.MinX) / m.CntsX
}
//得到每个格子在Y轴方向高度
func (m *AOIManager) GridHeight() int {
return (m.MaxY-m.MinY) / m.CntsY
}
//初始化一个地图 AOIManager
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),
}
//隶属于当前地图的全部格子 也一并进行初始化
for y:=0; y < cntsY; y++ {
for x:=0; x < cntsX; x++ {
//初始化一个格子
//格子ID := cntsX * y + x
gid := y*cntsX + x
//给aoiManager添加一个格子
aoiMgr.grids[gid] = NewGrid(gid,
aoiMgr.MinX + x*aoiMgr.GridWidth(),
aoiMgr.MinX + (x+1)*aoiMgr.GridWidth(),
aoiMgr.MinY + y*aoiMgr.GridHeight(),
aoiMgr.MinY + (y+1)*aoiMgr.GridHeight())
}
}
return aoiMgr
}
调试打印当前的地图信息方法
func (m *AOIManager) String() string {
s := fmt.Sprintf("AOIManager : \n MinX:%d,MaxX:%d,cntsX:%d, minY:%d, maxY:%d,cntsY:%d, Grids inManager:\n",
m.MinX, m.MaxX, m.CntsX, m.MinY, m.MaxY, m.CntsY)
//打印全部的格子
for _, grid := range m.grids {
s += fmt.Sprintln(grid)
}
return s
}
2.2.实现AOIManager的方法
//添加一个PlayerID到一个AOI格子中
func (m *AOIManager) AddPidToGrid(pID, gID int) {
m.grids[gID].Add(pID, nil)
}
//移除一个PlayerID 从一个AOI区域中
func (m *AOIManager) RemovePidFromGrid(pID, gID int) {
m.grids[gID].Remove(pID)
}
//通过格子ID获取当前格子的全部PlayerID
func(m *AOIManager) GetPidsByGid(gID int) (playerIDs []int) {
playerIDs = m.grids[gID].GetPlayerIDs()
return
}
//通过一个格子ID得到当前格子的周边九宫格的格子ID集合
func (m *AOIManager) GetSurroundGridsByGid(gID int) (grids []*Grid) {
//判断gid是否在AOI中
if _, ok := m.grids[gID]; !ok {
return
}
//将当前中心GID放入九宫格切片中
grids = append(grids, m.grids[gID])
//==== > 判读GID左边是否有格子? 右边是否有格子
//通过格子ID 得到x轴编号 idx = gID % cntsX
idx := gID % m.CntsX
//判断idx编号左边是否还有格子
if idx > 0 {
//将左边的格子加入到 grids 切片中
grids = append(grids, m.grids[gID-1])
}
//判断idx编号右边是否还有格子
if idx < m.CntsX-1 {
//将右边的格子加入到 grids 切片中
grids = append(grids, m.grids[gID+1])
}
// ===> 得到一个x轴的格子集合,遍历这个格子集合
// for ... 依次判断 格子的上面是否有格子?下面是否有格子
//将X轴全部的Grid ID 放到一个slice中 ,遍历整个slice
gidsX := make([]int, 0, len(grids))
for _, v := range grids {
gidsX = append(gidsX, v.GID)
}
for _, gid := range gidsX {
//10,11,12
//通过Gid得到当前Gid的Y轴编号
//idy = gID / cntsX
idy := gid/m.CntsX
//上方是否还有格子
if idy > 0 {
grids = append(grids, m.grids[gid-m.CntsX])
}
//下方是否还有格子
if idy < m.CntsY-1 {
grids = append(grids, m.grids[gid+m.CntsX])
}
}
return
}
//通过x,y坐标得到对应的格子ID
func (m *AOIManager) GetGidByPos(x, y float32) int {
if x < 0 || int(x) >= m.MaxX {
return -1
}
if y < 0 || int(y) >= m.MaxY {
return -1
}
//根据坐标 得到当前玩家所在格子ID
idx := (int(x) - m.MinX) / m.GridWidth()
idy := (int(y) - m.MinY) / m.GridHeight()
//gid = idy*cntsX + idx
gid := idy * m.CntsX + idx
return gid
}
//根据一个坐标 得到 周边九宫格之内的全部的 玩家ID集合
func (m *AOIManager) GetSurroundPIDsByPos(x, y float32) (playerIDs []int) {
//通过x,y得到一个格子对应的ID
gid := m.GetGidByPos(x,y)
//通过格子ID 得到周边九宫格 集合
grids := m.GetSurroundGridsByGid(gid)
fmt.Println("gid = ", gid)
//将分别将九宫格内的全部的玩家 放在 playerIDs
for _, grid := range grids {
playerIDs = append(playerIDs, grid.GetPlayerIDs()...)
}
return
}
//通过坐标 将pid 加入到一个格子中
func (m *AOIManager) AddToGridByPos(pID int, x, y float32) {
gID := m.GetGidByPos(x,y)
//取出当前的格子
grid := m.grids[gID]
//给格子添加玩家
grid.Add(pID, nil)
}
//通过坐标 把一个player从一个格子中删除
func (m *AOIManager) RemoteFromGridbyPos(pID int , x, y float32) {
gID := m.GetGidByPos(x,y)
grid := m.grids[gID]
grid.Remove(pID)
}