package aoiGrid import ( //"f1-game/internal/algorithm/aoi" "f1-game/internal/types" "math" clog "github.com/cherry-game/cherry/logger" ) type ( Config struct { Width int32 // 地图宽(x) Height int32 // 地图高(y) GridWidth int32 // 网格宽(x) GridHeight int32 // 网格高(y) } AOI struct { EventBase config Config gridNumX int32 // x轴的格子数量 gridNumY int32 // y轴的格子数量 grids map[int32]*Grid // 所有格子数 key:id,value:*Grid objects Objects // 所有对象 key:ObjectID, value:*aoi.Object } ) func New(config Config) *AOI { gridAOI := &AOI{ EventBase: EventBase{}, config: config, grids: make(map[int32]*Grid), objects: Objects{}, } gridAOI.init() return gridAOI } func (p *AOI) init() { if p.config.Width < 1 || p.config.Height < 1 { clog.Panicf("Config params error. %v", p) } if p.config.GridWidth < 1 /*|| p.config.GridWidth > p.config.Width*/ { clog.Panicf("Config params error. %v", p) } if p.config.GridHeight < 1 /*|| p.config.GridHeight > p.config.Height*/ { clog.Panicf("Config params error. %v", p) } p.gridNumX = int32(math.Ceil(float64(p.config.Width) / float64(p.config.GridWidth))) p.gridNumY = int32(math.Ceil(float64(p.config.Height) / float64(p.config.GridHeight))) for y := range p.gridNumY { for x := range p.gridNumX { grid := p.newGrid(x, y) p.grids[grid.id] = grid } } } func (p *AOI) Get(objectID int64) *Object { return p.objects.Get(objectID) } func (p *AOI) Enter(object Object) bool { gridID := p.GetGridID(object.Point.X, object.Point.Y) grid, found := p.grids[gridID] if !found { clog.Warnf("[Enter] gridID not found. %s", object.Info()) return false } // add object p.objects.Add(&object) if object.IsWatcher() { grid.AddWatcher(&object) if p.WatcherEnterFunc != nil { markers := p.Get9GridMarkers(gridID) if !markers.IsEmpty() { p.WatcherEnterFunc(&object, markers) } } } if object.IsMarker() { grid.AddMarker(&object) if p.MarkerEnterFunc != nil { watchers := p.Get9GridWatchers(gridID) p.MarkerEnterFunc(&object, watchers) } } return true } func (p *AOI) Leave(objectID int64) { object := p.objects.Get(objectID) if object == nil { clog.Warnf("[Leave] objectID = %d not found.", objectID) return } // remove object p.objects.Remove(objectID) gridID := p.GetGridID(object.Point.X, object.Point.Y) grid, found := p.grids[gridID] if !found { clog.Warnf("[Leave] gridID not found. %s", object.Info()) return } if object.IsWatcher() { grid.DelWatcher(object) } if object.IsMarker() { grid.DelMarker(object) if p.MarkerLeaveFunc != nil { delWatchers := p.Get9GridWatchers(gridID) if !delWatchers.IsEmpty() { p.MarkerLeaveFunc(object, delWatchers) } } } } func (p *AOI) Move(objectID int64, x, y int32) bool { object := p.objects.Get(objectID) if object == nil { return false } if object.Point.X == x && object.Point.Y == y { return false } if !p.CheckPoint(x, y) { clog.Warnf("[Move] check point error. %s", object.Info()) return false } oldGridID := p.GetGridID(object.Point.X, object.Point.Y) newGridID := p.GetGridID(x, y) oldMapGrid := p.grids[oldGridID] newMapGrid := p.grids[newGridID] if oldMapGrid == nil || newMapGrid == nil { clog.Warnf("[Move] Get map grid error. %s", object.Info()) return false } // set new point object.SetPoint(x, y) if object.IsWatcher() { if oldGridID != newGridID { oldMapGrid.DelWatcher(object) newMapGrid.AddWatcher(object) if p.WatcherMoveFunc != nil { old9Grids := p.Get9Grids(oldGridID) new9Grids := p.Get9Grids(newGridID) addMarkers, _ := p.GetDiffMarkers(old9Grids, new9Grids) if !addMarkers.IsEmpty() { p.WatcherMoveFunc(object, addMarkers) } } } } if object.IsMarker() { if oldGridID != newGridID { oldMapGrid.DelMarker(object) newMapGrid.AddMarker(object) } if p.MarkerMoveFunc != nil { viewWatchers := p.Get9GridWatchers(newGridID) if !viewWatchers.IsEmpty() { p.MarkerMoveFunc(object, viewWatchers) } } } return true } func (p *AOI) GetWatcherOwners(objectID int64) types.Set[int64] { owners := types.NewSet[int64]() object := p.objects.Get(objectID) if object == nil { return owners } if object.IsWatcher() { clog.Warnf("[GetWatchers] objectID = {%s} is a watcher. get fail.%s", object.Info()) return owners } gridID := p.GetGridID(object.Point.X, object.Point.Y) mapGrid := p.grids[gridID] if mapGrid == nil { clog.Warnf("[GetWatchers] objectID = {%s}, old tower not found. %s", object.Info()) return owners } for _, watcher := range mapGrid.watchers { owners.Add(watcher.OwnerID()) } return owners } func (p *AOI) newGrid(x, y int32) *Grid { var ( id = y*p.gridNumX + x // 0开头 minX = x * p.config.GridWidth minY = y * p.config.GridHeight maxX = (x + 1) * p.config.GridWidth maxY = (y + 1) * p.config.GridHeight ) grid := &Grid{ id: id, minX: minX, minY: minY, maxX: maxX, maxY: maxY, markers: Objects{}, watchers: Objects{}, } return grid } func (p *AOI) GetGridID(x, y int32) int32 { if x >= p.config.Width { x = x - 1 } if y >= p.config.Height { y = y - 1 } // y / 格子高度 * x轴格子数量 + x / y轴格子数量 return y/p.config.GridHeight*p.gridNumX + x/p.gridNumY } func (p *AOI) GetDiffMarkers(oldGrids MapGrid, newGrids MapGrid) (addObjects, delObjects Objects) { bothGridIDMap := map[int32]struct{}{} for _, newGrid := range newGrids { if !oldGrids.Contains(newGrid) { addObjects.Adds(newGrid.markers) } else { bothGridIDMap[newGrid.id] = struct{}{} } } for _, oldGrid := range oldGrids { if _, ok := bothGridIDMap[oldGrid.id]; !ok { delObjects.Adds(oldGrid.markers) } } return } func (p *AOI) Get9GridMarkers(gridID int32) Objects { return p.get9GridObjects(gridID, true) } func (p *AOI) Get9GridWatchers(gridID int32) Objects { return p.get9GridObjects(gridID, false) } func (p *AOI) get9GridObjects(gridID int32, getMarker bool) Objects { var ( grids = p.Get9Grids(gridID) retObjects = Objects{} ) for _, grid := range grids { if getMarker { retObjects.Adds(grid.markers) } else { retObjects.Adds(grid.watchers) } } return retObjects } func (p *AOI) Get9Grids(gridID int32) MapGrid { var ( mapGrid = make(MapGrid, 9) ix = gridID % p.gridNumX iy = gridID / p.gridNumX ) // 中心格子 mapGrid.Add(p.grids[gridID]) // 左 if ix-1 >= 0 { mapGrid.Add(p.grids[gridID-1]) if iy-1 >= 0 { mapGrid.Add(p.grids[gridID-1-p.gridNumX]) } if iy+1 < p.gridNumY { mapGrid.Add(p.grids[gridID-1+p.gridNumX]) } } // 右 if ix+1 < p.gridNumX { mapGrid.Add(p.grids[gridID+1]) if iy-1 >= 0 { mapGrid.Add(p.grids[gridID+1-p.gridNumX]) } if iy+1 < p.gridNumY { mapGrid.Add(p.grids[gridID+1+p.gridNumX]) } } // 下 if iy-1 >= 0 { mapGrid.Add(p.grids[gridID-p.gridNumX]) } // 上 if iy+1 < p.gridNumY { mapGrid.Add(p.grids[gridID+p.gridNumX]) } return mapGrid } // CheckPoint 坐标是否合法 func (p *AOI) CheckPoint(x, y int32) bool { return x >= 0 && y >= 0 && x < p.config.Width && y < p.config.Height }