libgdx游戏引擎教程实战篇(三)移植俄罗斯方块(附源码)

转自:http://www.apkbus.com/android-58414-1-1.html

 

我们接着实战篇的第二讲继续我们的移植工作。我们开始移植最重要的部分,游戏界面,GameScreen,我们将需要移植的工程的GameView里面的一些变量都复制过来,并做一些相应的修改,比如SDK中用来保存方块图片的Bitmap数组我们用libgdx中的Textrue来代替之类的。

其他的基本都相同,比如对数组进行操作的函数如(以下这些函数的原函数在SDK版本的GameView.java下


 

  1. public boolean checkfull()

  2. public boolean checkcollide(int x,int y,int[][]bricks,int[][]frame)

  3. public void copy()

  4. public void roll()

  5. public void toLeft()

  6. public void toRight()

  7. public void toBelow()
复制代码


 

这几个函数都不要做大的修改。直接复制过来然后少做修改即可。具体的修改大家可以看代码。还是那句话,直接对数组进行操作的函数都是对数组的值进行操作,对整个游戏来说是抽象的,无论是哪个版本我们都是拿一个二维数组代表方块,因此它们都是可以直接复制过来的。


 

至于SDK 版本的GameView.java 里,本身实现了Runnable()接口,用了一个线程来不停的产生新的方块,这个功能我们在libgdx 里面也很容易实现,直接新开一个线程实现就可以。 每次显示,即系统调用Screen的show()方法的时候都启动这个线程就可以了。



未移植前,也就是SDK版本中的GameView.java这部分用来产生新方块的代码是这样的:

  1. public void run() {
  2. // TODO Auto-generated method stub
  3. try{
  4. while(isplaying==1){
  5. if(isflying==0){
  6. switch((int) Math.round(Math.random() * 7)){
  7. case 0:brick=new COBrick((int) rand.nextInt(6));System.out.print("O!"+brick.color);break;
  8. case 1:brick=new CLBrick((int) rand.nextInt(6));System.out.print("L!"+brick.color);break;
  9. case 2:brick=new CLLBrick((int) rand.nextInt(6));System.out.print("LL!"+brick.color);break;
  10. case 3:brick=new CSBrick((int) rand.nextInt(6));System.out.print("S!"+brick.color);break;
  11. case 4:brick=new CSSBrick((int) rand.nextInt(6));System.out.print("SS!"+brick.color);break;
  12. case 5:brick=new CIBrick((int) rand.nextInt(6));System.out.print("I!"+brick.color);break;
  13. case 6:brick=new CCBrick((int) rand.nextInt(6));System.out.print("C!"+brick.color);break;
  14. }
  15. x=0;
  16. y=3;
  17. isflying=1;
  18. try{
  19. bricks=brick.checkori(brick.ori);
  20. }catch(NullPointerException e){
  21. brick=new CSSBrick((int) rand.nextInt(6));
  22. bricks=brick.checkori(brick.ori);
  23. }
  24. }
  25. toBelow();
  26. Draw();
  27. Thread.sleep(speed);
  28. }
  29. }catch (InterruptedException e){

  30. }

  31. }
复制代码

其中我加入了适当的打印代码便于发现问题的时候追踪。

相当于 我们新开了一个线程用来检测当前是否有方块在空中 (isflying) ,如果没有则新生成一个新的方块。


 

将这段代码 直接复制到 libgdx版的GameScreen.java 里面去 放在哪里呢?放在show()里面。 并新开一个线程管理。每次屏幕GameScreen显示的时候,即系统调用show()方法的时候,我们都新开一个线程, 并在该屏幕被其他屏幕替换(系统调用其hide()方法),或者我们突然锁住屏幕的时候,调用pause()方法时,我们将线程关闭(一定要关闭!!!!)


下面这段代码是加在libgdx版本GameScreen.java的show()方法里的:

  1. new Thread(new Runnable() {

  2. @Override
  3. public void run() {
  4. // TODO Auto-generated method stub
  5. try{
  6. while(isplaying==true){
  7. if(!isflying){
  8. switch((int) Math.round(Math.random() * 7)){
  9. case 0:brick=new COBrick((int) rand.nextInt(6));System.out.print("O!"+brick.color);break;
  10. case 1:brick=new CLBrick((int) rand.nextInt(6));System.out.print("L!"+brick.color);break;
  11. case 2:brick=new CLLBrick((int) rand.nextInt(6));System.out.print("LL!"+brick.color);break;
  12. case 3:brick=new CSBrick((int) rand.nextInt(6));System.out.print("S!"+brick.color);break;
  13. case 4:brick=new CSSBrick((int) rand.nextInt(6));System.out.print("SS!"+brick.color);break;
  14. case 5:brick=new CIBrick((int) rand.nextInt(6));System.out.print("I!"+brick.color);break;
  15. case 6:brick=new CCBrick((int) rand.nextInt(6));System.out.print("C!"+brick.color);break;
  16. }
  17. x=0;
  18. y=3;
  19. isflying=true;
  20. try{
  21. bricks=brick.checkori(brick.ori);
  22. }catch(NullPointerException e){
  23. brick=new CSSBrick((int) rand.nextInt(6));
  24. bricks=brick.checkori(brick.ori);
  25. }
  26. }
  27. toBelow();
  28. Thread.sleep(speed);
  29. }
  30. }catch (InterruptedException e){

  31. }
  32. }
  33. }).start();
复制代码

和原来的对照一下,基本没有任何变化。还有一个重要的问题,怎么关闭一个线程呢?很简单,我们在hide()pause()方法内令isplayingfalse就可以了,再每次show的时候重新置为true,就像下面显示的那样:

  1. @Override
  2. public void hide() {
  3. // TODO Auto-generated method stub
  4. isplaying=false;
  5. }

  6. @Override
  7. public void pause() {
  8. // TODO Auto-generated method stub
  9. isplaying=false;
  10. }
复制代码
我们在关闭的一瞬间,前面开的那个用于产生新方块的线程还在不停地运行,我们将isplaying置为false,告诉系统我们不玩了,那么线程的run()方法就跳出了while循环,然后顺序执行完,线程就自行关闭了、

继续下面的工作。

另外原来的SDK版本中的 GameView.java 里面的 Draw ()方法我们也不再需要,
我们可以看到原来的代码是将需要重绘的消息传给了 UI 线程即 ViewActivity
,然后UI线程收到这个消息调用invalidate()方法通知系统重绘画面。 而现在我们用的是libgdx游戏引擎,可以直接在 render 函数里重绘 ,我们先将原来的代码给删除
, 不保留 Draw() 方法,因为我们的 Libgdx render() 函数里会自动帮我们绘图,我们不需要手动调用绘制的方法。我们在 render() 里面添加如下代码:
  1. batch.begin();
  2. for(int i=0;i<21;i++)
  3. for(int j=0;j<10;j++)
  4. //根据屏幕大小做了一个简单的适配,只适配了QVGA,HVGA,WVGA
  5. if(temframe[i][j]!=0){
  6. if(max>=320&&max<480){
  7. batch.draw(image[temframe[i][j]-1], (float)(50+14*j), max-(float)(25+14*i),14,14);
  8. }
  9. if(max>=480&&max<800){
  10. batch.draw(image[temframe[i][j]-1], (float)(80+16*j), max- (float)(70+16*i),16,16);
  11. }
  12. if(max>=800){
  13. batch.draw(image[temframe[i][j]-1], (float)(100+28*j),max-(float)(120+28*i),28,28);
  14. }
  15. }
  16. //结束时要调用end()方法
复制代码


这里又有一点要再次强调!!!!!!libgdx中的坐标和SDK中是不一样的!!!SDK中以左上角为原点,libgdx的是笛卡尔坐标系,以左下角为原点!!!上面这段代码是从SDK版本的ViewActivity的OnDraw()函数中移植过来的,大家可以对照一下看看有什么不同的地方。



另外,我们还要把SDK版本中的ViewActivity.java里面除了上面用到的OnDraw()方法外的一些方法也复制过来,因为我们现在用一个GameScreen直接作为一个界面,因此我们实际是在一个GameScreen里面做了GameView.javaViewActivity两部分的工作,因此我们也需要将ViewActivity里面的一些方法给移植进去。save()read()savedata()等方法,当然都要做少量的调整。由于read()方法中需要取到当前设备上下文Context,而GameScreen中是无法取得的,我们就可以修改GameScreen的构造函数传入UiActivity来获得这个Context修改后的GameScreen构造函数:

  1. public GameScreen(UiActivity activity) {
  2. super();
  3. this.activity=activity;
  4. // TODO Auto-generated constructor stub
  5. }
复制代码

其中需要的用来保存UiActivity引用的变量activity我们自行添加。

上面提到的那些方法如toLeft()等方法,根据libgdx的要求修改过后的方法如下(这些修改后的代码都放在GameScreen.java里面哦), 很多重点和解释都在注释里面,想继续往下进行移植工作的童鞋们还是仔细看看吧
  1. //空中方块向左移动
  2. public void toLeft(){
  3. if(checkcollide(x,((int)(y-1)),bricks,frame)){
  4. System.out.print("Left");
  5. y-=1;
  6. copy();
  7. Draw();
  8. }
  9. }
  10. //向右移动
  11. public void toRight(){
  12. if(checkcollide(x,((int)(y+1)),bricks,frame)){
  13. y+=1;
  14. copy();
  15. Draw();
  16. }

  17. }
  18. //向下移动
  19. public void toBelow(){
  20. System.out.println("In below!");
  21. try{
  22. if(bricks!=null){
  23. if(checkcollide(((int)(x+1)),y,bricks,frame)){
  24. x+=1;
  25. copy();
  26. Draw();
  27. }
  28. else {
  29. if(checkfull()){
  30. UiActivity.gameover=true;
  31. }
  32. for(int i=0;i<4;i++)
  33. for(int j=0;j<4;j++){
  34. if(bricks[i][j]!=0)
  35. frame[x+i][y+j]=bricks[i][j];
  36. }
  37. isflying=false;
  38. bricks=null;
  39. for(int i=0;i<21;i++){
  40. int flag=1;
  41. for(int j=0;j<10;j++){
  42. if(frame[i][j]==0)
  43. flag=0;
  44. }
  45. if(flag==1){
  46. for(int k=i;k>0;k--)
  47. frame[k]=frame[k-1].clone();
  48. for(int p=0;p<10;p++)
  49. frame[0][p]=0;
  50. score+=100;
  51. }
  52. }
  53. }
  54. }
  55. }catch(NullPointerException e){
  56. e.printStackTrace();
  57. }
  58. }
  59. //旋转
  60. public void roll(){
  61. if(checkcollide(x,y,brick.checkori((int)(brick.ori+1)),frame)){
  62. brick.ori+=1;
  63. bricks=brick.checkori(brick.ori);
  64. copy();
  65. Draw();
  66. }

  67. }
  68. //更新temframe里的内容,用于实时绘图
  69. public void copy(){
  70. for(int k=0;k<21;k++)
  71. temframe[k]=frame[k].clone();
  72. for(int i=0;i<4;i++)
  73. for(int j=0;j<4;j++)
  74. if(bricks[i][j]!=0&&temframe[x+i][y+j]==0)
  75. temframe[x+i][y+j]=bricks[i][j];
  76. }
  77. public boolean checkcollide(int x,int y,int[][]bricks,int[][]frame){
  78. if(bricks!=null)
  79. {
  80. int max_x=0,min_x=3,max_y=0,min_y=3;
  81. boolean collide=true;
  82. for(int i=0;i<4;i++)
  83. for(int j=0;j<4;j++)
  84. if(bricks[i][j]!=0){
  85. if(j<min_y)
  86. min_y=j;
  87. if(j>max_y)
  88. max_y=j;
  89. if(i>max_x)
  90. max_x=i;
  91. if(i<min_x)
  92. min_x=i;
  93. }
  94. if((max_x+x)>20||(max_y+y)>10||y+min_y<0)
  95. collide=false;
  96. try{
  97. int i;
  98. int j;
  99. for(i=0;i<=max_x;i++)
  100. for(j=min_y;j<=max_y;j++)
  101. if(bricks[i][j]!=0&&frame[x+i][y+j]!=0){
  102. collide=false;
  103. }
  104. }catch(ArrayIndexOutOfBoundsException e){
  105. return false;
  106. }
  107. return collide;
  108. }
  109. return false;
  110. }

  111. //判断是否方块到顶,游戏结束
  112. public boolean checkfull(){
  113. for(int i=0;i<21;i++)
  114. for(int j=0;j<10;j++){
  115. if(temframe[i][j]!=frame[i][j])
  116. return false;
  117. }
  118. return true;
  119. }

  120. public void save(String fileName, String fileContent) throws Exception {
  121. FileOutputStream fileOutputStream = activity.openFileOutput(
  122. fileName, Context.MODE_PRIVATE);
  123. fileOutputStream.write(fileContent.getBytes());
  124. }
  125. public String read(String fileName) throws Exception {
  126. FileInputStream fileInputStream = activity.openFileInput(fileName);
  127. ByteArrayOutputStream byteArray = new ByteArrayOutputStream();

  128. byte[] buffer = new byte[1024];
  129. int len = 0;
  130. while ((len = fileInputStream.read(buffer)) > 0) {
  131. byteArray.write(buffer, 0, len);
  132. };
  133. return byteArray.toString();
  134. }
  135. public void savedata(){
  136. String framedata="";
  137. for(int i=0;i<21;i++)
  138. for(int j=0;j<10;j++){
  139. framedata+=Integer.toString(frame[i][j])+",";
  140. }
  141. framedata+=Integer.toString(score);
  142. try {
  143. save("Tetris_slide.txt",framedata);
  144. } catch (Exception e) {
  145. // TODO Auto-generated catch block
  146. e.printStackTrace();
  147. }
  148. }
复制代码
不过我们还是没有做一个工作,就是捕捉触摸事件。可是我又要说一句招人讨厌的话了...本篇篇幅过长...放在下一讲吧=。=

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值