package sprite import ( "image" "image/draw" "image/png" "log" "os" "sort" ) // 设置为true启用调试日志 var debugLog = false func logDebug(format string, args ...interface{}) { if debugLog { log.Printf(format, args...) } } // SpriteIndexNode 表示同一个Index下的精灵链表节点 type SpriteIndexNode struct { Sprite *Sprite Next *SpriteIndexNode // 指向同一Index下的下一个精灵 } // IndexGroup 表示一个索引组,包含所有具有相同Index的精灵 type IndexGroup struct { Index int Head *SpriteIndexNode // 指向第一个精灵 Count int // 该组中精灵数量 Next *IndexGroup // 指向下一个索引组 Prev *IndexGroup // 指向上一个索引组 } // NamedSpriteBoard 提供按名称哈希查找,按索引排序的精灵图管理 type NamedSpriteBoard struct { Sprites []*Sprite // nameMap 用于O(1)的按名称查找,键为Name,值为精灵指针 nameMap map[string]*Sprite // indexGroups 按Index排序的索引组链表 indexHead *IndexGroup // 索引组链表头 indexTail *IndexGroup // 索引组链表尾 // 索引组的映射表,用于O(1)按索引查找索引组 indexMap map[int]*IndexGroup // 总精灵数量 count int } // NewNamedSpriteBoard 创建一个新的NamedSpriteBoard func NewNamedSpriteBoard() *NamedSpriteBoard { return &NamedSpriteBoard{ Sprites: make([]*Sprite, 0), nameMap: make(map[string]*Sprite), indexMap: make(map[int]*IndexGroup), indexHead: nil, indexTail: nil, count: 0, } } // AddSprite 添加精灵,O(1)时间复杂度查找,O(logn)插入 func (b *NamedSpriteBoard) AddSprite(sprite *Sprite) { // 必须有名称 if sprite.Name == "" { return } // 如果已存在同名精灵,先移除 if _, exists := b.nameMap[sprite.Name]; exists { b.RemoveSpriteByName(sprite.Name) } // 将精灵添加到名称映射 b.nameMap[sprite.Name] = sprite // 查找或创建索引组 indexGroup, exists := b.indexMap[sprite.Index] if !exists { // 创建新的索引组 indexGroup = &IndexGroup{ Index: sprite.Index, Head: nil, Count: 0, Next: nil, Prev: nil, } // 将索引组添加到映射表 b.indexMap[sprite.Index] = indexGroup // 插入到索引组链表中的正确位置 b.insertIndexGroup(indexGroup) } // 创建新的精灵节点 newNode := &SpriteIndexNode{ Sprite: sprite, Next: nil, } // 在索引组中按名称排序插入 b.insertSpriteToGroup(indexGroup, newNode) // 更新计数 indexGroup.Count++ b.count++ // 更新兼容层 b.updateSpriteArray() } // insertIndexGroup 将索引组插入到正确的位置,O(logn)时间复杂度 func (b *NamedSpriteBoard) insertIndexGroup(group *IndexGroup) { // 如果是第一个索引组 if b.indexHead == nil { b.indexHead = group b.indexTail = group return } // 如果应该插入到头部 if group.Index < b.indexHead.Index { group.Next = b.indexHead b.indexHead.Prev = group b.indexHead = group return } // 如果应该插入到尾部 if group.Index > b.indexTail.Index { group.Prev = b.indexTail b.indexTail.Next = group b.indexTail = group return } // 找到插入位置 - 将链表的元素放入数组进行二分查找 groups := make([]*IndexGroup, 0) current := b.indexHead for current != nil { groups = append(groups, current) current = current.Next } // 二分查找插入位置 insertPos := sort.Search(len(groups), func(i int) bool { return groups[i].Index >= group.Index }) // 插入到找到的位置之前 prevGroup := groups[insertPos-1] nextGroup := groups[insertPos] group.Next = nextGroup group.Prev = prevGroup prevGroup.Next = group nextGroup.Prev = group } // insertSpriteToGroup 将精灵节点按名称排序插入到索引组,O(n)时间复杂度 // 由于同一个Index下的精灵数量通常不多,所以使用简单的插入排序 func (b *NamedSpriteBoard) insertSpriteToGroup(group *IndexGroup, node *SpriteIndexNode) { // 如果索引组为空 if group.Head == nil { group.Head = node return } // 如果应该插入到头部 if node.Sprite.Name < group.Head.Sprite.Name { node.Next = group.Head group.Head = node return } // 查找插入位置 current := group.Head for current.Next != nil && current.Next.Sprite.Name < node.Sprite.Name { current = current.Next } // 插入节点 node.Next = current.Next current.Next = node } // GetSpriteByName 通过名称获取精灵,O(1)时间复杂度 func (b *NamedSpriteBoard) GetSpriteByName(name string) *Sprite { sprite, exists := b.nameMap[name] if exists { return sprite } return nil } // GetSpritesByIndex 获取具有特定索引的所有精灵,O(1)查找 + O(n)收集 func (b *NamedSpriteBoard) GetSpritesByIndex(index int) []*Sprite { group, exists := b.indexMap[index] if !exists { return nil } sprites := make([]*Sprite, 0, group.Count) current := group.Head for current != nil { sprites = append(sprites, current.Sprite) current = current.Next } return sprites } // RemoveSpriteByName 通过名称删除精灵,O(1)查找 + O(n)删除 func (b *NamedSpriteBoard) RemoveSpriteByName(name string) bool { sprite, exists := b.nameMap[name] if !exists { return false } // 从名称映射中移除 delete(b.nameMap, name) // 查找对应的索引组 group := b.indexMap[sprite.Index] if group == nil { return false } // 从索引组中移除精灵 removed := b.removeSpriteFromGroup(group, name) if !removed { return false } // 如果索引组变为空,则删除该索引组 if group.Count == 0 { b.removeIndexGroup(group) } // 更新计数 b.count-- // 更新兼容层 b.updateSpriteArray() return true } // removeSpriteFromGroup 从索引组中移除指定名称的精灵,O(n)时间复杂度 func (b *NamedSpriteBoard) removeSpriteFromGroup(group *IndexGroup, name string) bool { logDebug("removeSpriteFromGroup: 尝试从索引组 %d 中移除精灵 %s", group.Index, name) // 如果索引组为空 if group.Head == nil { logDebug("removeSpriteFromGroup: 索引组为空") return false } // 如果是头节点 if group.Head.Sprite.Name == name { logDebug("removeSpriteFromGroup: 移除头节点 %s", name) group.Head = group.Head.Next group.Count-- return true } // 查找节点 current := group.Head for current.Next != nil && current.Next.Sprite.Name != name { logDebug("removeSpriteFromGroup: 检查节点 %s", current.Next.Sprite.Name) current = current.Next } // 如果找到了节点 if current.Next != nil { logDebug("removeSpriteFromGroup: 找到并移除节点 %s", name) current.Next = current.Next.Next group.Count-- return true } logDebug("removeSpriteFromGroup: 未找到节点 %s", name) return false } // removeIndexGroup 从索引组链表中移除索引组,O(1)时间复杂度 func (b *NamedSpriteBoard) removeIndexGroup(group *IndexGroup) { // 从索引映射表中移除 delete(b.indexMap, group.Index) // 从链表中移除 if group.Prev != nil { group.Prev.Next = group.Next } else { b.indexHead = group.Next // 头节点变更 } if group.Next != nil { group.Next.Prev = group.Prev } else { b.indexTail = group.Prev // 尾节点变更 } } // UpdateSpriteIndex 更新精灵的索引,O(1)查找 + O(logn)插入 func (b *NamedSpriteBoard) UpdateSpriteIndex(name string, newIndex int) bool { sprite, exists := b.nameMap[name] if !exists { return false } // 保存旧索引 oldIndex := sprite.Index // 从旧索引组中移除 oldGroup := b.indexMap[oldIndex] if oldGroup == nil { return false } if !b.removeSpriteFromGroup(oldGroup, name) { return false } // 如果旧索引组变为空,则删除 if oldGroup.Count == 0 { b.removeIndexGroup(oldGroup) } // 更新精灵的索引 sprite.Index = newIndex // 查找或创建新索引组 newGroup, exists := b.indexMap[newIndex] if !exists { // 创建新的索引组 newGroup = &IndexGroup{ Index: newIndex, Head: nil, Count: 0, Next: nil, Prev: nil, } // 将索引组添加到映射表 b.indexMap[newIndex] = newGroup // 插入到索引组链表中的正确位置 b.insertIndexGroup(newGroup) } // 创建新的精灵节点 newNode := &SpriteIndexNode{ Sprite: sprite, Next: nil, } // 在新索引组中按名称排序插入 b.insertSpriteToGroup(newGroup, newNode) newGroup.Count++ // 更新兼容层 b.updateSpriteArray() return true } // UpdateSpriteName 更新精灵的名称,O(1)查找 + O(n)插入 func (b *NamedSpriteBoard) UpdateSpriteName(oldName, newName string) bool { logDebug("UpdateSpriteName: 尝试将 %s 更新为 %s", oldName, newName) sprite, exists := b.nameMap[oldName] if !exists { logDebug("UpdateSpriteName: 未找到精灵 %s", oldName) return false } // 检查新名称是否已存在 if _, exists := b.nameMap[newName]; exists && newName != oldName { logDebug("UpdateSpriteName: 新名称 %s 已存在", newName) return false } // 保存索引 index := sprite.Index logDebug("UpdateSpriteName: 精灵索引为 %d", index) // 从索引组中移除再添加,以保持排序 group := b.indexMap[index] if group == nil { logDebug("UpdateSpriteName: 未找到索引组 %d", index) return false } logDebug("UpdateSpriteName: 在索引组 %d 中查找名称 %s", index, oldName) // 调试输出当前索引组的所有精灵 current := group.Head i := 0 for current != nil { logDebug("UpdateSpriteName: 索引组精灵[%d]: %s", i, current.Sprite.Name) current = current.Next i++ } // 从索引组链表中删除节点 if !b.removeSpriteFromGroup(group, oldName) { logDebug("UpdateSpriteName: 从索引组中移除精灵 %s 失败", oldName) return false } // 从名称映射中移除旧名称 delete(b.nameMap, oldName) // 更新精灵名称 sprite.Name = newName // 添加到新名称映射 b.nameMap[newName] = sprite // 创建新节点并添加回索引组 newNode := &SpriteIndexNode{ Sprite: sprite, Next: nil, } b.insertSpriteToGroup(group, newNode) group.Count++ // 增加计数,因为removeSpriteFromGroup已经减少了计数 // 更新兼容层 b.updateSpriteArray() logDebug("UpdateSpriteName: 成功更新名称从 %s 到 %s", oldName, newName) return true } // GetAllSprites 获取所有精灵,按Index主排序,Name次排序 func (b *NamedSpriteBoard) GetAllSprites() []*Sprite { sprites := make([]*Sprite, 0, b.count) // 遍历所有索引组 currentGroup := b.indexHead for currentGroup != nil { // 遍历当前索引组中的所有精灵 currentNode := currentGroup.Head for currentNode != nil { sprites = append(sprites, currentNode.Sprite) currentNode = currentNode.Next } currentGroup = currentGroup.Next } return sprites } // updateSpriteArray 更新BaseBoard中的Sprites数组以保持兼容性 func (b *NamedSpriteBoard) updateSpriteArray() { b.Sprites = b.GetAllSprites() } // RenderToImage 将精灵板渲染为图像 func (b *NamedSpriteBoard) RenderToImage() *image.RGBA { // 计算所有精灵图覆盖的最大区域 var minX, minY, maxX, maxY int // 初始化为最大/最小可能值,以便于后续比较 initialized := false // 遍历所有精灵计算边界 currentGroup := b.indexHead for currentGroup != nil { currentNode := currentGroup.Head for currentNode != nil { sprite := currentNode.Sprite if sprite.Image != nil { srcBounds := sprite.Image.Bounds() spriteMinX := sprite.Position.X spriteMinY := sprite.Position.Y spriteMaxX := sprite.Position.X + srcBounds.Dx() spriteMaxY := sprite.Position.Y + srcBounds.Dy() // 首次初始化边界值或更新边界 if !initialized { minX, minY = spriteMinX, spriteMinY maxX, maxY = spriteMaxX, spriteMaxY initialized = true } else { // 更新边界值 if spriteMinX < minX { minX = spriteMinX } if spriteMinY < minY { minY = spriteMinY } if spriteMaxX > maxX { maxX = spriteMaxX } if spriteMaxY > maxY { maxY = spriteMaxY } } } currentNode = currentNode.Next } currentGroup = currentGroup.Next } // 如果没有精灵图,则使用默认大小 if !initialized { minX, minY = 0, 0 maxX, maxY = 1, 1 } // 确保有最小尺寸 if maxX <= minX { maxX = minX + 1 } if maxY <= minY { maxY = minY + 1 } // 计算画布尺寸,保留负坐标空间 width := maxX - minX height := maxY - minY // 创建画布,大小由精灵图决定 img := image.NewRGBA(image.Rect(0, 0, width, height)) // 按索引排序遍历所有精灵组 currentGroup = b.indexHead for currentGroup != nil { currentNode := currentGroup.Head for currentNode != nil { sprite := currentNode.Sprite if sprite.Image != nil { // 获取精灵的边界 srcBounds := sprite.Image.Bounds() // 计算原始目标区域(相对于minX, minY偏移) dstMinX := sprite.Position.X - minX dstMinY := sprite.Position.Y - minY dstMaxX := dstMinX + srcBounds.Dx() dstMaxY := dstMinY + srcBounds.Dy() // 计算源图像的裁剪区域(如果精灵部分在画布外) srcMinX := srcBounds.Min.X srcMinY := srcBounds.Min.Y // 处理左边界超出 if dstMinX < 0 { srcMinX -= dstMinX // 向右移动源图像起点 dstMinX = 0 } // 处理上边界超出 if dstMinY < 0 { srcMinY -= dstMinY // 向下移动源图像起点 dstMinY = 0 } // 处理右边界超出 if dstMaxX > width { dstMaxX = width } // 处理下边界超出 if dstMaxY > height { dstMaxY = height } // 如果裁剪后的区域有效(宽度和高度都大于0) if dstMinX < dstMaxX && dstMinY < dstMaxY { dstRect := image.Rect(dstMinX, dstMinY, dstMaxX, dstMaxY) srcPt := image.Point{X: srcMinX, Y: srcMinY} // 绘制裁剪后的精灵到画布上 draw.Draw(img, dstRect, sprite.Image, srcPt, draw.Over) } } // 移动到下一个精灵 currentNode = currentNode.Next } // 移动到下一个索引组 currentGroup = currentGroup.Next } return img } func (b *NamedSpriteBoard) GetRenderBounds() (minX, minY, maxX, maxY int) { // 计算所有精灵图覆盖的最大区域 // 初始化为最大/最小可能值,以便于后续比较 initialized := false // 遍历所有精灵计算边界 currentGroup := b.indexHead for currentGroup != nil { currentNode := currentGroup.Head for currentNode != nil { sprite := currentNode.Sprite if sprite.Image != nil { srcBounds := sprite.Image.Bounds() spriteMinX := sprite.Position.X spriteMinY := sprite.Position.Y spriteMaxX := sprite.Position.X + srcBounds.Dx() spriteMaxY := sprite.Position.Y + srcBounds.Dy() // 首次初始化边界值或更新边界 if !initialized { minX, minY = spriteMinX, spriteMinY maxX, maxY = spriteMaxX, spriteMaxY initialized = true } else { // 更新边界值 if spriteMinX < minX { minX = spriteMinX } if spriteMinY < minY { minY = spriteMinY } if spriteMaxX > maxX { maxX = spriteMaxX } if spriteMaxY > maxY { maxY = spriteMaxY } } } currentNode = currentNode.Next } currentGroup = currentGroup.Next } // 如果没有精灵图,则使用默认大小 if !initialized { minX, minY = 0, 0 maxX, maxY = 1, 1 } // 确保有最小尺寸 if maxX <= minX { maxX = minX + 1 } if maxY <= minY { maxY = minY + 1 } return minX, minY, maxX, maxY } // SaveToPng 将精灵板渲染为图像并保存到文件 func (b *NamedSpriteBoard) SaveToPng(filename string) error { // 渲染图像 img := b.RenderToImage() // 创建文件 f, err := os.Create(filename) if err != nil { return err } defer f.Close() // 将图像编码为PNG并写入文件 if err := png.Encode(f, img); err != nil { return err } return nil }