2021SC@SDUSC

目录

  • 一.概述
  • 二.代码分析
    • 1.Update()
    • 2.Draw()
    • 3.Layout()

一.概述

本文将介绍ebiten在RunGame函数中逐帧执行的Update()方法,Draw()方法和Layout()方法。

首先分析确定这三个方法所具有的功能是什么:

Update()——更新游戏逻辑,几乎每一帧都要调用,Ebiten默认每秒调用Update方法60次,且第一次调用Draw方法之前确保必须先调用一次Update方法初始化。

Draw()——绘制游戏画面,每帧都会调用游戏的Draw函数来绘制屏幕,参数表示一个屏幕图像。采用更新后的内容作为游戏屏幕。

Layout()——接受以设备无关像素为单位的原生外部大小,并返回游戏的逻辑屏幕大小,在台式机上,外部是窗口或显示器(全屏模式)。在浏览器上,外部是一个实体元素;在手机上,外部是视野的大小。即使外部大小和屏幕大小不同,渲染比例也会自动调整为与外部相配。确保在第一次调用Update之前先调用Layout。

下面将逐一分析其实现原理。

二.代码分析

上述三个方法接口定义如下:

type Game interface {Update() errorDraw(screen *Image)Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int)
}

1.Update()

更新游戏逻辑,该方法通常是在游戏的开发过程中自行实现上述game类的接口,每个游戏Update()中的内容都不相同,以游戏2048为例,Update()方法如下:

//Update更新当前游戏状态
func (g *Game) Update() error {g.input.Update()if err := g.board.Update(g.input); err != nil {return err}return nil
}

其中此时的Game类和g.input.Update(),g.board.Update()都是开发者自己定义的方法,用于更新该游戏的逻辑,此处就不过多赘述了。

2.Draw()

还是以2048游戏为例,此时定义的Draw()如下:

func (g *Game) Draw(screen *ebiten.Image) {if g.boardImage == nil {w, h := g.board.Size()g.boardImage = ebiten.NewImage(w, h)}screen.Fill(backgroundColor)g.board.Draw(g.boardImage)op := &ebiten.DrawImageOptions{}sw, sh := screen.Size()bw, bh := g.boardImage.Size()x := (sw - bw) / 2y := (sh - bh) / 2op.GeoM.Translate(float64(x), float64(y))screen.DrawImage(g.boardImage, op)
}

功能是将当前游戏以 图像 的形式绘制到指定屏幕上。
其中多次调用了ebiten的方法,下面依次分析。

实现判断当前屏幕释放存在可以绘制的背景图像,如果没有,那么调用ebiten的新建图像方法:

//newimage返回空图像。
//。
//如果宽度或高度小于1或大于设备相关的最大尺寸,newimage会死机。
//。
//如果RunGame已经结束,newimage会死机。
func NewImage(width, height int) *Image {if isRunGameEnded() {panic(fmt.Sprintf("ebiten: NewImage cannot be called after RunGame finishes"))}if width <= 0 {panic(fmt.Sprintf("ebiten: width at NewImage must be positive but %d", width))}if height <= 0 {panic(fmt.Sprintf("ebiten: height at NewImage must be positive but %d", height))}i := &Image{mipmap: mipmap.New(width, height),bounds: image.Rect(0, 0, width, height),}i.addr = ireturn i
}

返回一个初始化的空Image类型的对象。位于根目录下的image.go文件内,Image类即图像类的定义如下:

//Image表示一组矩形像素。
//像素格式为Alpha预乘RGBA。
//Image实现image.Image和dra.Image。
type Image struct {//addr保持自我检查复制。//类似的示例请参见string.Builder。addr *Imagemipmap *mipmap.Mipmap //Mipmap(有时候拼写成mipmap)是一种电脑图形图像技术,用于在三维图像的二维代替物中达到立体感效应。bounds   image.Rectangle //边界original *Imagescreen   bool
}

接下来调用Fill方法给这个空图像填充背景颜色。位于根目录下的image.go文件内
Fill方法的实现细节如下:

//Fill用纯色填充图像。
//。
//释放图像时,Fill不执行任何操作
func (i *Image) Fill(clr color.Color) {//使用原来的大小覆盖整个地域(#1691)。//DrawImage自动裁剪渲染区域。orig := iif i.isSubImage() {orig = i.original}w, h := orig.Size()op := &DrawImageOptions{}op.GeoM.Scale(float64(w), float64(h))r, g, b, a := clr.RGBA()var rf, gf, bf, af float64if a > 0 {rf = float64(r) / float64(a)gf = float64(g) / float64(a)bf = float64(b) / float64(a)af = float64(a) / 0xffff}op.ColorM.Scale(rf, gf, bf, af)op.CompositeMode = CompositeModeCopyi.DrawImage(emptySubImage, op)
}

先判断i图像是不是子图像,如果是,那么就获取它的源图像,并获取宽高,再设置绘制图像时的选项,即DrawImageOptions。下一步中的GeoM是图像的几何变换选项,该方法的目的是将图像的矩阵按照上面给出的宽高进行缩放,下面的颜色同理,然后再设置图像绘制的合成模式为复制。最后调用DrawImage方法完成绘制。

其中DrawImageOptions的定义如下:

//DrawImageOptions表示DrawImage的选项。
type DrawImageOptions struct {//Geom是要绘制的几何矩阵。//默认(零)值为Identity,即在(0,0)处绘制图像GeoM GeoM//ColorM是要绘制的颜色矩阵。//默认(零)值为Identity,不会改变任何颜色。ColorM ColorM//CompositeMode是绘制的合成模式。//默认值(零)为规则的Alpha混合CompositeMode CompositeMode//滤镜是纹理滤镜的一种。//默认(零)值为FilterNeadest。Filter Filter
}

两个缩放方法的实现即进行相关的矩阵计算,实现如下,分别位于根目录下的geom.go文件内和colorm.go文件内。

//Scale按(x,y)缩放矩阵。
func (g *GeoM) Scale(x, y float64) {a := (g.a_1 + 1) * xb := g.b * xtx := g.tx * xc := g.c * yd := (g.d_1 + 1) * yty := g.ty * yg.a_1 = a - 1g.b = bg.c = cg.d_1 = d - 1g.tx = txg.ty = ty
}
//Scale按(r,g,b,a)缩放矩阵
func (c *ColorM) Scale(r, g, b, a float64) {c.impl = c.affineColorM().Scale(float32(r), float32(g), float32(b), float32(a))
}

最后是DrawImage方法,传入两个参数分别是图像和图像绘制选项,实现细节如下:

func (i *Image) DrawImage(img *Image, options *DrawImageOptions) {i.copyCheck()if img.isDisposed() {panic("ebiten: the given image to DrawImage must not be disposed")}if i.isDisposed() {return}dstBounds := i.Bounds()dstRegion := driver.Region{X:      float32(dstBounds.Min.X),Y:      float32(dstBounds.Min.Y),Width:  float32(dstBounds.Dx()),Height: float32(dstBounds.Dy()),}//锁定前计算折点,因为用户可以在。//options.ImageParts接口没有死锁(例如调用Image函数)。if options == nil {options = &DrawImageOptions{}}bounds := img.Bounds()mode := driver.CompositeMode(options.CompositeMode)filter := driver.Filter(options.Filter)a, b, c, d, tx, ty := options.GeoM.elements32()sx0 := float32(bounds.Min.X)sy0 := float32(bounds.Min.Y)sx1 := float32(bounds.Max.X)sy1 := float32(bounds.Max.Y)vs := graphics.QuadVertices(sx0, sy0, sx1, sy1, a, b, c, d, tx, ty, 1, 1, 1, 1)is := graphics.QuadIndices()srcs := [graphics.ShaderImageNum]*mipmap.Mipmap{img.mipmap}i.mipmap.DrawTriangles(srcs, vs, is, options.ColorM.affineColorM(), mode, filter, driver.AddressUnsafe, dstRegion, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false, canSkipMipmap(options.GeoM, filter))
}

首先是i.copyCheck()方法,检查图像i和i.addr是否相同即进行保持自我复制检查。

再检查i和传入的img参数的mipmap是否为空,均不为空后进入下一步。

i.Bounds()返回目的图像的边界即bounds属性。

driver.Region提取出刚刚获得的边界中的边界起点X,Y值以及边界宽高。

然后判断传入的option是否为空,如果为空则重新初始化。

接下来获取传入图像的边界img.Bounds(),图像绘制选项的合成模式,纹理过滤以及几何变换参数a, b, c, d, tx, ty。

获取边界的宽高的最大值和最小值,并根据上述变量得到一个不会和其他分片重叠的四边形分片。

is是一个四元指数,src是着色器图像数量*一个初始化的Mipmap对象。

最后根据mipmap的DrawTriangles方法完成图像的绘制。

该方法就是Draw()方法的 核心部分 ,上述最后三步的实现细节在此暂时不讨论。

3.Layout()

const (ScreenWidth  = 420ScreenHeight = 600boardSize    = 4
)
//Layout实现ebiten.Game的布局
func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {return ScreenWidth, ScreenHeight
}

在本例子中,Layout()就是返回事先人为设置好的屏幕宽高,并根据给定的外部尺寸进行调整。

2021SC@SDUSC山东大学软件学院软件工程应用与实践--Ebiten代码分析 源码分析(三)相关推荐

  1. 2021SC@SDUSC山东大学软件学院软件工程应用与实践--Ebiten代码分析 源码分析(四)

    2021SC@SDUSC 目录 一.概述 二.代码分析 1.graphics.QuadVertices() 2.mipmap.Mipmap() 3.i.mipmap.DrawTriangles() 一 ...

  2. 2021SC@SDUSC山东大学软件学院软件工程应用与实践--YOLOV5代码分析(十三)metrics.py-1

    2021SC@SDUSC 前言 这篇分析metrics.py文件,这个文件是用来计算评估指标,包括mAP.混淆矩阵.IOU相关的函数. fitness函数 def fitness(x):# Model ...

  3. 2021SC@SDUSC山东大学软件学院软件工程应用与实践--YOLOV5代码分析(四)general.py-2

    2021SC@SDUSC 目录 前言 is_writeable函数 is_docker函数 is_colab函数 is_pip函数 is_ascii函数 file_size函数 check_onlin ...

  4. 2021SC@SDUSC山东大学软件学院软件工程应用与实践--YOLOV5代码分析(八)plots.py-1

    2021SC@SDUSC 前言 这篇分析plot.py文件,就如其名称一样,主要是一些用以展示的代码,也不是核心代码 外部库 from copy import copy from pathlib im ...

  5. 2021SC@SDUSC山东大学软件学院软件工程应用与实践--quark renderer代码分析 第十二篇 绘画系统分析(3):连线(line)

    2021SC@SDUSC 这是绘画系统的第三个大部分,连线,连线的内容包括最上层的链接线抽象类,和细分的直线,折线,贝塞尔曲线以及内外旋轮曲线五个部分. 首先是连接线抽象类. 连接线抽象类,需要成为连 ...

  6. 软件工程应用于实践:AJ-Report项目 源码分析(8)

    2021SC@SDUSC 本次分析的是report-ui\src\views\report\excelreport\designer处的代码 data () {return {activeName: ...

  7. 软件工程应用于实践:AJ-Report项目 源码分析(7)

    2021SC@SDUSC 本次分析的是report-ui\src\views\report\bigscreen\designer\map中的代码 series: [{type: 'map',map: ...

  8. 山东大学软件工程应用与实践——Pig代码综述

    2021SC@SDUSC 目录 一.Pig是什么? 二.Pig的特点 三.Pig安装部署 四.组内分工 一.Pig是什么? Pig 是Apache平台下的一个免费开源项目,是MapReduce的一个抽 ...

  9. <2021SC@SDUSC>博客(5)山东大学软件工程应用与实践JPress代码分析(四)

    2021SC@SDUSC 前言 在前面三篇文章中,我对 JPress 项目的基础框架 JFinal 和 JBoot 在使用层面进行了拆解与分析.在接下来的文章中,我将在代码层面对 JPress 项目中 ...

最新文章

  1. ASP.NET中树形图的实现
  2. yocto中文环境搭建
  3. 反射中getDeclaredConstructors和getConstructors两个方法的区别,然后setAccessible什么时候用,作用是什么?
  4. Python-快速排序算法
  5. 教你实现splash欢迎页面延迟跳转的6种方法
  6. ImageMagick 拆分透明PNG 合并JPG和Alpha Mask
  7. 解题:HNOI 2008 玩具装箱
  8. php上传图片大小判断,jQuery实现判断上传图片类型和大小的方法示例
  9. Windows下VSCode运行shell
  10. Latex写论文格式注意点
  11. 2022电大国家开放大学网上形考任务-大学语文非免费(非答案)
  12. 从头开始学习->JVM(三):类加载器(上)
  13. 纯css写滚动的弹幕特效
  14. CentOS 安装Nvidia驱动+CUDA+cuDNN+Anaconda3
  15. python和access哪个好过计算机二级_大一考计算机二级,那考office、C语言、VB、Java、Access还是Python好呢?...
  16. 关于halcon深度图转灰度图
  17. Matlab 将计算结果制作成 gif 动画
  18. 固态移动硬盘“函数不正确”
  19. 甲骨文业绩超预期股价大涨近11% 市值首超2000亿美元
  20. Excel如何对比两表格差异

热门文章

  1. 搞了三天终于成功跑起来GitHub上的vue-element-admin最新解决办法!(mac系统亲测有效)
  2. 报错解决方案参考:《xv6分析与实验》中关于qemu运行报错解决方法
  3. html5在线学习系统,Canvas LMS 在线学习管理系统
  4. 怎么批量修改照片的分辨率?照片dpi怎么调?
  5. sa提开放系统下的虚拟新贵Virtualbox权技巧之xp_regwrite替换sethc.exe
  6. python环境准备(二)
  7. CRC16查表法原理
  8. 台式计算机无线网经常掉线,电脑无线网老是掉线怎么办?教你解决掉线问题
  9. html div 自动滚动到底部,javascript让DIV的滚动自动滚动到最底部-4种方法
  10. 鲁大师4月安卓新机性能/流畅榜:ROG游戏手机7摘得性能桂冠 vivo登顶流畅榜