├── README.md ├── creature.go ├── grid.go ├── aoi_test.go └── gridMgr.go /README.md: -------------------------------------------------------------------------------- 1 | # gridview 2 | # gridview 3 | -------------------------------------------------------------------------------- /creature.go: -------------------------------------------------------------------------------- 1 | package gridview 2 | 3 | type creature struct { 4 | posX int 5 | posY int 6 | id string 7 | } 8 | 9 | func newCreature(posX, posY int, id string) *creature { 10 | obj := &creature{ 11 | posX: posX, 12 | posY: posY, 13 | id: id, 14 | } 15 | return obj 16 | } 17 | -------------------------------------------------------------------------------- /grid.go: -------------------------------------------------------------------------------- 1 | package gridview 2 | 3 | import "sync" 4 | 5 | type grid struct { 6 | gridId int 7 | row int 8 | col int 9 | 10 | // 网格矩形坐标 11 | minX int 12 | maxX int 13 | minY int 14 | maxY int 15 | 16 | // 集合 17 | palyers map[string]bool 18 | mutex sync.RWMutex 19 | } 20 | 21 | func (g *grid) AddPlayer(playerId string) { 22 | g.mutex.RLock() 23 | defer g.mutex.RUnlock() 24 | g.palyers[playerId] = true 25 | } 26 | 27 | func (g *grid) DeletePlayer(playerId string) { 28 | g.mutex.RLock() 29 | defer g.mutex.RUnlock() 30 | delete(g.palyers, playerId) 31 | } 32 | 33 | func NewGrid(_gridId int, 34 | _minX int, 35 | _maxX int, 36 | _minY int, 37 | _maxY int, 38 | _row int, 39 | _col int) *grid { 40 | obj := &grid{ 41 | gridId: _gridId, 42 | minX: _minX, 43 | maxX: _maxX, 44 | minY: _minY, 45 | maxY: _maxY, 46 | row: _row, 47 | col: _col, 48 | palyers: make(map[string]bool, 0), 49 | } 50 | return obj 51 | } 52 | -------------------------------------------------------------------------------- /aoi_test.go: -------------------------------------------------------------------------------- 1 | package gridview 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "strconv" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | var ( 12 | Debug = false 13 | ) 14 | 15 | func init() { 16 | rand.Seed(time.Now().UnixNano()) 17 | } 18 | 19 | func randCoord(min, max int) int { 20 | return min + rand.Intn(max-min) 21 | } 22 | 23 | func TestGrid(t *testing.T) { 24 | mapGrids := NewGridMgr(0, 4000, 0, 4000, 20, 20) 25 | 26 | // 初始化20000个player 27 | creatures := make(map[string]*creature, 200) 28 | for i := 1; i <= 20000; i++ { 29 | posX := rand.Intn(4000) 30 | posY := rand.Intn(4000) 31 | pid := strconv.Itoa(i) 32 | obj := newCreature(posX, posY, pid) 33 | creatures[pid] = obj 34 | mapGrids.Enter(posX, posY, pid) 35 | } 36 | 37 | // all creatures move one time 38 | t0 := time.Now() 39 | for _, obj := range creatures { 40 | oldX, oldY := obj.posX, obj.posY 41 | for { 42 | posX, posY := oldX+randCoord(-5, 5), oldY+randCoord(-5, 5) 43 | if mapGrids.ValidPos(posX, posY) { 44 | obj.posX, obj.posY = posX, posY 45 | break 46 | } 47 | } 48 | mapGrids.Move(oldX, oldY, obj.posX, obj.posY, obj.id) 49 | } 50 | sub := time.Now().Sub(t0) 51 | fmt.Printf("all creatures move one time used %v !\n", sub) 52 | 53 | // one creature move 20000 times 54 | t0 = time.Now() 55 | obj := creatures[strconv.Itoa(1+rand.Intn(20000))] 56 | for i := 0; i < 20000; i++ { 57 | oldX, oldY := obj.posX, obj.posY 58 | for { 59 | posX, posY := oldX+randCoord(-10, 10), oldY+randCoord(-10, 10) 60 | if mapGrids.ValidPos(posX, posY) { 61 | obj.posX, obj.posY = posX, posY 62 | break 63 | } 64 | } 65 | 66 | lostGrids, bornGrids, aoiGrids, _ := mapGrids.Move(oldX, oldY, obj.posX, obj.posY, obj.id) 67 | printInfo(lostGrids, bornGrids, aoiGrids, mapGrids, obj) 68 | } 69 | sub = time.Now().Sub(t0) 70 | fmt.Printf("one creature move 20000 times used %v !\n", sub) 71 | } 72 | 73 | func printInfo(lostGrids, bornGrids, aoiGrids map[int]*grid, mapMgr *gridMgr, obj *creature) { 74 | if !Debug { 75 | return 76 | } 77 | 78 | printInfo := func(grids map[int]*grid) { 79 | for _, grid := range grids { 80 | fmt.Printf("grid id:%v,row:%v,col:%v, range:(minX:%v,maxX:%v,minY:%v,maxY:%v)\n", grid.gridId, grid.row, grid.col, grid.minX, grid.maxX, grid.minY, grid.maxY) 81 | } 82 | } 83 | grid := mapMgr.GetGridByPos(obj.posX, obj.posY) 84 | 85 | fmt.Printf("x:%v, y:%v,lostGrids len:%v\n", obj.posX, obj.posY, len(lostGrids)) 86 | printInfo(lostGrids) 87 | print("---------------------------------------\n") 88 | 89 | fmt.Printf("x:%v, y:%v,bornGrids len:%v\n", obj.posX, obj.posY, len(bornGrids)) 90 | printInfo(bornGrids) 91 | print("---------------------------------------\n") 92 | 93 | fmt.Printf("row:%v,col:%v, aoiGrids len:%v\n", grid.row, grid.col, len(aoiGrids)) 94 | printInfo(aoiGrids) 95 | print("****************************************************************\n") 96 | } 97 | -------------------------------------------------------------------------------- /gridMgr.go: -------------------------------------------------------------------------------- 1 | package gridview 2 | 3 | import ( 4 | "math" 5 | "sync" 6 | ) 7 | 8 | type gridMgr struct { 9 | allGrids map[int]*grid 10 | mutex sync.RWMutex 11 | 12 | // 地图大小 13 | minX int 14 | maxX int 15 | minY int 16 | maxY int 17 | 18 | // 行列 19 | row int 20 | col int 21 | 22 | // 格子大小 23 | lenX int 24 | lenY int 25 | } 26 | 27 | func (g *gridMgr) ValidPos(posX, posY int) bool { 28 | if posX <= g.minX || posX > g.maxX || posY <= g.minY || posY > g.maxY { 29 | return false 30 | } 31 | return true 32 | } 33 | 34 | func (g *gridMgr) Enter(posX, posY int, playerId string) map[int]*grid { 35 | grid := g.GetGridByPos(posX, posY) 36 | grid.AddPlayer(playerId) 37 | 38 | return g.GetInterestAreaByPos(posX, posY) 39 | } 40 | 41 | func (g *gridMgr) Leave(posX, posY int, playerId string) map[int]*grid { 42 | grid := g.GetGridByPos(posX, posY) 43 | grid.DeletePlayer(playerId) 44 | 45 | return g.GetInterestAreaByPos(posX, posY) 46 | } 47 | 48 | func (g *gridMgr) Move(oldPosX, oldPosY, posX, posY int, playerId string) (lostGrids, bornGrids, aoiGrids map[int]*grid, isCrossGrid bool) { 49 | oldGrid := g.GetGridByPos(oldPosX, oldPosY) 50 | curGrid := g.GetGridByPos(posX, posY) 51 | 52 | if oldGrid != curGrid { 53 | oldGrid.DeletePlayer(playerId) 54 | curGrid.AddPlayer(playerId) 55 | 56 | oldArea := g.GetInterestAreaByPos(oldPosX, oldPosY) 57 | curArea := g.GetInterestAreaByPos(posX, posY) 58 | 59 | lostGrids = make(map[int]*grid) 60 | bornGrids = make(map[int]*grid) 61 | for _, obj := range oldArea { 62 | if _, exist := curArea[obj.gridId]; !exist { 63 | lostGrids[obj.gridId] = obj 64 | } 65 | } 66 | for _, obj := range curArea { 67 | if _, exist := oldArea[obj.gridId]; !exist { 68 | bornGrids[obj.gridId] = obj 69 | } 70 | } 71 | } 72 | 73 | aoiGrids = g.GetInterestAreaByPos(posX, posY) 74 | return 75 | } 76 | 77 | func (g *gridMgr) AddAoiByGridId(gridId int, playerId string) { 78 | g.mutex.RLock() 79 | defer g.mutex.RUnlock() 80 | 81 | grid := g.allGrids[gridId] 82 | grid.AddPlayer(playerId) 83 | } 84 | 85 | func (g *gridMgr) AddAoiByPos(posX, posY int, playerId string) { 86 | grid := g.GetGridByPos(posX, posY) 87 | grid.AddPlayer(playerId) 88 | } 89 | 90 | func (g *gridMgr) GetGridByPos(posX, posY int) *grid { 91 | row, col := g.GetRowColByPos(posX, posY) 92 | result := g.GetGridByRowCol(row, col) 93 | if result == nil { 94 | print("\n GetGridByPos grid is nil:", row, "---", col) 95 | } 96 | return result 97 | } 98 | 99 | func (g *gridMgr) GetGridByRowCol(row, col int) *grid { 100 | gridId := col + (row-1)*g.col 101 | return g.GetGridById(gridId) 102 | } 103 | 104 | func (g *gridMgr) GetGridById(gridId int) *grid { 105 | g.mutex.RLock() 106 | defer g.mutex.RUnlock() 107 | return g.allGrids[gridId] 108 | } 109 | 110 | func (g *gridMgr) GetRowColByPos(posX, posY int) (int, int) { 111 | col := int(math.Ceil(float64(posX) / float64(g.lenX))) 112 | row := int(math.Ceil(float64(posY) / float64(g.lenY))) 113 | if col == 0 { 114 | col += 1 115 | } 116 | if row == 0 { 117 | row += 1 118 | } 119 | return row, col 120 | } 121 | 122 | func (g *gridMgr) GetInterestAreaByPos(posX, posY int) map[int]*grid { 123 | row, col := g.GetRowColByPos(posX, posY) 124 | 125 | curGrid := g.GetGridByRowCol(row, col) 126 | grids := make(map[int]*grid, 9) 127 | midGrids := make(map[int]*grid, 3) 128 | grids[curGrid.gridId] = curGrid 129 | midGrids[curGrid.gridId] = curGrid 130 | 131 | // 中间左边 132 | if col > 1 { 133 | temp := g.GetGridByRowCol(row, col-1) 134 | grids[temp.gridId] = temp 135 | midGrids[temp.gridId] = temp 136 | } 137 | // 中间右边 138 | if col < g.col { 139 | temp := g.GetGridByRowCol(row, col+1) 140 | grids[temp.gridId] = temp 141 | midGrids[temp.gridId] = temp 142 | } 143 | 144 | // 遍历中间,搜索上下行 145 | for _, grid := range midGrids { 146 | if grid.row > 1 { 147 | temp := g.GetGridByRowCol(grid.row-1, grid.col) 148 | grids[temp.gridId] = temp 149 | } 150 | if grid.row < g.row { 151 | temp := g.GetGridByRowCol(grid.row+1, grid.col) 152 | grids[temp.gridId] = temp 153 | } 154 | } 155 | return grids 156 | } 157 | 158 | func NewGridMgr(_minX int, 159 | _maxX int, 160 | _minY int, 161 | _maxY int, 162 | _lenX int, 163 | _lenY int, 164 | ) *gridMgr { 165 | obj := &gridMgr{ 166 | minX: _minX, 167 | maxX: _maxX, 168 | minY: _minY, 169 | maxY: _maxY, 170 | lenX: _lenX, 171 | lenY: _lenY, 172 | allGrids: make(map[int]*grid), 173 | } 174 | 175 | // 初始化网格 176 | col := int(math.Ceil(float64((_maxX - _minX) / _lenX))) 177 | row := int(math.Ceil(float64((_maxY - _minY) / _lenY))) 178 | for i := 1; i <= col; i++ { 179 | for j := 0; j < row; j++ { 180 | id := i + j*col 181 | gridMinX := i*_lenX - _lenX 182 | gridMaxX := i * _lenX 183 | girdMinY := j * _lenY 184 | girdMaxY := j*_lenY + _lenY 185 | grid := NewGrid(id, gridMinX, gridMaxX, girdMinY, girdMaxY, j+1, i) 186 | obj.allGrids[id] = grid 187 | } 188 | } 189 | obj.col = col 190 | obj.row = row 191 | 192 | return obj 193 | } 194 | --------------------------------------------------------------------------------