转自:http://www.apkbus.com/android-58760-1-1.html
移植素材SDK版本俄罗斯方块源码下载:
Tetris_slide.rar(537.04 KB, 下载次数: 72)
本讲移植后的源码:
libgdxTetris.rar(3.97 MB, 下载次数: 605)
我们今天继续上次 俄罗斯方块移植第五讲的内容,在第四讲基本结束的时候,我们已经基本实现了游戏的功能,但一些最主要的功能都没有添加进去,如存档,退出提示等等。
为了体现 libgdx和SDK版本的不同,我们先添加一个粒子系统的效果。不然我们为什么要用游戏引擎做游戏?就是因为它在游戏开发方面比SDK更强大,否则我们直接用SDK开发好了。这个效果已经在第三讲实现过了,没有看过的同学可以看看。我们直接把这段代码复制进来。(复制到UiScreen的render()函数里, 当然对应新增加的变量要自行添加,对应的 particle.p和particle.png放到asset文件夹下)。
粒子效果出来了。我们继续下一步的工作。我们接下来需要原SDK版下方滑动开始的效果,首先对照一下,下方是原SDK版本的开始界面,注意看到下方有一个可以滑动用于开始游戏的滑块。
我们将原来 SDK版本的中构成滑块的图片资源经过修改后复制到新的libgdx版本中去,放在工程的assets文件夹下
注意我们这里把底座图片
换成一张空的图片(背景透明,png格式),空图片的大小为 1024*128 (还记得 libgdx 的要求吗,图片的边长必须是 2 的幂次),滑块图片我们稍作处理,也变成 2的幂次。
然后我们这里教大家使用Libgdx里面一个很方便的控件,模拟SDK版本中的滑块效果,这是一个封装好的类,Slider。我们先来看看libgdx 0.9.4中的官方文档中是怎么说的。
java.lang.Object
|com.badlogic.gdx.scenes.scene2d.Actor
|com.badlogic.gdx.scenes.scene2d.ui.Widget
| com.badlogic.gdx.scenes.scene2d.ui.Slider
我们可以看到,所谓的滑块类 Slider也是一个Actor,那么它也要加在舞台stage中绘制。
我们来看看 Slider 的构造函数:
我们今天用第二种构造函数(其实第三种和第二种的区别只在于第三种在创建时给 Slider 取了一个名字),至于第一种和第四种的用法大家可以参见官方文档,特别是 Skin 的用法大家可以看一看,很有帮助。
我们将修改好的UiScreen里关于
的所有代码全部删掉,因为我们不再需要用这个按钮来触发事件了,我们改用一个滑块类 Slider来触发事件,因此我们完全可以抛弃这个按钮了。
我们添加一个 Slider 变量:
在 UiScreen.java 的 show() 函数中对它进行初始化,我们根据构造函数的需要传入相应的参数,其中我们用到了 libgdx 中的一个类, NinePatch,相应的使用可以参见巴士内帖子:
android游戏开发框架libgdx的使用(五)--舞台和常用UI类
http://www.apkbus.com/android-19751-1-1.html
其实我们这里只是简单构造一个NinePatch,所以NinePatch如何使用其实也不用深究了。
这里的Ninepatch 的构造函数里面的参数在这里是可以随便设定的,因为我们从 Slider 构造函数中需要的 SliderStyle 可以看出:
第一个NinePatch中的图片是底座的图片,而我前面做好的底座的图片其实是一张背景为透明的空图片,所以参数可以随便设定没有任何影响,而第二个参数TextrureRegion是滑块的图片。由此我们可以构造以下语句:
- NinePatch n1 = new NinePatch(new Texture(Gdx.files.internal("pla.png")), 14, 14, 18, 18);
- bar=new Texture(Gdx.files.internal("btn_blue.png"));
- slider=new Slider(0,min,1f,new SliderStyle(n1, new TextureRegion(new Texture(Gdx.files.internal("btn_blue.png")), 0, 0, 100, 96)));
复制代码
这里再顺便解释一下
Slider
构造函数
中的前三个参数的意思。
官方文档中是这样说的:
“Themin and max values determine the range the values of this slider can take on,the steps parameter specifies the distance between individual values. E.g. mincould be 4, max could be 10 and steps could be 0.2, giving you a total of 30values, 4.0 4.2, 4.4 and so on.”
也就是说,min和max值分别代表了这个Slider可以代表的最大值和最小值,而一个Slider最左端的值就是min,最右端的值就是max,至于第三个参数也很好理解,step就是步长,比如step是1,min是10,max是20,那么滑块根据滑动的不同距离就可以取到,10,11,12,13…20等等,从这里我们也可以看出,真正决定滑块每滑动一步在屏幕上移动的距离是和(max-min)和step两个值有关的,step越大,每动一下滑块在屏幕上移动的距离就越远,从某种角度上来说滑动就显得生涩。
我们现在还没有为Slider添加监听事件,但的的确确这个slider已经构造好了,当然我们还要手动设置一下Slider的长度,在官方文档下有这么一句话:
“Thepreferred width of a slider is 140”
也就是说,默认的Slider长度是140,为了确认这一点,我们先来运行一下,当然运行之前还要把这个Slider加入舞台才能正确显示:
我们看到,这个时候我其实已经将滑块滑到底了,但是我们又可以清楚地根据粒子系统的轨迹看出我的手已经滑过了很远的距离,可是滑块依然不再前进,因为它已经滑到底了。另外,我们当前使用的测试机的分辨率是800*480的,即宽就是480,我们对比一下140所在的位置,发现确实差不多在140的位置滑块就滑到头了,为了使滑块横跨整个屏幕,我们添加如下的一条语句:
- //把滑块的长度设为宽度。
- slider.width=min;
复制代码
再运行一下看看效果:
大家可以看到,这一次是滑动到头了。
但是我们滑动到头以后并没有添加触发事件,因此我们滑动到头以后也不会有任何变化。
我们再查看一下API文档:
我们发现可以给Slider设置一个监听器,监听Slider。
在Slider构造完成后添加监听器:
- slider.setValueChangedListener(new ValueChangedListener() {
- @Override
- public void changed(Slider arg0, float arg1) {
- // TODO Auto-generated method stub
- if(!sliderFinished&&(int)arg1==arg0.width){
- sliderFinished=true;
- activity.tg.setScreen(activity.gs);
- }
- }
- });
复制代码
其中我们在UiScreen.java中添加了一个boolean变量sliderFinished,用于标记,作用很显然,就是为了防止重复触发事件,因为在实际操作用,我们手在滑动滑块时很可能在将滑块划到底时手不立即松开,那么就会触发多次事件,这是我们不想看到的,因此,我们做一个标记,保证setScreen的事件只触发一次。
现在程序中的Slider就添加完毕了,也可以正常使用了,但是我们发现,滑块只划到一半的时候,并不会回到原位,而是停留在该位置,不会自动复位。那么我们就要认为给它复位了。
我们查看Slider的官方文档,发现有如下方法:
这两个方法可以判断当前Slider是否正在被拖拽或者被按下。我们在UiScreen.java里面处理这些事件,用来实现复位的功能。
我们只要在UiScreen.java的render()函数中加上这么一段代码:
- if(!slider.isDragging()&&slider.getValue()>10){
- slider.setValue(slider.getValue()-10);
- }
复制代码
表示在没有拖拽的情况下滑块缓慢复位。
运行一下,效果正常,在滑块拖动到头的时候进入游戏界面。
这一讲我们只修改了UiScreen.java这一个源文件,因此我们把这次修改后的完整代码贴出来,其中还有一些小修改,在程序中都做了注释:
修改后的UiScreen.java完整代码:
- public class UiScreen implements Screen {
- Texture texture;
- TextureRegion background;
- boolean hasini;
- SpriteBatch batch;
- Stage stage;
- Slider slider;
- int width;
- int height;
- //边长的最大值和最小值
- int max;
- int min;
- Texture tx1;
- Texture tx2;
- Texture tx3;
- Texture bar;
- UiActivity activity;
- ParticleEffect particle;
- ParticleEffect tem;
- ParticleEffectPool particlepool;
- ArrayList particlelist;
- boolean sliderFinished;
- public UiScreen(UiActivity activity){
- super();
- this.activity=activity;
- // TODO Auto-generated constructor stub
- }
- @Override
- public void dispose() {
- // TODO Auto-generated method stub
- }
- @Override
- public void hide() {
- // TODO Auto-generated method stub
- }
- @Override
- public void pause() {
- // TODO Auto-generated method stub
- }
- @Override
- public void render(float arg0) {
- // TODO Auto-generated method stub
- Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
- Gdx.gl.glClearColor(0f,0f,0f,0f);
- batch.begin();
- batch.draw(background,0,0,min,max);
- batch.end();
- if(!slider.isDragging()&&slider.getValue()>10){
- slider.setValue(slider.getValue()-10);
- }
- if(Gdx.input.isTouched()){
- //当此触摸点与上一触摸点距离大于一定值的时候触发新的粒子系统,由此减小系统负担
- tem=particlepool.obtain();
- tem.setPosition(Gdx.input.getX(),Gdx.graphics.getHeight()-Gdx.input.getY());
- particlelist.add(tem);
- }
- batch.begin();
- for(int i=0;i
- particlelist.get(i).draw(batch, Gdx.graphics.getDeltaTime());
- }
- batch.end();
- //清除已经播放完成的粒子系统
- ParticleEffect temparticle;
- for(int i=0;i
- temparticle=particlelist.get(i);
- if(temparticle.isComplete()){
- particlelist.remove(i);
- }
- }
- //舞台绘制要在背景绘制之后,不然背景会覆盖在按钮表面,我们就看不到按钮了
- stage.act(Gdx.graphics.getDeltaTime());
- stage.draw();
- }
- @Override
- public void resize(int arg0, int arg1) {
- // TODO Auto-generated method stub
- }
- @Override
- public void resume() {
- // TODO Auto-generated method stub
- }
- @Override
- public void show() {
- // TODO Auto-generated method stub
- //做一个简单的适配。这里解释一下为什么不直接令max=height,原因在于有时候我们从锁屏回到游戏,
- //从横屏切换到竖屏或者从竖屏切换到横屏的时候,libGdx有时候会来不及切换,也就是说我们可能
- //getWidth得到的是实际的height值,getHeight得到的是实际的width值,所以这里增加一个长宽哪一个
- //更大的语句,这样就不会出错了
- width=Gdx.graphics.getWidth();
- height=Gdx.graphics.getHeight();
- max=width>height?width:height;
- min=width>height?height:width;
- //再做一个简单的适配
- if(max>=320&&max<480)
- max=320;
- if(max>=480&&max<800)
- max=480;
- if(max>=800)
- max=800;
- //将滑块标记重新初始化
- sliderFinished=false;
- if(!hasini){
- batch=new SpriteBatch();
- stage=new Stage(min,max,true);
- particle = new ParticleEffect();
- particle.load(Gdx.files.internal("particle.p"), Gdx.files.internal(""));
- particlepool=new ParticleEffectPool(particle, 5, 10);
- particlelist=new ArrayList();
- texture=new Texture(Gdx.files.internal("background"+max+".jpg"));
- //重点在这条语句,我们只取了texture的一部分,红色的多余部分我们没有取
- background=new TextureRegion(texture, 0, 0, min, max);
- NinePatch n1 = new NinePatch(new Texture(Gdx.files.internal("pla.png")), 14, 14, 18, 18);
- bar=new Texture(Gdx.files.internal("btn_blue.png"));
- slider=new Slider(0,min,1f,new SliderStyle(n1, new TextureRegion(new Texture(Gdx.files.internal("btn_blue.png")), 0, 0, 100, 96)));
- slider.setValueChangedListener(new ValueChangedListener() {
- @Override
- public void changed(Slider arg0, float arg1) {
- // TODO Auto-generated method stub
- if(!sliderFinished&&(int)arg1==arg0.width){
- sliderFinished=true;
- activity.tg.setScreen(activity.gs);
- }
- }
- });
- //把滑块的长度设为宽度。
- slider.width=min;
- stage.addActor(slider);
- hasini=true;
- }
- //这句话是必须的,而且在if(hasini)之外,无论资源是否加载完成,每次显示的时候我们将当前屏幕
- //设置能够接受用户输入
- Gdx.input.setInputProcessor(stage);
- }
- }
复制代码
这一讲我们介绍了libgdx中Slider控件的用法。下面几讲我们将继续完善程序,如存档功能等等。