您好,登錄后才能下訂單哦!
俄羅斯方塊Tetris是一款很經典的益智游戲,之前就做了一款桌面版的java俄羅斯方塊,這次就嘗試著寫了一款適用于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眾多大小不一的屏幕,需要對布局進行動態規劃。在xml文件中,android:padding=”25px”,text1代表的是上方的文字區域,txet1的背景是一張半透明的圖片,在運行程序時會根據手機屏幕大小動態規劃其高度。text2是文字區域以及游戲區域之間的間距,我將它的高度定為固定值“30px”。而游戲區域的高度亦是會動態規劃的,自定義的view將會添加在FrameLayout當中。
自定義的view組件代碼如下,用來繪制并顯示所有的方塊:
public class Brick extends View { // 要繪制的方塊的坐標集 private boolean[][] map; //保存每個坐標點在屏幕中的坐標 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); } } } } }
當中,map是一個boolean類型的二維數組,因為我將游戲區域的比例設為10乘15,所以map的大小即為map[10][15],map[0][0]即為游戲區域的左上角,map[9][14]為游戲區域的右下角。如果方塊落在了某個坐標點,則該坐標值設為true,否則為false。則當方塊不斷下落時,通過計算方塊的新的坐標點并重新構建新的map,即可獲得新的view對象。
BRICK_WIDTH為每個方塊的寬度,在構造函數中獲得。
因此,如果new一個Brick對象,且map的值均設為true,將之添加到FrameLayout當中,即可獲得如下效果:
在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乘以十五后,即游戲區域的高度了,這樣就可以算出文字區域的高度了
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種,每個方塊下落時的初始坐標點亦如下所示:
如正方形方塊有四個點,坐標分別為(4,0)(5,0)(4,1)(5,1)。用List< Point[] >類型的listPoints來保存坐標集合。注意:每個下落方塊的第一個坐標點均是有特殊作用的,這個后邊會說到。
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用來保存所有固定不動的方塊坐標點,map2用來保存還在下落的方塊的坐標點,這樣就能夠new兩個Brick對象,然后通過覆蓋的方法來使之同時顯示在同個區域內。這也是我將它們添加到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)函數傳入不同參數即可實現對應操作:
// 左移 public void leftBrick(View view) { move(-1, 0); } // 右移 public void rightBrick(View view) { move(1, 0); }
變形操作我在我的另一篇博文中也寫到過:用Java寫俄羅斯方塊,需要下落方塊有一個固定的旋轉點,這個旋轉點我設為下落方塊的第一個坐標點,這也是我前邊所說的第一個坐標點的特殊作用。
// 變形 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; } } }
注意當中的j = j + 1語句,因為當第j行消行后上方區域需要整個“下沉”一行,所以原來的第j-1行就變成了第j行,所以還需要再從現在的第j行檢查起,加一的原因是在for循環中j會減一,所以這里先加一。
下移操作是整個程序設計中的難點,在這里面要實現消行檢查,map1和map2的數據更新,以便刷新新的界面,這里代碼就不再貼出了。
此外,我原本是打算用手勢操作來控制方塊的移動的,可因為根據手指滑動來判斷方向會有很大誤差,所以我最終還是采用Button來實現控制操作,可以看到效果圖當中有三個不同形狀的圖片,分別對應左移,變形和右移,且該三個Button組件是在Brick之后添加到布局文件當中的,這樣才能使按鈕圖片是覆蓋在方塊表面。
源代碼下載:Android版俄羅斯方塊
更多關于俄羅斯方塊的文章,請點擊查看專題:《俄羅斯方塊》
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。