记一次用golang开发游戏客户端的内存占用优化

纯golang语言开发客户端

前提:
游戏库 ebiten
引擎介绍地址 ebiten.org/

Ebiten 是Go 编程语言的开源游戏库。Ebiten 的简单 API 允许您快速轻松地开发可以跨多个平台部署的 2D 游戏。有别于unity3d,UE,cocos creator,godot等游戏引擎,没有图形界面。

没优化之前代码:

1.玩家的结构体如下:

里面定义了6个map数据结构,在游戏初始化阶段,根据事先分类好的图片名字(一共368张散装图),分类存入map。

type Player struct { X float64 Y float64 State int Direction int MouseX int MouseY int ImgNow map[int]*ebiten.Image ImgWeaNow map[int]*ebiten.Image ImgSkillNow map[int]*ebiten.Image asset map[string]*ebiten.Image //man assetWea map[string]*ebiten.Image //weapon skill_asset map[string]*ebiten.Image //skill image *embed.FS } func NewPlayer(x, y float64, state, dir, mx, my int, images *embed.FS) *Player { play := &Player{ X: x, Y: y, State: state, Direction: dir, MouseX: mx, MouseY: my, ImgNow: make(map[int]*ebiten.Image, 8), ImgWeaNow: make(map[int]*ebiten.Image, 8), ImgSkillNow: make(map[int]*ebiten.Image, 6), asset: make(map[string]*ebiten.Image, 160), assetWea: make(map[string]*ebiten.Image, 160), skill_asset: make(map[string]*ebiten.Image, 48), image: images, } return play }

2.加载游戏素材

利用go协程并发加载游戏素材和手动GC

func (p *Player) LoadImages() { wg := sync.WaitGroup{} wg.Add(3) //load skill images go func() { for j := 0; j < 8; j++ { for i := 0; i < 6; i++ { s, _ := p.image.ReadFile("resource/man/skill/" + strconv.Itoa(j) + "_skill_" + strconv.Itoa(i) + ".png") mg := tools.GetEbitenImage(s) name := strconv.Itoa(j) + "_" + strconv.Itoa(i) p.skill_asset[name] = mg } } go func() { runtime.GC() }() wg.Done() }() ........... }

此种模式下,一次性使用多个map存储大量的数据,导致游戏启动阶段。内存占用相当之高,高达890MB左右,随着游戏运行,golang自动GC,才缓慢降到300MB左右。

优化方案

1.使用图集打包工具,将散图合批成一张大图。

 为什么使用图集: 减少游戏包体和内存占用 减少drawcall,提高cpu和GPU效率。

打包后如下图:
| 一个json文件
| 一张图集
记一次用golang开发游戏客户端的内存占用优化

2.根据小图在大图中的位置,读取目标图块。
使用texturepacker工具库,去读取上面的JSON文件。
只支持”JSON (Hash)” 格式

https://github.com/fzipp/texturepacker

3.代码优化

少使用Map存储大量数据
type Player struct { X float64 Y float64 State int Direction int MouseX int MouseY int image *embed.FS } func NewPlayer(x, y float64, state, dir, mx, my int, images *embed.FS) *Player { play := &Player{ X: x, Y: y, State: state, Direction: dir, MouseX: mx, MouseY: my, image: images, } return play }

素材不事先存入大量map,当渲染到对应图像的时候,直接从图集中截取。这样大大提高程序效率和减少内存占用。

 imagess, x, y := g.player.GetAnimator("man", name) //player option op = &ebiten.DrawImageOptions{} op.GeoM.Translate(float64(SCREENWIDTH/2+OFFSETX+x), float64(SCREENHEIGHT/2+OFFSETY+y)) op.GeoM.Scale(0.7, 0.7) op.Filter = ebiten.FilterLinear screen.DrawImage(imagess, op)
游戏素材加载资源后在代码尾部加入适当的手动GC
go func() { runtime.GC() }()

通过上面的代码改造,现在启动阶段能稳定在240mb左右。

源码地址

github地址 Demo

本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 1

兄弟哪里学的,快教教

2年前 评论