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
|
Color color.Color
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToSprite 在精灵图上绘制圆形,保留原有的图像
|
// AddToSprite 在精灵图上绘制圆形,保留原有的图像
|
||||||
func (c *Circle) ToSprite(sprite *Sprite) {
|
func (c *Circle) AddToSprite(sprite *Sprite) {
|
||||||
// 检查精灵是否已有图像
|
// 检查精灵是否已有图像
|
||||||
if sprite.Image == nil {
|
if sprite.Image == nil {
|
||||||
// 如果没有图像,创建一个新图像
|
// 如果没有图像,创建一个新图像
|
||||||
@ -94,10 +94,6 @@ func (c *Circle) ToSprite(sprite *Sprite) {
|
|||||||
// 获取当前图像大小和位置
|
// 获取当前图像大小和位置
|
||||||
bounds := sprite.Image.Bounds()
|
bounds := sprite.Image.Bounds()
|
||||||
|
|
||||||
// 计算圆心相对于精灵图像的位置
|
|
||||||
relCenterX := c.Center.X - sprite.Position.X
|
|
||||||
relCenterY := c.Center.Y - sprite.Position.Y
|
|
||||||
|
|
||||||
// 直接使用原图像,不再扩展
|
// 直接使用原图像,不再扩展
|
||||||
var newImage *image.RGBA
|
var newImage *image.RGBA
|
||||||
newImage, _ = sprite.Image.(*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
|
sprite.Image = newImage
|
||||||
|
@ -77,7 +77,7 @@ func TestLineToSprite(t *testing.T) {
|
|||||||
|
|
||||||
if !redPixelFound {
|
if !redPixelFound {
|
||||||
// 如果没找到,保存图像用于调试
|
// 如果没找到,保存图像用于调试
|
||||||
board := NewNamedSpriteBoard(100, 100)
|
board := NewNamedSpriteBoard()
|
||||||
board.AddSprite(sprite)
|
board.AddSprite(sprite)
|
||||||
board.SaveToPng("test_line1.png")
|
board.SaveToPng("test_line1.png")
|
||||||
t.Errorf("未找到红色线条像素")
|
t.Errorf("未找到红色线条像素")
|
||||||
@ -138,14 +138,14 @@ func TestLineToSprite(t *testing.T) {
|
|||||||
|
|
||||||
if !bluePixelFound {
|
if !bluePixelFound {
|
||||||
// 保存图像用于调试
|
// 保存图像用于调试
|
||||||
board := NewNamedSpriteBoard(100, 100)
|
board := NewNamedSpriteBoard()
|
||||||
board.AddSprite(sprite)
|
board.AddSprite(sprite)
|
||||||
board.SaveToPng("test_line2.png")
|
board.SaveToPng("test_line2.png")
|
||||||
t.Errorf("未找到蓝色线条像素")
|
t.Errorf("未找到蓝色线条像素")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 总是保存最终图像以便查看结果
|
// 总是保存最终图像以便查看结果
|
||||||
board := NewNamedSpriteBoard(100, 100)
|
board := NewNamedSpriteBoard()
|
||||||
board.AddSprite(sprite)
|
board.AddSprite(sprite)
|
||||||
board.SaveToPng("test_line_final.png")
|
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 {
|
if sprite.Image == nil {
|
||||||
@ -208,7 +208,7 @@ func TestCircleToSprite(t *testing.T) {
|
|||||||
// 如果找到了绿色像素,则测试通过
|
// 如果找到了绿色像素,则测试通过
|
||||||
if len(foundPoints) == 0 {
|
if len(foundPoints) == 0 {
|
||||||
// 保存图像用于调试
|
// 保存图像用于调试
|
||||||
board := NewNamedSpriteBoard(100, 100)
|
board := NewNamedSpriteBoard()
|
||||||
board.AddSprite(sprite)
|
board.AddSprite(sprite)
|
||||||
board.SaveToPng("test_circle1.png")
|
board.SaveToPng("test_circle1.png")
|
||||||
t.Errorf("未找到绿色圆形像素")
|
t.Errorf("未找到绿色圆形像素")
|
||||||
@ -233,7 +233,7 @@ func TestCircleToSprite(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 在已有精灵上绘制第二个圆
|
// 在已有精灵上绘制第二个圆
|
||||||
circle2.ToSprite(sprite)
|
circle2.AddToSprite(sprite)
|
||||||
|
|
||||||
// 检查图像是否扩展
|
// 检查图像是否扩展
|
||||||
newBounds := sprite.Image.Bounds()
|
newBounds := sprite.Image.Bounds()
|
||||||
@ -269,7 +269,7 @@ func TestCircleToSprite(t *testing.T) {
|
|||||||
|
|
||||||
if !yellowPixelFound {
|
if !yellowPixelFound {
|
||||||
// 保存图像用于调试
|
// 保存图像用于调试
|
||||||
board := NewNamedSpriteBoard(100, 100)
|
board := NewNamedSpriteBoard()
|
||||||
board.AddSprite(sprite)
|
board.AddSprite(sprite)
|
||||||
board.SaveToPng("test_circle2.png")
|
board.SaveToPng("test_circle2.png")
|
||||||
t.Errorf("未找到黄色圆形像素")
|
t.Errorf("未找到黄色圆形像素")
|
||||||
|
@ -35,8 +35,6 @@ type IndexGroup struct {
|
|||||||
|
|
||||||
// NamedSpriteBoard 提供按名称哈希查找,按索引排序的精灵图管理
|
// NamedSpriteBoard 提供按名称哈希查找,按索引排序的精灵图管理
|
||||||
type NamedSpriteBoard struct {
|
type NamedSpriteBoard struct {
|
||||||
Width int
|
|
||||||
Height int
|
|
||||||
Sprites []*Sprite
|
Sprites []*Sprite
|
||||||
// nameMap 用于O(1)的按名称查找,键为Name,值为精灵指针
|
// nameMap 用于O(1)的按名称查找,键为Name,值为精灵指针
|
||||||
nameMap map[string]*Sprite
|
nameMap map[string]*Sprite
|
||||||
@ -50,10 +48,8 @@ type NamedSpriteBoard struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewNamedSpriteBoard 创建一个新的NamedSpriteBoard
|
// NewNamedSpriteBoard 创建一个新的NamedSpriteBoard
|
||||||
func NewNamedSpriteBoard(width, height int) *NamedSpriteBoard {
|
func NewNamedSpriteBoard() *NamedSpriteBoard {
|
||||||
return &NamedSpriteBoard{
|
return &NamedSpriteBoard{
|
||||||
Width: width,
|
|
||||||
Height: height,
|
|
||||||
Sprites: make([]*Sprite, 0),
|
Sprites: make([]*Sprite, 0),
|
||||||
nameMap: make(map[string]*Sprite),
|
nameMap: make(map[string]*Sprite),
|
||||||
indexMap: make(map[int]*IndexGroup),
|
indexMap: make(map[int]*IndexGroup),
|
||||||
@ -462,32 +458,122 @@ func (b *NamedSpriteBoard) updateSpriteArray() {
|
|||||||
|
|
||||||
// RenderToImage 将精灵板渲染为图像
|
// RenderToImage 将精灵板渲染为图像
|
||||||
func (b *NamedSpriteBoard) RenderToImage() *image.RGBA {
|
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
|
currentGroup := b.indexHead
|
||||||
for currentGroup != nil {
|
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
|
currentNode := currentGroup.Head
|
||||||
for currentNode != nil {
|
for currentNode != nil {
|
||||||
sprite := currentNode.Sprite
|
sprite := currentNode.Sprite
|
||||||
if sprite.Image != nil {
|
if sprite.Image != nil {
|
||||||
// 获取精灵的边界
|
// 获取精灵的边界
|
||||||
bounds := sprite.Image.Bounds()
|
srcBounds := sprite.Image.Bounds()
|
||||||
|
|
||||||
// 计算目标区域
|
// 计算原始目标区域(相对于minX, minY偏移)
|
||||||
min := image.Point{
|
dstMinX := sprite.Position.X - minX
|
||||||
X: sprite.Position.X,
|
dstMinY := sprite.Position.Y - minY
|
||||||
Y: sprite.Position.Y,
|
dstMaxX := dstMinX + srcBounds.Dx()
|
||||||
}
|
dstMaxY := dstMinY + srcBounds.Dy()
|
||||||
max := image.Point{
|
|
||||||
X: sprite.Position.X + bounds.Dx(),
|
// 计算源图像的裁剪区域(如果精灵部分在画布外)
|
||||||
Y: sprite.Position.Y + bounds.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) {
|
func TestNamedSpriteBoard(t *testing.T) {
|
||||||
// 创建测试板
|
// 创建测试板
|
||||||
board := NewNamedSpriteBoard(100, 100)
|
board := NewNamedSpriteBoard()
|
||||||
|
|
||||||
// 测试添加精灵
|
// 测试添加精灵
|
||||||
sprite1 := &Sprite{
|
sprite1 := &Sprite{
|
||||||
@ -186,7 +186,7 @@ func TestNamedSpriteBoard(t *testing.T) {
|
|||||||
|
|
||||||
// 性能测试
|
// 性能测试
|
||||||
func BenchmarkNamedSpriteBoardAdd(b *testing.B) {
|
func BenchmarkNamedSpriteBoardAdd(b *testing.B) {
|
||||||
board := NewNamedSpriteBoard(1000, 1000)
|
board := NewNamedSpriteBoard()
|
||||||
img := image.NewRGBA(image.Rect(0, 0, 10, 10))
|
img := image.NewRGBA(image.Rect(0, 0, 10, 10))
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
@ -203,7 +203,7 @@ func BenchmarkNamedSpriteBoardAdd(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkNamedSpriteBoardGetByName(b *testing.B) {
|
func BenchmarkNamedSpriteBoardGetByName(b *testing.B) {
|
||||||
// 准备测试数据
|
// 准备测试数据
|
||||||
board := NewNamedSpriteBoard(1000, 1000)
|
board := NewNamedSpriteBoard()
|
||||||
img := image.NewRGBA(image.Rect(0, 0, 10, 10))
|
img := image.NewRGBA(image.Rect(0, 0, 10, 10))
|
||||||
names := make([]string, 1000)
|
names := make([]string, 1000)
|
||||||
|
|
||||||
@ -227,7 +227,7 @@ func BenchmarkNamedSpriteBoardGetByName(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkNamedSpriteBoardGetByIndex(b *testing.B) {
|
func BenchmarkNamedSpriteBoardGetByIndex(b *testing.B) {
|
||||||
// 准备测试数据
|
// 准备测试数据
|
||||||
board := NewNamedSpriteBoard(1000, 1000)
|
board := NewNamedSpriteBoard()
|
||||||
img := image.NewRGBA(image.Rect(0, 0, 10, 10))
|
img := image.NewRGBA(image.Rect(0, 0, 10, 10))
|
||||||
|
|
||||||
for i := 0; i < 1000; i++ {
|
for i := 0; i < 1000; i++ {
|
||||||
@ -248,7 +248,7 @@ func BenchmarkNamedSpriteBoardGetByIndex(b *testing.B) {
|
|||||||
|
|
||||||
func TestNamedSpriteBoardRenderToImage(t *testing.T) {
|
func TestNamedSpriteBoardRenderToImage(t *testing.T) {
|
||||||
// 创建测试板
|
// 创建测试板
|
||||||
board := NewNamedSpriteBoard(200, 200)
|
board := NewNamedSpriteBoard()
|
||||||
|
|
||||||
// 创建几个具有不同索引的彩色精灵
|
// 创建几个具有不同索引的彩色精灵
|
||||||
// 索引值小的精灵会被先绘制,索引值大的精灵会覆盖在上面
|
// 索引值小的精灵会被先绘制,索引值大的精灵会覆盖在上面
|
||||||
|
Loading…
x
Reference in New Issue
Block a user