libgdx学习笔记系列(三)会动的小人
程序员文章站
2022-05-21 10:25:38
...
(重要提示:Stage类在3.19号被作者更新后初始化方法已经改变,本篇代码在最新的版本中,会出错。详细请参考源码,及文档和第七篇笔记viewpoint的介绍 )
上次弄好了文字显示,可能有的同学感觉很繁琐。今天开始之前先给大家介绍下另外一种文字显示方法。直接使用字体文件。
拷贝今天刚刚更新编译的好的freetype的相关jar包和so文件。还有字体文件,(系统盘中的windows/fonts目录下有大量字体可以用,当然你也可以下载个性字体。)
修改上篇的代码如下:
确实比原来的方便了点,起码不用使用工具类手动编辑文字了。
如果游戏中的文字比较少,推荐使用上篇手动生成,节省系统资源,毕竟加载字体文件开销还是比较大的。如果文字比较多,可以使用这种方法。但是一定注意及时释放资源。
好了,接下来显示个人物吧
人物图片
拷贝人物图片到android中的assets目录
我放到了data/image目录下
修改Mygame如下:
其实主要就2行代码
加载人物
显示出来
注意后边的三个参数,texture人物纹理,后边两个参数其实就是指屏幕的左下角原点。
libgdx是以屏幕左下角为起始点的。
但是这个小人还不会动。
在开始动之前,先要说下Stage和Actor的概念。顾名思义,舞台和演员。
其实很好理解。以现实中的舞台和演员为例。比如京剧,其中演员就是指的具体的角色青衣,花旦等等扮演者都是演员。舞台当然就是表演的场所了。
具体在游戏中,演员本身有自己的一些特性,比如会走动,会唱歌,跳舞等等,舞台就是容纳演员表演的舞台。游戏中的演员一般就是指具体的游戏元素,例如我们图片中的人物,还有中间显示的文字。
再举个大家大部分都玩过的游戏。愤怒的小鸟,什么是演员呢。小鸟,猪,弹弓,被关的小鸟,场景中的各种冰块,石头,木制。建筑物,甚至于,显示的分数,碰撞的特效都可以称为演员。舞台呢,就是容纳这些演员的一个容器。
我们先看看Actor的源码中的解释
演员中主要包括它的位置信息,大小,缩放,旋转,颜色等等特征。
具体可参考官方的API文档。
源码+API文档=最权威教程
希望大家善用API文档和源码。
然后再看下stage的源码和文档的解释。
我们看到Group root;这个定义。group是什么呢
Group 就是舞台中的所有演员,舞台是通过Group的 addActor方法来添加演员的。
大概概念介绍到这里,如有疑问可以参考一些教程和官方文档
我们来新建一个演员类如下
修改MyGame类
运行下,好了,动起来了。(相关图片可以在源码中获取)
Animation 是我们实现动画的关键类。其实通过查看它的源码我们可以知道。
其实它就是通过切换单个图片文件来实现连续动画的。并且我们还可以改变它的播放模式,
例如动画只播放一次,循环播放,倒退播放(可以想象下磁带倒着播放),循环随机播放等等
这里我们使用的是正常播放模式,默认循环播放。
这是干什么用的呢。还是那句话,看源码
return the time span between the current frame and the last frame in seconds. Might be smoothed over n frames.
什么意思呢,它返回的是当前帧和前一帧之间的时间跨度。
为了更直观。我输出了下它的变化。
输出结果(注意,这里我使用的是真实的设备:华为C8813)
恩,每帧之间的间隔大约是0.016s。这说明什么呢。
在理解这个之前我们要先了解下帧和帧率的概念,什么是帧呢,简单理解就是屏幕刷新一次。显示出我们需要看到的内容。还记得以前的老电影吗?播放胶片的那个(当然现在也是播放胶片),它每秒大约播放24张胶片,每张胶片为1帧,相当于每秒刷新24次,那么它的帧率就是24.对于游戏简单来说,帧率就是我们每秒刷新屏幕的次数,还记得前面说的render()方法吗,我不是说它其实就像个循环方法一样不停循环码,其实这个可以认为是它每秒执行了多少次。执行了多少次呢。算下吧。1s/0.016s≈62.5 也就是大概60。大家可以数下上面日志输出的个数。从第二行58秒开始到倒数第二行结束。这一秒内输出了多少次。
当然为了验证下我的计算和猜测。我们来输出下我们游戏真实的帧率。
修改MyGame代码如下
62的帧率。验证了我的猜测。
在这里我要提醒下大家,不要把帧率,和动画播放速度弄混了。
动画切换速度为0.06s。帧率间隔为0.016,为了直观算作0.02好了。
估算下,每张图片显示的时间,屏幕刷新了几次。
这么弱的数学题我就不算了
通过上面的介绍,相信都能了解上面两句的含义了。留给大家理解。
好了,收工。
源码地址:http://pan.baidu.com/s/1sjHFJh7
注意:图片资源来自于互联网,仅限学习,请勿用于商业用途,否则后果自负。
上次弄好了文字显示,可能有的同学感觉很繁琐。今天开始之前先给大家介绍下另外一种文字显示方法。直接使用字体文件。
拷贝今天刚刚更新编译的好的freetype的相关jar包和so文件。还有字体文件,(系统盘中的windows/fonts目录下有大量字体可以用,当然你也可以下载个性字体。)
修改上篇的代码如下:
@Override public void create() { //加载字体文件从电脑上拷贝的华文琥珀字体,当然你可以使用任意一种中文字体。 FreeTypeFontGenerator freeTypeFontGenerator = new FreeTypeFontGenerator(Gdx.files.internal("data/font/STHUPO.TTF")); //关于字体的一些配置参数,详细的可以看官方API和FreeTypeFontGenerator的源码 FreeTypeFontGenerator.FreeTypeFontParameter fontParameter = new FreeTypeFontGenerator.FreeTypeFontParameter(); //注意这不是我们要显示的文字,其实相当于我们上篇使用的文字工具编辑的字符串内容, // 这里是默认的字符串加上了我们要使用的汉字。 fontParameter.characters = FreeTypeFontGenerator.DEFAULT_CHARS + "你好"; //根据参数设置生成的字体数据,这就相当于上篇的myfont.fnt文件和myfont.png FreeTypeFontGenerator.FreeTypeBitmapFontData fontData = freeTypeFontGenerator.generateData(fontParameter); //及时释放资源,避免内存泄漏,因为中文字体文件一般都比较大 freeTypeFontGenerator.dispose(); //从字面看,其实也是加载的图片 bitmapFont = new BitmapFont(fontData, fontData.getTextureRegions(), false); batch = new SpriteBatch(); //文字绘制 (注释掉昨天的) // bitmapFont = new BitmapFont(Gdx.files.internal("data/font/myfont.fnt"), // Gdx.files.internal("data/font/myfont.png"), false); }
确实比原来的方便了点,起码不用使用工具类手动编辑文字了。
如果游戏中的文字比较少,推荐使用上篇手动生成,节省系统资源,毕竟加载字体文件开销还是比较大的。如果文字比较多,可以使用这种方法。但是一定注意及时释放资源。
好了,接下来显示个人物吧
人物图片
拷贝人物图片到android中的assets目录
我放到了data/image目录下
修改Mygame如下:
package com.me.mygdxgame; import com.badlogic.gdx.ApplicationListener; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator; public class MyGame implements ApplicationListener { private SpriteBatch batch; private BitmapFont bitmapFont; private Texture texture; @Override public void create() { //加载字体文件从电脑上拷贝的华文琥珀字体,当然你可以使用任意一种中文字体。 FreeTypeFontGenerator freeTypeFontGenerator = new FreeTypeFontGenerator(Gdx.files.internal("data/font/STHUPO.TTF")); //关于字体的一些配置参数,详细的可以看官方API和FreeTypeFontGenerator的源码 FreeTypeFontGenerator.FreeTypeFontParameter fontParameter = new FreeTypeFontGenerator.FreeTypeFontParameter(); //注意这不是我们要显示的文字,其实相当于我们上篇使用的文字工具编辑的字符串内容, // 这里是默认的字符串加上了我们要使用的汉字。 fontParameter.characters = FreeTypeFontGenerator.DEFAULT_CHARS + "你好"; //根据参数设置生成的字体数据,这就相当于上篇的myfont.fnt文件和myfont.png FreeTypeFontGenerator.FreeTypeBitmapFontData fontData = freeTypeFontGenerator.generateData(fontParameter); //及时释放资源,避免内存泄漏,因为中文字体文件一般都比较大 freeTypeFontGenerator.dispose(); //从字面看,其实也是加载的图片 bitmapFont = new BitmapFont(fontData, fontData.getTextureRegions(), false); batch = new SpriteBatch(); //文字绘制 (注释掉昨天的) // bitmapFont = new BitmapFont(Gdx.files.internal("data/font/myfont.fnt"), // Gdx.files.internal("data/font/myfont.png"), false); texture = new Texture(Gdx.files.internal("data/image/girl.png")); } @Override public void render() { //这个经常出现的代码就是opengl ES的清屏方法 Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); //使用一种颜色填充屏幕 //前三个是屏幕颜色的参数,最后一个是透明度,注意颜色不是我们熟悉的RGB Gdx.gl.glClearColor(0.57f, 0.40f, 0.55f, 1.0f); batch.begin(); bitmapFont.draw(batch, "你好libgdx!......", Gdx.graphics.getWidth() * 0.4f, Gdx.graphics.getHeight() / 2); batch.draw(texture,0, 0); batch.end(); } @Override public void dispose() { batch.dispose(); bitmapFont.dispose(); texture.dispose(); } @Override public void resize(int width, int height) { } @Override public void pause() { } @Override public void resume() { } }
其实主要就2行代码
加载人物
texture = new Texture(Gdx.files.internal("data/image/01.png"));
显示出来
batch.draw(texture,0, 0);
注意后边的三个参数,texture人物纹理,后边两个参数其实就是指屏幕的左下角原点。
libgdx是以屏幕左下角为起始点的。
但是这个小人还不会动。
在开始动之前,先要说下Stage和Actor的概念。顾名思义,舞台和演员。
其实很好理解。以现实中的舞台和演员为例。比如京剧,其中演员就是指的具体的角色青衣,花旦等等扮演者都是演员。舞台当然就是表演的场所了。
具体在游戏中,演员本身有自己的一些特性,比如会走动,会唱歌,跳舞等等,舞台就是容纳演员表演的舞台。游戏中的演员一般就是指具体的游戏元素,例如我们图片中的人物,还有中间显示的文字。
再举个大家大部分都玩过的游戏。愤怒的小鸟,什么是演员呢。小鸟,猪,弹弓,被关的小鸟,场景中的各种冰块,石头,木制。建筑物,甚至于,显示的分数,碰撞的特效都可以称为演员。舞台呢,就是容纳这些演员的一个容器。
我们先看看Actor的源码中的解释
2D scene graph node. An actor has a position, rectangular size, origin, scale, rotation, Z index, and color. The position * corresponds to the unrotated, unscaled bottom left corner of the actor. The position is relative to the actor's parent. The * origin is relative to the position and is used for scale and rotation. * <p> * An actor has a list of in progress {@link Action actions} that are applied to the actor (often over time). These are generally * used to change the presentation of the actor (moving it, resizing it, etc). See {@link #act(float)}, {@link Action} and its * many subclasses. * <p> * An actor has two kinds of listeners associated with it: "capture" and regular. The listeners are notified of events the actor * or its children receive. The regular listeners are designed to allow an actor to respond to events that have been delivered. * The capture listeners are designed to allow a parent or container actor to handle events before child actors. See {@link #fire} * for more details. * <p> * An {@link InputListener} can receive all the basic input events. More complex listeners (like {@link ClickListener} and * {@link ActorGestureListener}) can listen for and combine primitive events and recognize complex interactions like multi-touch * or pinch.
演员中主要包括它的位置信息,大小,缩放,旋转,颜色等等特征。
具体可参考官方的API文档。
源码+API文档=最权威教程
希望大家善用API文档和源码。
然后再看下stage的源码和文档的解释。
我们看到Group root;这个定义。group是什么呢
Group 就是舞台中的所有演员,舞台是通过Group的 addActor方法来添加演员的。
大概概念介绍到这里,如有疑问可以参考一些教程和官方文档
我们来新建一个演员类如下
package com.me.mygdxgame; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g2d.Animation; import com.badlogic.gdx.graphics.g2d.Batch; import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.scenes.scene2d.Actor; public class GirlActor extends Actor { TextureRegion[] girlRegion; float stateTime; //当前帧 TextureRegion currentFrame; Animation animation; public GirlActor(Texture[] texture) { girlRegion = new TextureRegion[16]; //把Texture转换下 for (int i = 0; i < 16; i++) { girlRegion[i] = new TextureRegion(texture[i]); } //动画播放,参数为动画播放速度。和纹理数组 //0.06*16=0.96 大概就是1秒钟播放完这个动画。 animation = new Animation(0.06f, girlRegion); } @Override public void draw(Batch batch, float parentAlpha) { stateTime += Gdx.graphics.getDeltaTime(); //下一帧 currentFrame = animation.getKeyFrame(stateTime, true); //绘制人物 batch.draw(currentFrame, 0, 0); } }
修改MyGame类
package com.me.mygdxgame; import com.badlogic.gdx.ApplicationListener; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator; import com.badlogic.gdx.scenes.scene2d.Stage; import com.badlogic.gdx.scenes.scene2d.ui.Label; public class MyGame implements ApplicationListener { private SpriteBatch batch; private BitmapFont bitmapFont; private Texture texture; private Stage stage; @Override public void create() { //加载字体文件从电脑上拷贝的华文琥珀字体,当然你可以使用任意一种中文字体。 FreeTypeFontGenerator freeTypeFontGenerator = new FreeTypeFontGenerator(Gdx.files.internal("data/font/STHUPO.TTF")); //关于字体的一些配置参数,详细的可以看官方API和FreeTypeFontGenerator的源码 FreeTypeFontGenerator.FreeTypeFontParameter fontParameter = new FreeTypeFontGenerator.FreeTypeFontParameter(); //注意这不是我们要显示的文字,其实相当于我们上篇使用的文字工具编辑的字符串内容, // 这里是默认的字符串加上了我们要使用的汉字。 fontParameter.characters = FreeTypeFontGenerator.DEFAULT_CHARS + "你好"; //根据参数设置生成的字体数据,这就相当于上篇的myfont.fnt文件和myfont.png FreeTypeFontGenerator.FreeTypeBitmapFontData fontData = freeTypeFontGenerator.generateData(fontParameter); //及时释放资源,避免内存泄漏,因为中文字体文件一般都比较大 freeTypeFontGenerator.dispose(); //从字面看,其实也是加载的图片 bitmapFont = new BitmapFont(fontData, fontData.getTextureRegions(), false); batch = new SpriteBatch(); //文字绘制 (注释掉昨天的) // bitmapFont = new BitmapFont(Gdx.files.internal("data/font/myfont.fnt"), // Gdx.files.internal("data/font/myfont.png"), false); texture = new Texture(Gdx.files.internal("data/image/girl.png")); Texture[] girlTextures = new Texture[16]; //加载人物动作的16幅图片 for (int i = 0; i < 16; i++) { girlTextures[i] = new Texture(Gdx.files.internal("data/image/" + (i + 1) + ".png")); } //初始化演员类 GirlActor girlActor = new GirlActor(girlTextures); //初始化舞台,舞台大小为屏幕大小 stage = new Stage(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), true); //把演员放入舞台 stage.addActor(girlActor); } @Override public void render() { //这个经常出现的代码就是opengl ES的清屏方法 Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); //使用一种颜色填充屏幕 //前三个是屏幕颜色的参数,最后一个是透明度,注意颜色不是我们熟悉的RGB Gdx.gl.glClearColor(0.57f, 0.40f, 0.55f, 1.0f); stage.act(); //画出舞台中的演员,draw方法其实就跟下边的方法类似也是以begin开始,end结束,所以我们就不用再写了。 stage.draw(); batch.begin(); bitmapFont.draw(batch, "你好libgdx!......", Gdx.graphics.getWidth() * 0.4f, Gdx.graphics.getHeight() / 2); // batch.draw(texture,0, 0); batch.end(); } @Override public void dispose() { batch.dispose(); bitmapFont.dispose(); texture.dispose(); stage.dispose(); } @Override public void resize(int width, int height) { } @Override public void pause() { } @Override public void resume() { } }
运行下,好了,动起来了。(相关图片可以在源码中获取)
Animation 是我们实现动画的关键类。其实通过查看它的源码我们可以知道。
其实它就是通过切换单个图片文件来实现连续动画的。并且我们还可以改变它的播放模式,
例如动画只播放一次,循环播放,倒退播放(可以想象下磁带倒着播放),循环随机播放等等
这里我们使用的是正常播放模式,默认循环播放。
Gdx.graphics.getDeltaTime();
这是干什么用的呢。还是那句话,看源码
return the time span between the current frame and the last frame in seconds. Might be smoothed over n frames.
什么意思呢,它返回的是当前帧和前一帧之间的时间跨度。
为了更直观。我输出了下它的变化。
@Override public void draw(Batch batch, float parentAlpha) { System.out.println("DeltaTime====="+Gdx.graphics.getDeltaTime()); stateTime += Gdx.graphics.getDeltaTime(); //下一帧 currentFrame = animation.getKeyFrame(stateTime, true); //绘制人物 batch.draw(currentFrame, 0, 0);
输出结果(注意,这里我使用的是真实的设备:华为C8813)
03-18 08:51:57.994 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016970333 03-18 08:51:58.004 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016164333 03-18 08:51:58.024 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016304668 03-18 08:51:58.034 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016325668 03-18 08:51:58.054 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.015347001 03-18 08:51:58.074 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.015698 03-18 08:51:58.084 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016385 03-18 08:51:58.104 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016376335 03-18 08:51:58.124 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016360667 03-18 08:51:58.134 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016013334 03-18 08:51:58.154 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016335 03-18 08:51:58.164 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016316334 03-18 08:51:58.184 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016595 03-18 08:51:58.204 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016330667 03-18 08:51:58.214 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016255334 03-18 08:51:58.234 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016421335 03-18 08:51:58.254 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016354334 03-18 08:51:58.264 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016077667 03-18 08:51:58.284 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016364332 03-18 08:51:58.304 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016402 03-18 08:51:58.314 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016265666 03-18 08:51:58.334 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016333 03-18 08:51:58.344 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016339 03-18 08:51:58.364 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016320666 03-18 08:51:58.394 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016559333 03-18 08:51:58.404 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.017074665 03-18 08:51:58.414 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016678333 03-18 08:51:58.434 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016316665 03-18 08:51:58.444 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016318668 03-18 08:51:58.464 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016178 03-18 08:51:58.474 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.015533668 03-18 08:51:58.504 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016484333 03-18 08:51:58.514 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016375002 03-18 08:51:58.534 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016400667 03-18 08:51:58.544 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016734667 03-18 08:51:58.564 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016447667 03-18 08:51:58.574 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.015874667 03-18 08:51:58.594 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016333 03-18 08:51:58.614 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016313998 03-18 08:51:58.624 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.015864002 03-18 08:51:58.644 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016313668 03-18 08:51:58.654 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016386667 03-18 08:51:58.674 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016461 03-18 08:51:58.694 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016348667 03-18 08:51:58.704 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016316665 03-18 08:51:58.724 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016343666 03-18 08:51:58.744 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016439 03-18 08:51:58.754 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016340334 03-18 08:51:58.774 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016478999 03-18 08:51:58.794 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016400333 03-18 08:51:58.804 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016583 03-18 08:51:58.824 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016164333 03-18 08:51:58.834 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016223 03-18 08:51:58.854 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016178 03-18 08:51:58.874 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016279334 03-18 08:51:58.884 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016175 03-18 08:51:58.904 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016370334 03-18 08:51:58.924 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016302332 03-18 08:51:58.934 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016382333 03-18 08:51:58.954 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016404334 03-18 08:51:58.974 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016325668 03-18 08:51:58.984 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016703665 03-18 08:51:59.004 3604-3747/com.me.mygdxgame I/System.out﹕ DeltaTime=====0.016402332
恩,每帧之间的间隔大约是0.016s。这说明什么呢。
在理解这个之前我们要先了解下帧和帧率的概念,什么是帧呢,简单理解就是屏幕刷新一次。显示出我们需要看到的内容。还记得以前的老电影吗?播放胶片的那个(当然现在也是播放胶片),它每秒大约播放24张胶片,每张胶片为1帧,相当于每秒刷新24次,那么它的帧率就是24.对于游戏简单来说,帧率就是我们每秒刷新屏幕的次数,还记得前面说的render()方法吗,我不是说它其实就像个循环方法一样不停循环码,其实这个可以认为是它每秒执行了多少次。执行了多少次呢。算下吧。1s/0.016s≈62.5 也就是大概60。大家可以数下上面日志输出的个数。从第二行58秒开始到倒数第二行结束。这一秒内输出了多少次。
当然为了验证下我的计算和猜测。我们来输出下我们游戏真实的帧率。
修改MyGame代码如下
package com.me.mygdxgame; import com.badlogic.gdx.ApplicationListener; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator; import com.badlogic.gdx.scenes.scene2d.Stage; import com.badlogic.gdx.scenes.scene2d.ui.Label; public class MyGame implements ApplicationListener { private SpriteBatch batch; private BitmapFont bitmapFont; private Texture texture; private Stage stage; Label fps; Label newFPS; @Override public void create() { //加载字体文件从电脑上拷贝的华文琥珀字体,当然你可以使用任意一种中文字体。 FreeTypeFontGenerator freeTypeFontGenerator = new FreeTypeFontGenerator(Gdx.files.internal("data/font/STHUPO.TTF")); //关于字体的一些配置参数,详细的可以看官方API和FreeTypeFontGenerator的源码 FreeTypeFontGenerator.FreeTypeFontParameter fontParameter = new FreeTypeFontGenerator.FreeTypeFontParameter(); //注意这不是我们要显示的文字,其实相当于我们上篇使用的文字工具编辑的字符串内容, // 这里是默认的字符串加上了我们要使用的汉字。 fontParameter.characters = FreeTypeFontGenerator.DEFAULT_CHARS + "你好"; //根据参数设置生成的字体数据,这就相当于上篇的myfont.fnt文件和myfont.png FreeTypeFontGenerator.FreeTypeBitmapFontData fontData = freeTypeFontGenerator.generateData(fontParameter); //及时释放资源,避免内存泄漏,因为中文字体文件一般都比较大 freeTypeFontGenerator.dispose(); //从字面看,其实也是加载的图片 bitmapFont = new BitmapFont(fontData, fontData.getTextureRegions(), false); batch = new SpriteBatch(); //文字绘制 (注释掉昨天的) // bitmapFont = new BitmapFont(Gdx.files.internal("data/font/myfont.fnt"), // Gdx.files.internal("data/font/myfont.png"), false); texture = new Texture(Gdx.files.internal("data/image/girl.png")); Texture[] girlTextures = new Texture[16]; //加载人物动作的16幅图片 for (int i = 0; i < 16; i++) { girlTextures[i] = new Texture(Gdx.files.internal("data/image/" + (i + 1) + ".png")); } //初始化演员类 GirlActor girlActor = new GirlActor(girlTextures); //初始化舞台,舞台大小为屏幕大小 stage = new Stage(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), true); //黑色显示 Label.LabelStyle labelStyle = new Label.LabelStyle(new BitmapFont(), Color.BLACK); //显示内容 fps = new Label("FPS:", labelStyle); fps.setName("fps"); //显示在左上角位置,减去显示字体的高度,要不然会跑到屏幕外面,根本看不到 fps.setY(Gdx.graphics.getHeight() - fps.getHeight()); fps.setX(0); //把演员放入舞台 stage.addActor(girlActor); stage.addActor(fps); } @Override public void render() { //这个经常出现的代码就是opengl ES的清屏方法 Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); //使用一种颜色填充屏幕 //前三个是屏幕颜色的参数,最后一个是透明度,注意颜色不是我们熟悉的RGB Gdx.gl.glClearColor(0.57f, 0.40f, 0.55f, 1.0f); //因为render方法在不停执行,那么我们就在这里实时的更新系统帧率的数据 //获取fps,然后修改它的显示为获取的系统帧率值 newFPS = (Label) stage.getRoot().findActor("fps"); newFPS.setText("FPS:" + Gdx.graphics.getFramesPerSecond()); stage.act(); //画出舞台中的演员,draw方法其实就跟下边的方法类似也是以begin开始,end结束,所以我们就不用再写了。 stage.draw(); batch.begin(); bitmapFont.draw(batch, "你好libgdx!......", Gdx.graphics.getWidth() * 0.4f, Gdx.graphics.getHeight() / 2); // batch.draw(texture,0, 0); batch.end(); } @Override public void dispose() { batch.dispose(); bitmapFont.dispose(); texture.dispose(); stage.dispose(); } @Override public void resize(int width, int height) { } @Override public void pause() { } @Override public void resume() { } }
62的帧率。验证了我的猜测。
在这里我要提醒下大家,不要把帧率,和动画播放速度弄混了。
动画切换速度为0.06s。帧率间隔为0.016,为了直观算作0.02好了。
估算下,每张图片显示的时间,屏幕刷新了几次。
这么弱的数学题我就不算了
stateTime += Gdx.graphics.getDeltaTime(); //下一帧 currentFrame = animation.getKeyFrame(stateTime, true);
通过上面的介绍,相信都能了解上面两句的含义了。留给大家理解。
好了,收工。
源码地址:http://pan.baidu.com/s/1sjHFJh7
注意:图片资源来自于互联网,仅限学习,请勿用于商业用途,否则后果自负。