Java游戏开发中怎样才能获得更快的FPS?

系统 1570 0

<!--[if gte mso 9]><xml> <w:WordDocument> <w:View>Normal</w:View> <w:Zoom>0</w:Zoom> <w:PunctuationKerning /> <w:DrawingGridVerticalSpacing>7.8 磅</w:DrawingGridVerticalSpacing> <w:DisplayHorizontalDrawingGridEvery>0</w:DisplayHorizontalDrawingGridEvery> <w:DisplayVerticalDrawingGridEvery>2</w:DisplayVerticalDrawingGridEvery> <w:ValidateAgainstSchemas /> <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> <w:IgnoreMixedContent>false</w:IgnoreMixedContent> <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> <w:Compatibility> <w:SpaceForUL /> <w:BalanceSingleByteDoubleByteWidth /> <w:DoNotLeaveBackslashAlone /> <w:ULTrailSpace /> <w:DoNotExpandShiftReturn /> <w:AdjustLineHeightInTable /> <w:BreakWrappedTables /> <w:SnapToGridInCell /> <w:WrapTextWithPunct /> <w:UseAsianBreakRules /> <w:DontGrowAutofit /> <w:UseFELayout /> </w:Compatibility> <w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel> </w:WordDocument> </xml><![endif]--><!--[if gte mso 9]><xml> <w:LatentStyles DefLockedState="false" LatentStyleCount="156"> </w:LatentStyles> </xml><![endif]--> <!--[if gte mso 10]> <mce:style><! /* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.0pt; font-family:"Times New Roman"; mso-fareast-font-family:"Times New Roman"; mso-ansi-language:#0400; mso-fareast-language:#0400; mso-bidi-language:#0400;} --> <!--[endif]-->

众所周知, Java 应用的运行速度虽然不慢,却也谈不上快,以最新的 JRE1.6 表现来说,至多也就是优胜于一些纯粹的解释型语言,距离 C/C++ 等编译型的执行效率还有一定差距。

平心而论,如果我们使用 Java 去制作一些简单的桌面应用,那么目前 Java 组件的绘图速度勉强还能接受;但如果用 Java 来进行游戏开发,尤其是制作一些需要高 FPS 才能满足的动态效果,就必须利用技术手段对其加以优化,解决其绘图效率问题,其它才有的谈。

什么是 FPS

这里所说的 FPS ,并非指射击游戏,而是指俗称的“帧率”( Frames per Second ,缩写: FPS )或“赫兹”( Hz )。笔者在以前的博文中曾给出过专门的解释及 Java 实现示例,此处不再赘述。

受到人类眼球的生理结构制约,通常情况下只要我们所见画面高于每秒 16 帧,就会认为画面是连贯的,生物学上称此现象为视觉暂留,这也就是为什么电影胶片是一格一格拍摄出来,然而我们却认为它是连续的一段。当然,所谓的 16 帧也只是一个中值,并非放之四海而皆准,根据具体视觉对象不同,帧率的具体标准会有所变化。在最坏情况下,动画不低于每秒 12 帧,现代电影不低于 24 帧,动作游戏不低于 30 帧,在人眼中的画面才是连续不间断的。

总之, FPS 数值越高则画面流畅度便越高,越低则越显停滞,要提高 Java 游戏运行效率,那么焦点就必然要集中在如何提高程序的 FPS 之上。

我们该从什么地方入手,才能提高 FPS

Java 绘图最终要通过 native ,这是地球人都知道的事情。这意味着除非我们学习 SWT 重写本地图形接口,否则显示部分我们无法干涉;这样便决定了我们对 FPS 的优化必然要集中在本地图形系统调用之前,而非之后。也就是说,提高 Java 应用的 FPS ,还要老生常谈的从 Image Graphics 及其相关子类入手。

那么,对这些尽人皆知的 Java 绘图组件,我们究竟能改进那些部分呢?

我在不久前发布的博文《 Java 游戏开发中应始终坚持的 10 项基本原则》中,曾经就如何构建一个高效的 Java 游戏发表过一些见解,其中第 7 点“始终双缓冲游戏图像”,它不但是解决 Java 图像闪烁问题的关键所在,而且同样是提高 Java 游戏帧率的制胜法宝之一。是的, Java 绘图机能的优化与改进,关键就在于如何对其缓冲区域进行优化处理。

下面,我将展示三种不同的 Java 缓冲绘图方式。

1 、采用 BufferedImage 对象进行缓冲

这种方法是最简单,同时也是最常用的双缓冲构建方式,也就是构建一个 BufferedImage 缓冲当前绘图,所有 Graphics 操作在其上进行,仅在需要时才将贴图 paint 于窗体之上,使用上再简单不过,但效率如何呢?文章进行到此处时尚不得而知。

2 、采用 BufferStrategy 构建缓冲区

使用 BufferStrategy 构建缓冲能够获得系统所提供的硬件加速, Java 系统会根据本地环境选择最适合的 BufferStrategy 。要创建 BufferStrategy ,需要使用 createBufferStrategy() 方法告诉系统你所期望的缓冲区数目(通常使用双缓冲,也就是填入“ 2 ”),并使用 getDrawGraphics() 方法在缓冲区之间进行交换,该方法返回下一个要使用的缓冲区。 BufferStrategy 最大的缺点与优点都在于其受本地图形环境影响,既不会出现很快的图形环境跑出很慢的 FPS ,也别指望很慢的图形环境跑出很快的 FPS

3 、完全在 BufferedImage DataBuffer 中进行图像处理

每个 BufferedImage 都有一个与之对应得 WritableRaster 对象( getRaster 方法获得),通过它我们获得指定 BufferedImage DataBuffer getDataBuffer 方法获得),与方法 1 类似,我们同样构建一个 BufferedImage 缓冲当前所有绘图,所有操作都在其上进行,仅在需要时才将贴图 paint 于窗体之上。但区别在于,由于 DataBuffer 可以转化为描述 BufferedImage 象素点的 int[] byte[] short[] 等数组集合,因此我们不再使用 Java 提供的 Graphics 对象,而是直接操作像素点进行所有绘图的实现。 但是,这样进行数组操作会快吗?

现在我们为其各自构建三个示例,尽量以比较趋同的处理流程构建,分别测算三种方法的具体效率。

PS :写完才突然想起,这台 2002 年买的电脑实在不适合测试 FPS -_-||| ),较新机型的 FPS 至少能达到此测试结果的 2 倍以上,特此声明……)

PS :以下图像素材源自成都汉森信息技术有限公司首页,特此声明)

以下开始将分别对三种方法分别进行绘制 1 名角色、绘制 100 名角色、绘制 1000 名角色的简单 FPS 绘图效率测算。

一、采用 BufferedImage 对象进行缓冲

<!--[if gte mso 9]><xml> <w:WordDocument> <w:View>Normal</w:View> <w:Zoom>0</w:Zoom> <w:PunctuationKerning /> <w:DrawingGridVerticalSpacing>7.8 磅</w:DrawingGridVerticalSpacing> <w:DisplayHorizontalDrawingGridEvery>0</w:DisplayHorizontalDrawingGridEvery> <w:DisplayVerticalDrawingGridEvery>2</w:DisplayVerticalDrawingGridEvery> <w:ValidateAgainstSchemas /> <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> <w:IgnoreMixedContent>false</w:IgnoreMixedContent> <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> <w:Compatibility> <w:SpaceForUL /> <w:BalanceSingleByteDoubleByteWidth /> <w:DoNotLeaveBackslashAlone /> <w:ULTrailSpace /> <w:DoNotExpandShiftReturn /> <w:AdjustLineHeightInTable /> <w:BreakWrappedTables /> <w:SnapToGridInCell /> <w:WrapTextWithPunct /> <w:UseAsianBreakRules /> <w:DontGrowAutofit /> <w:UseFELayout /> </w:Compatibility> <w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel> </w:WordDocument> </xml><![endif]--><!--[if gte mso 9]><xml> <w:LatentStyles DefLockedState="false" LatentStyleCount="156"> </w:LatentStyles> </xml><![endif]--> <!--[if gte mso 10]> <mce:style><! /* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.0pt; font-family:"Times New Roman"; mso-fareast-font-family:"Times New Roman"; mso-ansi-language:#0400; mso-fareast-language:#0400; mso-bidi-language:#0400;} --> <!--[endif]-->

源码如下:

1 DoubleBufferGameFrame.Java

<!--[if gte mso 9]><xml> <w:WordDocument> <w:View>Normal</w:View> <w:Zoom>0</w:Zoom> <w:PunctuationKerning /> <w:DrawingGridVerticalSpacing>7.8 磅</w:DrawingGridVerticalSpacing> <w:DisplayHorizontalDrawingGridEvery>0</w:DisplayHorizontalDrawingGridEvery> <w:DisplayVerticalDrawingGridEvery>2</w:DisplayVerticalDrawingGridEvery> <w:ValidateAgainstSchemas /> <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> <w:IgnoreMixedContent>false</w:IgnoreMixedContent> <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> <w:Compatibility> <w:SpaceForUL /> <w:BalanceSingleByteDoubleByteWidth /> <w:DoNotLeaveBackslashAlone /> <w:ULTrailSpace /> <w:DoNotExpandShiftReturn /> <w:AdjustLineHeightInTable /> <w:BreakWrappedTables /> <w:SnapToGridInCell /> <w:WrapTextWithPunct /> <w:UseAsianBreakRules /> <w:DontGrowAutofit /> <w:UseFELayout /> </w:Compatibility> <w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel> </w:WordDocument> </xml><![endif]--><!--[if gte mso 9]><xml> <w:LatentStyles DefLockedState="false" LatentStyleCount="156"> </w:LatentStyles> </xml><![endif]--> <!--[if gte mso 10]> <mce:style><! /* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.0pt; font-family:"Times New Roman"; mso-fareast-font-family:"Times New Roman"; mso-ansi-language:#0400; mso-fareast-language:#0400; mso-bidi-language:#0400;} --> <!--[endif]-->

2 DoubleBufferCanvas.Java

<!--[if !mso]> <mce:style><! v/:* {behavior:url(#default#VML);} o/:* {behavior:url(#default#VML);} w/:* {behavior:url(#default#VML);} .shape {behavior:url(#default#VML);} --> <!--[endif]--><!--[if gte mso 9]><xml> <w:WordDocument> <w:View>Normal</w:View> <w:Zoom>0</w:Zoom> <w:PunctuationKerning /> <w:DrawingGridVerticalSpacing>7.8 磅</w:DrawingGridVerticalSpacing> <w:DisplayHorizontalDrawingGridEvery>0</w:DisplayHorizontalDrawingGridEvery> <w:DisplayVerticalDrawingGridEvery>2</w:DisplayVerticalDrawingGridEvery> <w:ValidateAgainstSchemas /> <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> <w:IgnoreMixedContent>false</w:IgnoreMixedContent> <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> <w:Compatibility> <w:SpaceForUL /> <w:BalanceSingleByteDoubleByteWidth /> <w:DoNotLeaveBackslashAlone /> <w:ULTrailSpace /> <w:DoNotExpandShiftReturn /> <w:AdjustLineHeightInTable /> <w:BreakWrappedTables /> <w:SnapToGridInCell /> <w:WrapTextWithPunct /> <w:UseAsianBreakRules /> <w:DontGrowAutofit /> <w:UseFELayout /> </w:Compatibility> <w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel> </w:WordDocument> </xml><![endif]--><!--[if gte mso 9]><xml> <w:LatentStyles DefLockedState="false" LatentStyleCount="156"> </w:LatentStyles> </xml><![endif]--> <!--[if gte mso 10]> <mce:style><! /* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.0pt; font-family:"Times New Roman"; mso-fareast-font-family:"Times New Roman"; mso-ansi-language:#0400; mso-fareast-language:#0400; mso-bidi-language:#0400;} --> <!--[endif]-->

场景图 1 、角色图 1 FPS 60 - 70

<!--[if gte vml 1]><v:shapetype id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f"> <v:stroke joinstyle="miter" /> <v:formulas> <v:f eqn="if lineDrawn pixelLineWidth 0" /> <v:f eqn="sum @0 1 0" /> <v:f eqn="sum 0 0 @1" /> <v:f eqn="prod @2 1 2" /> <v:f eqn="prod @3 21600 pixelWidth" /> <v:f eqn="prod @3 21600 pixelHeight" /> <v:f eqn="sum @0 0 1" /> <v:f eqn="prod @6 1 2" /> <v:f eqn="prod @7 21600 pixelWidth" /> <v:f eqn="sum @8 21600 0" /> <v:f eqn="prod @7 21600 pixelHeight" /> <v:f eqn="sum @10 21600 0" /> </v:formulas> <v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect" /> <o:lock v:ext="edit" aspectratio="t" /> </v:shapetype><v:shape id="_x0000_i1025" type="#_x0000_t75" style='width:414.75pt; height:328.5pt'> <v:imagedata src="file:///C:/DOCUME~1/chenpeng/LOCALS~1/Temp/msohtml1/01/clip_image001.png" mce_src="file:///C:/DOCUME~1/chenpeng/LOCALS~1/Temp/msohtml1/01/clip_image001.png" o:title="awt_pt_0_t1" /> </v:shape><![endif]--><!--[if !vml]--><!--[endif]-->

pt0


场景图 1 、角色图 100 FPS 20 - 25

pt1


<!--[if gte vml 1]><v:shape id="_x0000_i1026" type="#_x0000_t75" style='width:414.75pt;height:328.5pt'> <v:imagedata src="file:///C:/DOCUME~1/chenpeng/LOCALS~1/Temp/msohtml1/01/clip_image003.png" mce_src="file:///C:/DOCUME~1/chenpeng/LOCALS~1/Temp/msohtml1/01/clip_image003.png" o:title="awt_pt_1_t1" /> </v:shape><![endif]--><!--[if !vml]--><!--[endif]-->

场景图 1 、角色图 1000 FPS 13 - 17

pt2

<!--[if gte mso 9]><xml> <w:WordDocument> <w:View>Normal</w:View> <w:Zoom>0</w:Zoom> <w:PunctuationKerning /> <w:DrawingGridVerticalSpacing>7.8 磅</w:DrawingGridVerticalSpacing> <w:DisplayHorizontalDrawingGridEvery>0</w:DisplayHorizontalDrawingGridEvery> <w:DisplayVerticalDrawingGridEvery>2</w:DisplayVerticalDrawingGridEvery> <w:ValidateAgainstSchemas /> <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> <w:IgnoreMixedContent>false</w:IgnoreMixedContent> <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> <w:Compatibility> <w:SpaceForUL /> <w:BalanceSingleByteDoubleByteWidth /> <w:DoNotLeaveBackslashAlone /> <w:ULTrailSpace /> <w:DoNotExpandShiftReturn /> <w:AdjustLineHeightInTable /> <w:BreakWrappedTables /> <w:SnapToGridInCell /> <w:WrapTextWithPunct /> <w:UseAsianBreakRules /> <w:DontGrowAutofit /> <w:UseFELayout /> </w:Compatibility> <w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel> </w:WordDocument> </xml><![endif]--><!--[if gte mso 9]><xml> <w:LatentStyles DefLockedState="false" LatentStyleCount="156"> </w:LatentStyles> </xml><![endif]--> <!--[if gte mso 10]> <mce:style><! /* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.0pt; font-family:"Times New Roman"; mso-fareast-font-family:"Times New Roman"; mso-ansi-language:#0400; mso-fareast-language:#0400; mso-bidi-language:#0400;} --> <!--[endif]-->

二、采用 BufferStrategy 构建缓冲区(双缓冲)

<!--[if gte mso 9]><xml> <w:WordDocument> <w:View>Normal</w:View> <w:Zoom>0</w:Zoom> <w:PunctuationKerning /> <w:DrawingGridVerticalSpacing>7.8 磅</w:DrawingGridVerticalSpacing> <w:DisplayHorizontalDrawingGridEvery>0</w:DisplayHorizontalDrawingGridEvery> <w:DisplayVerticalDrawingGridEvery>2</w:DisplayVerticalDrawingGridEvery> <w:ValidateAgainstSchemas /> <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> <w:IgnoreMixedContent>false</w:IgnoreMixedContent> <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> <w:Compatibility> <w:SpaceForUL /> <w:BalanceSingleByteDoubleByteWidth /> <w:DoNotLeaveBackslashAlone /> <w:ULTrailSpace /> <w:DoNotExpandShiftReturn /> <w:AdjustLineHeightInTable /> <w:BreakWrappedTables /> <w:SnapToGridInCell /> <w:WrapTextWithPunct /> <w:UseAsianBreakRules /> <w:DontGrowAutofit /> <w:UseFELayout /> </w:Compatibility> <w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel> </w:WordDocument> </xml><![endif]--><!--[if gte mso 9]><xml> <w:LatentStyles DefLockedState="false" LatentStyleCount="156"> </w:LatentStyles> </xml><![endif]--> <!--[if gte mso 10]> <mce:style><! /* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.0pt; font-family:"Times New Roman"; mso-fareast-font-family:"Times New Roman"; mso-ansi-language:#0400; mso-fareast-language:#0400; mso-bidi-language:#0400;} --> <!--[endif]--> 1、BufferStrategyGameFrame.java

2 BufferStrategyCanvas.java <!--[if gte mso 9]><xml> <w:WordDocument> <w:View>Normal</w:View> <w:Zoom>0</w:Zoom> <w:PunctuationKerning /> <w:DrawingGridVerticalSpacing>7.8 磅</w:DrawingGridVerticalSpacing> <w:DisplayHorizontalDrawingGridEvery>0</w:DisplayHorizontalDrawingGridEvery> <w:DisplayVerticalDrawingGridEvery>2</w:DisplayVerticalDrawingGridEvery> <w:ValidateAgainstSchemas /> <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> <w:IgnoreMixedContent>false</w:IgnoreMixedContent> <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> <w:Compatibility> <w:SpaceForUL /> <w:BalanceSingleByteDoubleByteWidth /> <w:DoNotLeaveBackslashAlone /> <w:ULTrailSpace /> <w:DoNotExpandShiftReturn /> <w:AdjustLineHeightInTable /> <w:BreakWrappedTables /> <w:SnapToGridInCell /> <w:WrapTextWithPunct /> <w:UseAsianBreakRules /> <w:DontGrowAutofit /> <w:UseFELayout /> </w:Compatibility> <w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel> </w:WordDocument> </xml><![endif]--><!--[if gte mso 9]><xml> <w:LatentStyles DefLockedState="false" LatentStyleCount="156"> </w:LatentStyles> </xml><![endif]--> <!--[if gte mso 10]> <mce:style><! /* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.0pt; font-family:"Times New Roman"; mso-ansi-language:#0400; mso-fareast-language:#0400; mso-bidi-language:#0400;} --> <!--[endif]-->

<!--[if !mso]> <mce:style><! v/:* {behavior:url(#default#VML);} o/:* {behavior:url(#default#VML);} w/:* {behavior:url(#default#VML);} .shape {behavior:url(#default#VML);} --> <!--[endif]--><!--[if gte mso 9]><xml> <w:WordDocument> <w:View>Normal</w:View> <w:Zoom>0</w:Zoom> <w:PunctuationKerning /> <w:DrawingGridVerticalSpacing>7.8 磅</w:DrawingGridVerticalSpacing> <w:DisplayHorizontalDrawingGridEvery>0</w:DisplayHorizontalDrawingGridEvery> <w:DisplayVerticalDrawingGridEvery>2</w:DisplayVerticalDrawingGridEvery> <w:ValidateAgainstSchemas /> <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> <w:IgnoreMixedContent>false</w:IgnoreMixedContent> <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> <w:Compatibility> <w:SpaceForUL /> <w:BalanceSingleByteDoubleByteWidth /> <w:DoNotLeaveBackslashAlone /> <w:ULTrailSpace /> <w:DoNotExpandShiftReturn /> <w:AdjustLineHeightInTable /> <w:BreakWrappedTables /> <w:SnapToGridInCell /> <w:WrapTextWithPunct /> <w:UseAsianBreakRules /> <w:DontGrowAutofit /> <w:UseFELayout /> </w:Compatibility> <w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel> </w:WordDocument> </xml><![endif]--><!--[if gte mso 9]><xml> <w:LatentStyles DefLockedState="false" LatentStyleCount="156"> </w:LatentStyles> </xml><![endif]--> <!--[if gte mso 10]> <mce:style><! /* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.0pt; font-family:"Times New Roman"; mso-ansi-language:#0400; mso-fareast-language:#0400; mso-bidi-language:#0400;} --> <!--[endif]-->

场景图 1 、角色图 1 FPS 80 - 90

bs0

<!--[if gte vml 1]><v:shapetype id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f"> <v:stroke joinstyle="miter" /> <v:formulas> <v:f eqn="if lineDrawn pixelLineWidth 0" /> <v:f eqn="sum @0 1 0" /> <v:f eqn="sum 0 0 @1" /> <v:f eqn="prod @2 1 2" /> <v:f eqn="prod @3 21600 pixelWidth" /> <v:f eqn="prod @3 21600 pixelHeight" /> <v:f eqn="sum @0 0 1" /> <v:f eqn="prod @6 1 2" /> <v:f eqn="prod @7 21600 pixelWidth" /> <v:f eqn="sum @8 21600 0" /> <v:f eqn="prod @7 21600 pixelHeight" /> <v:f eqn="sum @10 21600 0" /> </v:formulas> <v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect" /> <o:lock v:ext="edit" aspectratio="t" /> </v:shapetype><v:shape id="_x0000_i1025" type="#_x0000_t75" style='width:414.75pt; height:328.5pt'> <v:imagedata src="file:///C:/DOCUME~1/chenpeng/LOCALS~1/Temp/msohtml1/01/clip_image001.png" mce_src="file:///C:/DOCUME~1/chenpeng/LOCALS~1/Temp/msohtml1/01/clip_image001.png" o:title="awt_bs_0_t1" /> </v:shape><![endif]--><!--[if !vml]--><!--[endif]-->

场景图 1 、角色图 100 FPS 25 - 35

bs1

<!--[if gte vml 1]><v:shape id="_x0000_i1026" type="#_x0000_t75" style='width:414.75pt;height:328.5pt'> <v:imagedata src="file:///C:/DOCUME~1/chenpeng/LOCALS~1/Temp/msohtml1/01/clip_image003.png" mce_src="file:///C:/DOCUME~1/chenpeng/LOCALS~1/Temp/msohtml1/01/clip_image003.png" o:title="awt_bs_1_t1" /> </v:shape><![endif]--><!--[if !vml]--><!--[endif]-->

场景图 1 、角色图 1000 FPS 3 - 6

bs2

<!--[if gte mso 9]><xml> <w:WordDocument> <w:View>Normal</w:View> <w:Zoom>0</w:Zoom> <w:PunctuationKerning /> <w:DrawingGridVerticalSpacing>7.8 磅</w:DrawingGridVerticalSpacing> <w:DisplayHorizontalDrawingGridEvery>0</w:DisplayHorizontalDrawingGridEvery> <w:DisplayVerticalDrawingGridEvery>2</w:DisplayVerticalDrawingGridEvery> <w:ValidateAgainstSchemas /> <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> <w:IgnoreMixedContent>false</w:IgnoreMixedContent> <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> <w:Compatibility> <w:SpaceForUL /> <w:BalanceSingleByteDoubleByteWidth /> <w:DoNotLeaveBackslashAlone /> <w:ULTrailSpace /> <w:DoNotExpandShiftReturn /> <w:AdjustLineHeightInTable /> <w:BreakWrappedTables /> <w:SnapToGridInCell /> <w:WrapTextWithPunct /> <w:UseAsianBreakRules /> <w:DontGrowAutofit /> <w:UseFELayout /> </w:Compatibility> <w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel> </w:WordDocument> </xml><![endif]--><!--[if gte mso 9]><xml> <w:LatentStyles DefLockedState="false" LatentStyleCount="156"> </w:LatentStyles> </xml><![endif]--> <!--[if gte mso 10]> <mce:style><! /* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.0pt; font-family:"Times New Roman"; mso-ansi-language:#0400; mso-fareast-language:#0400; mso-bidi-language:#0400;} --> <!--[endif]-->

三、完全在 BufferedImage DataBuffer 中进行图像处理

实际上在 Java 网游海盗时代、 Java 网游暗影世界等等网络游戏、还有 netbaens 以及其它种种不算慢的 Java 应用中,都存在大量使用 DataBuffer 进行的图像渲染;但在使用方式上,却只有暗影使用的 JGnet 引擎与常用的 Image Graphics 搭配模式较为接近,所以此示例使用了 JGnet 的部分代码构建。

PS :实际上汉森的 JGnet 客户端引擎目前并未开源,此部分代码得自其主页 homepage.jar 的反编译,是前天我看 csdn CTO 栏目专访汉森老总后去其首页发现的;视频中说 JGnet 有部分代码准备开源,我估计应该是 homepage.jar 里这部分,因为没有混淆且功能较少,其网站地址 http://www.handseeing.com ,纯 Applet 站点 )。

为给不愿反编译的看客提供方便,这里我稍微对 JGnet 进行一点分析(即 homepage.jar 中的那部分)。

就代码来看, JGnet 中所有 UI 继承自 GUIWidget GUIWidget 继承自 Container ),所有 JGnet 产生的 GUI 组件也都应当继承它。 JGnet 提供了一个 GUIRoot 为底层面板,所有 GUIWidget 衍生组件都应附着于 GUIRoot 之上,另外该引擎提供有 AbstractGameClient ClientContext 接口及相关实现用以调度相关组件, JGnet GUIRoot 载体可以使用 Applet 或者 Frame

JGnet 中大部分资源采用 zip 打包,当然也可以读取单图, JGnet 资源加载依赖于其提供的 ImageFactory 类, ImageFactory 的操作可分为如下两步:

第一步是 addArchive ,进行此步骤时指定的 zip 资源会被加载,其后将 read 图像资源为 ShortBufferImage ,再根据 zip 中该资源对应路径缓存到 daff.utill 包自备的 HashMap 中。

第二步是 getImage ,即获得指定路径的 ShortBufferImage 对象,此步骤以缓存优先,如果获得失败会尝试调用 loadImage 方法再次加载,并缓存到 daff 包自备的 HashMap 对象中。其中 loadImage 方法会根据后缀判定加载的资源类型,其中 jpg gif 或者 png 后缀文件会调用 daff 包中提供的 AwtImageLoader 类转化为 ShortBufferImage ,而非此后缀文件会直接按照 ShortBufferImage 格式进行加载。

JGnet 的图像绘制主要使用其内部提供的 ShortBufferImage ShortBufferGraphics 以及相关衍生类。 ShortBufferImage 是一个抽象类,没有父类,所有 JGnet 中图形资源都将表现为 ShortBufferImage ,绘制 ShortBufferImage 需要使用 daff.gui 包提供的专用画布 ShortBufferGraphics ,实际上 ShortBufferGraphics 是一个针对于 ShortBufferImage 的象素渲染器,内部以 short 数组形式保存有一个空白 BufferedImage DataBuffer ,所有绘制基于其上。

同大多数网游一样, JGnet 中大量使用自定义图形格式 ( 后缀 .img) ,其解释与转化通过 CompressedImage 类得以完成,由于自定义格式通常能更简洁的声明图像中各元素关系,所以 JGnet 处理其自定义格式资源会比加载普通图像资源更快。

JGnet 会根据环境的不同会采用 MsJvmGraphics SunJvmGraphics 两套绘图组件(皆继承自 ShortBufferGraphics ),据此我们可以初步断定其运行环境上兼容微软 jvm ,也就是最低可运行于 JRE1.1 之上。

讲解结束,下面我以 JGnet 进行针对 DataBuffer 进行渲染的 FPS 测试(对其某些细节进行了使用习惯上的扩展)。

1 DataBufferGameFrame.java

<!--[if gte mso 9]><xml> <w:WordDocument> <w:View>Normal</w:View> <w:Zoom>0</w:Zoom> <w:PunctuationKerning /> <w:DrawingGridVerticalSpacing>7.8 磅</w:DrawingGridVerticalSpacing> <w:DisplayHorizontalDrawingGridEvery>0</w:DisplayHorizontalDrawingGridEvery> <w:DisplayVerticalDrawingGridEvery>2</w:DisplayVerticalDrawingGridEvery> <w:ValidateAgainstSchemas /> <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> <w:IgnoreMixedContent>false</w:IgnoreMixedContent> <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> <w:Compatibility> <w:SpaceForUL /> <w:BalanceSingleByteDoubleByteWidth /> <w:DoNotLeaveBackslashAlone /> <w:ULTrailSpace /> <w:DoNotExpandShiftReturn /> <w:AdjustLineHeightInTable /> <w:BreakWrappedTables /> <w:SnapToGridInCell /> <w:WrapTextWithPunct /> <w:UseAsianBreakRules /> <w:DontGrowAutofit /> <w:UseFELayout /> </w:Compatibility> <w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel> </w:WordDocument> </xml><![endif]--><!--[if gte mso 9]><xml> <w:LatentStyles DefLockedState="false" LatentStyleCount="156"> </w:LatentStyles> </xml><![endif]--> <!--[if gte mso 10]> <mce:style><! /* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.0pt; font-family:"Times New Roman"; mso-ansi-language:#0400; mso-fareast-language:#0400; mso-bidi-language:#0400;} --> <!--[endif]-->

2 DataBufferCanvas.java

<!--[if !mso]> <mce:style><! v/:* {behavior:url(#default#VML);} o/:* {behavior:url(#default#VML);} w/:* {behavior:url(#default#VML);} .shape {behavior:url(#default#VML);} --> <!--[endif]--><!--[if gte mso 9]><xml> <w:WordDocument> <w:View>Normal</w:View> <w:Zoom>0</w:Zoom> <w:PunctuationKerning /> <w:DrawingGridVerticalSpacing>7.8 磅</w:DrawingGridVerticalSpacing> <w:DisplayHorizontalDrawingGridEvery>0</w:DisplayHorizontalDrawingGridEvery> <w:DisplayVerticalDrawingGridEvery>2</w:DisplayVerticalDrawingGridEvery> <w:ValidateAgainstSchemas /> <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> <w:IgnoreMixedContent>false</w:IgnoreMixedContent> <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> <w:Compatibility> <w:SpaceForUL /> <w:BalanceSingleByteDoubleByteWidth /> <w:DoNotLeaveBackslashAlone /> <w:ULTrailSpace /> <w:DoNotExpandShiftReturn /> <w:AdjustLineHeightInTable /> <w:BreakWrappedTables /> <w:SnapToGridInCell /> <w:WrapTextWithPunct /> <w:UseAsianBreakRules /> <w:DontGrowAutofit /> <w:UseFELayout /> </w:Compatibility> <w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel> </w:WordDocument> </xml><![endif]--><!--[if gte mso 9]><xml> <w:LatentStyles DefLockedState="false" LatentStyleCount="156"> </w:LatentStyles> </xml><![endif]--> <!--[if gte mso 10]> <mce:style><! /* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.0pt; font-family:"Times New Roman"; mso-ansi-language:#0400; mso-fareast-language:#0400; mso-bidi-language:#0400;} --> <!--[endif]-->

场景图 1 、角色图 1 FPS 140 - 160

j0

<!--[if gte vml 1]><v:shapetype id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f"> <v:stroke joinstyle="miter" /> <v:formulas> <v:f eqn="if lineDrawn pixelLineWidth 0" /> <v:f eqn="sum @0 1 0" /> <v:f eqn="sum 0 0 @1" /> <v:f eqn="prod @2 1 2" /> <v:f eqn="prod @3 21600 pixelWidth" /> <v:f eqn="prod @3 21600 pixelHeight" /> <v:f eqn="sum @0 0 1" /> <v:f eqn="prod @6 1 2" /> <v:f eqn="prod @7 21600 pixelWidth" /> <v:f eqn="sum @8 21600 0" /> <v:f eqn="prod @7 21600 pixelHeight" /> <v:f eqn="sum @10 21600 0" /> </v:formulas> <v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect" /> <o:lock v:ext="edit" aspectratio="t" /> </v:shapetype><v:shape id="_x0000_i1025" type="#_x0000_t75" style='width:414.75pt; height:327.75pt'> <v:imagedata src="file:///C:/DOCUME~1/chenpeng/LOCALS~1/Temp/msohtml1/01/clip_image001.png" mce_src="file:///C:/DOCUME~1/chenpeng/LOCALS~1/Temp/msohtml1/01/clip_image001.png" o:title="20090402_jgnet_00" /> </v:shape><![endif]--><!--[if !vml]--><!--[endif]-->

场景图 1 、角色图 100 FPS 115 - 125

j1

<!--[if gte vml 1]><v:shape id="_x0000_i1026" type="#_x0000_t75" style='width:414.75pt;height:327.75pt'> <v:imagedata src="file:///C:/DOCUME~1/chenpeng/LOCALS~1/Temp/msohtml1/01/clip_image003.png" mce_src="file:///C:/DOCUME~1/chenpeng/LOCALS~1/Temp/msohtml1/01/clip_image003.png" o:title="20090402_jgnet_01" /> </v:shape><![endif]--><!--[if !vml]--><!--[endif]-->

场景图 1 、角色图 1000 FPS 55 - 70

j2

<!--[if gte mso 9]><xml> <w:WordDocument> <w:View>Normal</w:View> <w:Zoom>0</w:Zoom> <w:PunctuationKerning /> <w:DrawingGridVerticalSpacing>7.8 磅</w:DrawingGridVerticalSpacing> <w:DisplayHorizontalDrawingGridEvery>0</w:DisplayHorizontalDrawingGridEvery> <w:DisplayVerticalDrawingGridEvery>2</w:DisplayVerticalDrawingGridEvery> <w:ValidateAgainstSchemas /> <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> <w:IgnoreMixedContent>false</w:IgnoreMixedContent> <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> <w:Compatibility> <w:SpaceForUL /> <w:BalanceSingleByteDoubleByteWidth /> <w:DoNotLeaveBackslashAlone /> <w:ULTrailSpace /> <w:DoNotExpandShiftReturn /> <w:AdjustLineHeightInTable /> <w:BreakWrappedTables /> <w:SnapToGridInCell /> <w:WrapTextWithPunct /> <w:UseAsianBreakRules /> <w:DontGrowAutofit /> <w:UseFELayout /> </w:Compatibility> <w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel> </w:WordDocument> </xml><![endif]--><!--[if gte mso 9]><xml> <w:LatentStyles DefLockedState="false" LatentStyleCount="156"> </w:LatentStyles> </xml><![endif]--> <!--[if gte mso 10]> <mce:style><! /* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.0pt; font-family:"Times New Roman"; mso-ansi-language:#0400; mso-fareast-language:#0400; mso-bidi-language:#0400;} --> <!--[endif]-->

通过简单比较后我们可以发现, Graphics 绘图无论在直接 BufferedImage 双缓冲或者利用 BufferStrategy 以换取硬件加速的前提下,都无法与使用 DataBuffer 直接进行像素绘图的 ShortBufferGraphics 相比, BufferStrategy 在绘制千人时更出现了令笔者整个人都斯巴达了的 4 ||| ,即使在测试中笔者改 BufferedImage VolatileImage 也收效甚微,在配置较高的微机上应当会有些许改善,但用户使用机型却并非我们所能控制的。

由于 JGnet 中直接获得图像 DataBuffer short[] 形式,并以此进行图像绘制,所以 ShortBufferImage ShortBufferGraphics 称得上是一组先天的 Java 图形缓冲对象,就效率上讲,使用 ShortBufferGraphics 绘图获得的 FPS 明显高于使用 Graphics2D 绘图,这是数组操作先天性的效率优势所决定的。但缺点在于, ShortBufferImage ShortBufferGraphics 其并非直接继承自 Image Graphics ,两者间函数不能完全对应,有些常用方法尚待实现,很多常用方法尚需 ShortBufferGraphics 使用者自行解决。

单就效率而言,应该说采取 DataBuffer 进行缓冲绘图明显优于单纯利用 BufferStrategy 或者 BufferedImage 获得 Graphics 后进行绘图。

总体上看,如果我们追求最高 FPS ,则使用 BufferedImage 产生 DataBuffer 进行象素处理是最为高效的,与方法二混用后效率还将更高,但遗憾的是目前缺少相关开源组件,可用资源上略显不足,有待相关公司或者个人提供,另外我们也可以考虑功能混用,即主体部分使用 DataBuffer 渲染,未实现部分以 Graphics 补足,但这肯定会对运行效率产生影响。

由于 JGnet 目前属于闭源项目,所以我无法公开其任何代码实现。为此我自制了一个非常简单的 DataBuffer 中图像处理类 SimpleIntBufferImage ,以供各位看客参考。

<!--[if gte mso 9]><xml> <w:WordDocument> <w:View>Normal</w:View> <w:Zoom>0</w:Zoom> <w:PunctuationKerning /> <w:DrawingGridVerticalSpacing>7.8 磅</w:DrawingGridVerticalSpacing> <w:DisplayHorizontalDrawingGridEvery>0</w:DisplayHorizontalDrawingGridEvery> <w:DisplayVerticalDrawingGridEvery>2</w:DisplayVerticalDrawingGridEvery> <w:ValidateAgainstSchemas /> <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> <w:IgnoreMixedContent>false</w:IgnoreMixedContent> <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> <w:Compatibility> <w:SpaceForUL /> <w:BalanceSingleByteDoubleByteWidth /> <w:DoNotLeaveBackslashAlone /> <w:ULTrailSpace /> <w:DoNotExpandShiftReturn /> <w:AdjustLineHeightInTable /> <w:BreakWrappedTables /> <w:SnapToGridInCell /> <w:WrapTextWithPunct /> <w:UseAsianBreakRules /> <w:DontGrowAutofit /> <w:UseFELayout /> </w:Compatibility> <w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel> </w:WordDocument> </xml><![endif]--><!--[if gte mso 9]><xml> <w:LatentStyles DefLockedState="false" LatentStyleCount="156"> </w:LatentStyles> </xml><![endif]--> <!--[if gte mso 10]> <mce:style><! /* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.0pt; font-family:"Times New Roman"; mso-ansi-language:#0400; mso-fareast-language:#0400; mso-bidi-language:#0400;} --> <!--[endif]-->

SimpleIntBufferImage.java 源码如下:

<!--[if gte mso 9]><xml> <w:WordDocument> <w:View>Normal</w:View> <w:Zoom>0</w:Zoom> <w:PunctuationKerning /> <w:DrawingGridVerticalSpacing>7.8 磅</w:DrawingGridVerticalSpacing> <w:DisplayHorizontalDrawingGridEvery>0</w:DisplayHorizontalDrawingGridEvery> <w:DisplayVerticalDrawingGridEvery>2</w:DisplayVerticalDrawingGridEvery> <w:ValidateAgainstSchemas /> <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> <w:IgnoreMixedContent>false</w:IgnoreMixedContent> <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> <w:Compatibility> <w:SpaceForUL /> <w:BalanceSingleByteDoubleByteWidth /> <w:DoNotLeaveBackslashAlone /> <w:ULTrailSpace /> <w:DoNotExpandShiftReturn /> <w:AdjustLineHeightInTable /> <w:BreakWrappedTables /> <w:SnapToGridInCell /> <w:WrapTextWithPunct /> <w:UseAsianBreakRules /> <w:DontGrowAutofit /> <w:UseFELayout /> </w:Compatibility> <w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel> </w:WordDocument> </xml><![endif]--><!--[if gte mso 9]><xml> <w:LatentStyles DefLockedState="false" LatentStyleCount="156"> </w:LatentStyles> </xml><![endif]--> <!--[if gte mso 10]> <mce:style><! /* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.0pt; font-family:"Times New Roman"; mso-ansi-language:#0400; mso-fareast-language:#0400; mso-bidi-language:#0400;} --> <!--[endif]-->

简单应用:

效果如下:

copy

Java游戏开发中怎样才能获得更快的FPS?


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论