refactor: 修改精灵板构造函数,移除宽高参数,使用实际内容确定大小。优化图像处理逻辑,更新测试用例以适应新接口。
This commit is contained in:
parent
6cd5efa192
commit
1346911f86
10
sprite/2d.go
10
sprite/2d.go
@ -76,8 +76,8 @@ type Circle struct {
|
||||
Color color.Color
|
||||
}
|
||||
|
||||
// ToSprite 在精灵图上绘制圆形,保留原有的图像
|
||||
func (c *Circle) ToSprite(sprite *Sprite) {
|
||||
// AddToSprite 在精灵图上绘制圆形,保留原有的图像
|
||||
func (c *Circle) AddToSprite(sprite *Sprite) {
|
||||
// 检查精灵是否已有图像
|
||||
if sprite.Image == nil {
|
||||
// 如果没有图像,创建一个新图像
|
||||
@ -94,10 +94,6 @@ func (c *Circle) ToSprite(sprite *Sprite) {
|
||||
// 获取当前图像大小和位置
|
||||
bounds := sprite.Image.Bounds()
|
||||
|
||||
// 计算圆心相对于精灵图像的位置
|
||||
relCenterX := c.Center.X - sprite.Position.X
|
||||
relCenterY := c.Center.Y - sprite.Position.Y
|
||||
|
||||
// 直接使用原图像,不再扩展
|
||||
var newImage *image.RGBA
|
||||
newImage, _ = sprite.Image.(*image.RGBA)
|
||||
@ -109,7 +105,7 @@ func (c *Circle) ToSprite(sprite *Sprite) {
|
||||
|
||||
// 在新图像上绘制圆
|
||||
// 超出图像边界的部分将被自动丢弃
|
||||
drawhelper.DrawCircleOnImage(newImage, relCenterX, relCenterY, c.Radius, c.Color)
|
||||
drawhelper.DrawCircleOnImage(newImage, c.Center.X, c.Center.Y, c.Radius, c.Color)
|
||||
|
||||
// 更新精灵图像
|
||||
sprite.Image = newImage
|
||||
|
@ -77,7 +77,7 @@ func TestLineToSprite(t *testing.T) {
|
||||
|
||||
if !redPixelFound {
|
||||
// 如果没找到,保存图像用于调试
|
||||
board := NewNamedSpriteBoard(100, 100)
|
||||
board := NewNamedSpriteBoard()
|
||||
board.AddSprite(sprite)
|
||||
board.SaveToPng("test_line1.png")
|
||||
t.Errorf("未找到红色线条像素")
|
||||
@ -138,14 +138,14 @@ func TestLineToSprite(t *testing.T) {
|
||||
|
||||
if !bluePixelFound {
|
||||
// 保存图像用于调试
|
||||
board := NewNamedSpriteBoard(100, 100)
|
||||
board := NewNamedSpriteBoard()
|
||||
board.AddSprite(sprite)
|
||||
board.SaveToPng("test_line2.png")
|
||||
t.Errorf("未找到蓝色线条像素")
|
||||
}
|
||||
|
||||
// 总是保存最终图像以便查看结果
|
||||
board := NewNamedSpriteBoard(100, 100)
|
||||
board := NewNamedSpriteBoard()
|
||||
board.AddSprite(sprite)
|
||||
board.SaveToPng("test_line_final.png")
|
||||
}
|
||||
@ -167,7 +167,7 @@ func TestCircleToSprite(t *testing.T) {
|
||||
}
|
||||
|
||||
// 在精灵上绘制圆
|
||||
circle.ToSprite(sprite)
|
||||
circle.AddToSprite(sprite)
|
||||
|
||||
// 验证精灵现在有了图像
|
||||
if sprite.Image == nil {
|
||||
@ -208,7 +208,7 @@ func TestCircleToSprite(t *testing.T) {
|
||||
// 如果找到了绿色像素,则测试通过
|
||||
if len(foundPoints) == 0 {
|
||||
// 保存图像用于调试
|
||||
board := NewNamedSpriteBoard(100, 100)
|
||||
board := NewNamedSpriteBoard()
|
||||
board.AddSprite(sprite)
|
||||
board.SaveToPng("test_circle1.png")
|
||||
t.Errorf("未找到绿色圆形像素")
|
||||
@ -233,7 +233,7 @@ func TestCircleToSprite(t *testing.T) {
|
||||
}
|
||||
|
||||
// 在已有精灵上绘制第二个圆
|
||||
circle2.ToSprite(sprite)
|
||||
circle2.AddToSprite(sprite)
|
||||
|
||||
// 检查图像是否扩展
|
||||
newBounds := sprite.Image.Bounds()
|
||||
@ -269,7 +269,7 @@ func TestCircleToSprite(t *testing.T) {
|
||||
|
||||
if !yellowPixelFound {
|
||||
// 保存图像用于调试
|
||||
board := NewNamedSpriteBoard(100, 100)
|
||||
board := NewNamedSpriteBoard()
|
||||
board.AddSprite(sprite)
|
||||
board.SaveToPng("test_circle2.png")
|
||||
t.Errorf("未找到黄色圆形像素")
|
||||
|
@ -35,8 +35,6 @@ type IndexGroup struct {
|
||||
|
||||
// NamedSpriteBoard 提供按名称哈希查找,按索引排序的精灵图管理
|
||||
type NamedSpriteBoard struct {
|
||||
Width int
|
||||
Height int
|
||||
Sprites []*Sprite
|
||||
// nameMap 用于O(1)的按名称查找,键为Name,值为精灵指针
|
||||
nameMap map[string]*Sprite
|
||||
@ -50,10 +48,8 @@ type NamedSpriteBoard struct {
|
||||
}
|
||||
|
||||
// NewNamedSpriteBoard 创建一个新的NamedSpriteBoard
|
||||
func NewNamedSpriteBoard(width, height int) *NamedSpriteBoard {
|
||||
func NewNamedSpriteBoard() *NamedSpriteBoard {
|
||||
return &NamedSpriteBoard{
|
||||
Width: width,
|
||||
Height: height,
|
||||
Sprites: make([]*Sprite, 0),
|
||||
nameMap: make(map[string]*Sprite),
|
||||
indexMap: make(map[int]*IndexGroup),
|
||||
@ -462,32 +458,122 @@ func (b *NamedSpriteBoard) updateSpriteArray() {
|
||||
|
||||
// RenderToImage 将精灵板渲染为图像
|
||||
func (b *NamedSpriteBoard) RenderToImage() *image.RGBA {
|
||||
// 创建画布
|
||||
img := image.NewRGBA(image.Rect(0, 0, b.Width, b.Height))
|
||||
// 计算所有精灵图覆盖的最大区域
|
||||
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 {
|
||||
// 获取精灵的边界
|
||||
bounds := sprite.Image.Bounds()
|
||||
srcBounds := sprite.Image.Bounds()
|
||||
|
||||
// 计算目标区域
|
||||
min := image.Point{
|
||||
X: sprite.Position.X,
|
||||
Y: sprite.Position.Y,
|
||||
}
|
||||
max := image.Point{
|
||||
X: sprite.Position.X + bounds.Dx(),
|
||||
Y: sprite.Position.Y + bounds.Dy(),
|
||||
// 计算原始目标区域(相对于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
|
||||
}
|
||||
|
||||
// 绘制精灵到画布上
|
||||
draw.Draw(img, image.Rectangle{Min: min, Max: max}, sprite.Image, bounds.Min, draw.Over)
|
||||
// 处理上边界超出
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// 移动到下一个精灵
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
|
||||
func TestNamedSpriteBoard(t *testing.T) {
|
||||
// 创建测试板
|
||||
board := NewNamedSpriteBoard(100, 100)
|
||||
board := NewNamedSpriteBoard()
|
||||
|
||||
// 测试添加精灵
|
||||
sprite1 := &Sprite{
|
||||
@ -186,7 +186,7 @@ func TestNamedSpriteBoard(t *testing.T) {
|
||||
|
||||
// 性能测试
|
||||
func BenchmarkNamedSpriteBoardAdd(b *testing.B) {
|
||||
board := NewNamedSpriteBoard(1000, 1000)
|
||||
board := NewNamedSpriteBoard()
|
||||
img := image.NewRGBA(image.Rect(0, 0, 10, 10))
|
||||
|
||||
b.ResetTimer()
|
||||
@ -203,7 +203,7 @@ func BenchmarkNamedSpriteBoardAdd(b *testing.B) {
|
||||
|
||||
func BenchmarkNamedSpriteBoardGetByName(b *testing.B) {
|
||||
// 准备测试数据
|
||||
board := NewNamedSpriteBoard(1000, 1000)
|
||||
board := NewNamedSpriteBoard()
|
||||
img := image.NewRGBA(image.Rect(0, 0, 10, 10))
|
||||
names := make([]string, 1000)
|
||||
|
||||
@ -227,7 +227,7 @@ func BenchmarkNamedSpriteBoardGetByName(b *testing.B) {
|
||||
|
||||
func BenchmarkNamedSpriteBoardGetByIndex(b *testing.B) {
|
||||
// 准备测试数据
|
||||
board := NewNamedSpriteBoard(1000, 1000)
|
||||
board := NewNamedSpriteBoard()
|
||||
img := image.NewRGBA(image.Rect(0, 0, 10, 10))
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
@ -248,7 +248,7 @@ func BenchmarkNamedSpriteBoardGetByIndex(b *testing.B) {
|
||||
|
||||
func TestNamedSpriteBoardRenderToImage(t *testing.T) {
|
||||
// 创建测试板
|
||||
board := NewNamedSpriteBoard(200, 200)
|
||||
board := NewNamedSpriteBoard()
|
||||
|
||||
// 创建几个具有不同索引的彩色精灵
|
||||
// 索引值小的精灵会被先绘制,索引值大的精灵会覆盖在上面
|
||||
|
Loading…
x
Reference in New Issue
Block a user