国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

一款適用于Android平臺的俄羅斯方塊

2019-10-21 21:49:37
字體:
供稿:網(wǎng)友

俄羅斯方塊Tetris是一款很經(jīng)典的益智游戲,之前就做了一款桌面版的java俄羅斯方塊,這次就嘗試著寫了一款適用于Android平臺的俄羅斯方塊。

整個程序設(shè)計十分簡潔,只是運用了兩個類而已,最終做出的效果圖如下所示:

Android,俄羅斯方塊

首先,要考慮的自然是游戲應(yīng)該如何布局的問題了。我的想法是將手機(jī)屏幕分為上下兩部分,上邊用來顯示游戲者的名稱、所得分?jǐn)?shù)以及下一個方塊,稱為“文字區(qū)域”,下邊自然就是游戲區(qū)域了。 
如圖所示:

Android,俄羅斯方塊

布局文件如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/linear" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/three" android:orientation="vertical" android:padding="25px" > <TextView android:id="@+id/text1" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/hh"/> <TextView android:id="@+id/text2" android:layout_width="match_parent" android:layout_height="wrap_content" android:height="30px" /> <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content" > </FrameLayout></LinearLayout>

為了讓游戲能夠更好地適配Android眾多大小不一的屏幕,需要對布局進(jìn)行動態(tài)規(guī)劃。在xml文件中,android:padding=”25px”,text1代表的是上方的文字區(qū)域,txet1的背景是一張半透明的圖片,在運行程序時會根據(jù)手機(jī)屏幕大小動態(tài)規(guī)劃其高度。text2是文字區(qū)域以及游戲區(qū)域之間的間距,我將它的高度定為固定值“30px”。而游戲區(qū)域的高度亦是會動態(tài)規(guī)劃的,自定義的view將會添加在FrameLayout當(dāng)中。

自定義的view組件代碼如下,用來繪制并顯示所有的方塊:

public class Brick extends View { // 要繪制的方塊的坐標(biāo)集 private boolean[][] map; //保存每個坐標(biāo)點在屏幕中的坐標(biāo) private Point[][] Points; private int PADDING = 3; //方塊的寬度 private float BRICK_WIDTH; private Paint paint = new Paint(); private RectF rectf; public Brick(Context context, boolean[][] map, float BRICK_WIDTH) { super(context); this.map = map; this.BRICK_WIDTH = BRICK_WIDTH; setBackgroundResource(R.drawable.one); } public void onDraw(Canvas canvas) { super.onDraw(canvas); Points = new Point[10][15]; for (int j = 0; j < 15; j++) {  for (int i = 0; i < 10; i++) {  Points[i][j] = new Point(i * (int) BRICK_WIDTH, j* (int) BRICK_WIDTH);  } } for (int j = 0; j < 15; j++) {  for (int i = 0; i < 10; i++) {  if (map[i][j]) {    paint.setColor(Color.parseColor("#f7faf3"));   float x = Points[i][j].x;   float y = Points[i][j].y;   rectf = new RectF(x, y, x + BRICK_WIDTH, y + BRICK_WIDTH);   canvas.drawRect(rectf, paint);  paint.setColor(Color.parseColor("#4c8e0b"));  rectf = new RectF(x + PADDING, y + PADDING, x + BRICK_WIDTH- PADDING, y + BRICK_WIDTH - PADDING);  canvas.drawRect(rectf, paint);  }  } } }}

當(dāng)中,map是一個boolean類型的二維數(shù)組,因為我將游戲區(qū)域的比例設(shè)為10乘15,所以map的大小即為map[10][15],map[0][0]即為游戲區(qū)域的左上角,map[9][14]為游戲區(qū)域的右下角。如果方塊落在了某個坐標(biāo)點,則該坐標(biāo)值設(shè)為true,否則為false。則當(dāng)方塊不斷下落時,通過計算方塊的新的坐標(biāo)點并重新構(gòu)建新的map,即可獲得新的view對象。 
BRICK_WIDTH為每個方塊的寬度,在構(gòu)造函數(shù)中獲得。 
因此,如果new一個Brick對象,且map的值均設(shè)為true,將之添加到FrameLayout當(dāng)中,即可獲得如下效果:

Android,俄羅斯方塊

在Activity類中,通過如下代碼可獲得屏幕信息:

//獲取屏幕的寬度和高度DisplayMetrics metric = new DisplayMetrics();getWindowManager().getDefaultDisplay().getMetrics(metric);SCREEN_WIDTH = metric.widthPixels;SCREEN_HIGHT = metric.heightPixels;

SCREEN_WIDTH 是屏幕寬度,SCREEN_HIGHT 是屏幕高度,則SCREEN_WIDTH 減去兩倍PADDING,再除以十后,就可以得到方塊的寬度BRICK_WIDTH,而BRICK_WIDTH乘以十五后,即游戲區(qū)域的高度了,這樣就可以算出文字區(qū)域的高度了

BRICK_WIDTH = (SCREEN_WIDTH - 2 * PADDING) / 10;GAME_HIGHT = 15 * BRICK_WIDTH;TEXT_HIGHT = SCREEN_HIGHT - 2 * PADDING - 30 - GAME_HIGHT;text = (TextView) findViewById(R.id.text1);frame = (FrameLayout) findViewById(R.id.frame);text.setHeight((int) TEXT_HIGHT);

下落方法的基本形狀有如下6種,每個方塊下落時的初始坐標(biāo)點亦如下所示:

Android,俄羅斯方塊

如正方形方塊有四個點,坐標(biāo)分別為(4,0)(5,0)(4,1)(5,1)。用List< Point[] >類型的listPoints來保存坐標(biāo)集合。注意:每個下落方塊的第一個坐標(biāo)點均是有特殊作用的,這個后邊會說到。

private static List<Point[]> listPoints; static { listPoints = new ArrayList<Point[]>(6); listPoints.add(new Point[] { new Point(4, 0), new Point(5, 0),  new Point(4, 1), new Point(5, 1) }); listPoints.add(new Point[] { new Point(4, 1), new Point(4, 0),  new Point(4, 2), new Point(4, 3) }); listPoints.add(new Point[] { new Point(4, 1), new Point(5, 0),  new Point(4, 0), new Point(4, 2) }); listPoints.add(new Point[] { new Point(5, 1), new Point(5, 0),  new Point(4, 0), new Point(5, 2) }); listPoints.add(new Point[] { new Point(5, 1), new Point(5, 0),  new Point(4, 1), new Point(5, 2) }); listPoints.add(new Point[] { new Point(4, 1), new Point(4, 0),  new Point(5, 1), new Point(5, 2) }); }

在程序中,我的想法是new兩個map對象,map1用來保存所有固定不動的方塊坐標(biāo)點,map2用來保存還在下落的方塊的坐標(biāo)點,這樣就能夠new兩個Brick對象,然后通過覆蓋的方法來使之同時顯示在同個區(qū)域內(nèi)。這也是我將它們添加到FrameLayout布局的原因。

下落方塊的移動算法如下,適用于左移還有右移

//移動 public void move(int moveX, int moveY) { for (int i = 0; i < point.length; i++) {  int newX = point[i].x + moveX;  int newY = point[i].y + moveY;  if (newX < 0 || newX > 9 || newY > 14 || map1[newX][newY]) {  return;  } } for (int k = 0; k < 15; k++) {  for (int t = 0; t < 10; t++) {  map2[t][k] = false;  } } for (int j = 0; j < point.length; j++) {  point[j].x = point[j].x + moveX;  point[j].y = point[j].y + moveY; } for (int j = 0; j < point.length; j++) {  int x = point[j].x;  int y = point[j].y;  map2[x][y] = true; } frame.removeView(brick2); brick2 = new Brick(this, map2, BRICK_WIDTH); frame.addView(brick2); }

則要左移和右移時只要分別為move(int moveX, int moveY)函數(shù)傳入不同參數(shù)即可實現(xiàn)對應(yīng)操作:

 // 左移 public void leftBrick(View view) { move(-1, 0); } // 右移 public void rightBrick(View view) { move(1, 0); }

變形操作我在我的另一篇博文中也寫到過:用Java寫俄羅斯方塊,需要下落方塊有一個固定的旋轉(zhuǎn)點,這個旋轉(zhuǎn)點我設(shè)為下落方塊的第一個坐標(biāo)點,這也是我前邊所說的第一個坐標(biāo)點的特殊作用。

// 變形 public void changeBrick(View view) { if (point[0].x + 1 == point[1].x && point[2].x + 1 == point[3].x  && point[0].y + 1 == point[2].y) {  return; } for (int i = 0; i < point.length; i++) {  int newX = point[0].y + point[0].x - point[i].y;  int newY = point[0].y - point[0].x + point[i].x;  if (newX < 0 || newX > 9 || newY > 14 || map1[newX][newY]) {  return;  } } for (int i = 0; i < point.length; i++) {  int newX = point[0].y + point[0].x - point[i].y;  int newY = point[0].y - point[0].x + point[i].x;  point[i].x = newX;  point[i].y = newY; } for (int k = 0; k < 15; k++) {  for (int t = 0; t < 10; t++) {  map2[t][k] = false;  } } for (int j = 0; j < point.length; j++) {  int x = point[j].x;  int y = point[j].y;  map2[x][y] = true; } frame.removeView(brick2); brick2 = new Brick(this, map2, BRICK_WIDTH); frame.addView(brick2); }

消行操作需要在每次下落方塊無法再下落時檢查是否需要實行,所以檢查是從第十五行開始直到第一行:

// 消行 public void MoveLine() { for (int j = 14; j >= 0; j--) {  int n = 0;  for (int i = 0; i < 10; i++) {  if (map1[i][j]) {   n++;  }  }  if (n == 10) {  for (int k = j; k > 0; k--) {   for (int i = 0; i < 10; i++) {   map1[i][k] = map1[i][k - 1];   }  }  j = j + 1;  } } }

注意當(dāng)中的j = j + 1語句,因為當(dāng)?shù)趈行消行后上方區(qū)域需要整個“下沉”一行,所以原來的第j-1行就變成了第j行,所以還需要再從現(xiàn)在的第j行檢查起,加一的原因是在for循環(huán)中j會減一,所以這里先加一。

下移操作是整個程序設(shè)計中的難點,在這里面要實現(xiàn)消行檢查,map1和map2的數(shù)據(jù)更新,以便刷新新的界面,這里代碼就不再貼出了。

此外,我原本是打算用手勢操作來控制方塊的移動的,可因為根據(jù)手指滑動來判斷方向會有很大誤差,所以我最終還是采用Button來實現(xiàn)控制操作,可以看到效果圖當(dāng)中有三個不同形狀的圖片,分別對應(yīng)左移,變形和右移,且該三個Button組件是在Brick之后添加到布局文件當(dāng)中的,這樣才能使按鈕圖片是覆蓋在方塊表面。

源代碼下載:Android版俄羅斯方塊

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持VEVB武林網(wǎng)。


注:相關(guān)教程知識閱讀請移步到Android開發(fā)頻道。
發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 巴塘县| 芮城县| 奎屯市| 福鼎市| 广平县| 乾安县| 灵璧县| 县级市| 天祝| 乐东| 内乡县| 宁晋县| 江西省| 宜都市| 昌黎县| 阿拉善右旗| 安图县| 广河县| 鹤山市| 碌曲县| 拉萨市| 麦盖提县| 宜都市| 苏州市| 萍乡市| 天台县| 海门市| 江北区| 陵川县| 贞丰县| 嘉定区| 延安市| 泰顺县| 屏山县| 晋宁县| 凤翔县| 江陵县| 曲麻莱县| 阿图什市| 阳信县| 新疆|