java實現(xiàn)俄羅斯方塊小游戲
本文實例為大家分享了java實現(xiàn)俄羅斯方塊的具體代碼,供大家參考,具體內(nèi)容如下
使用一個二維數(shù)組保存游戲的地圖:
// 游戲地圖格子,每個格子保存一個方塊,數(shù)組紀(jì)錄方塊的狀態(tài) private State map[][] = new State[rows][columns];
游戲前先將所有地圖中的格子初始化為空:
/* 初始化所有的方塊為空 */ for (int i = 0; i < map.length; i++) { for (int j = 0; j < map[i].length; j++) { map[i][j] = State.EMPTY; } }
玩游戲過程中,我們能夠看到界面上的方塊,那么就得將地圖中所有的方塊繪制出來,當(dāng)然,除了需要繪制方塊外,游戲積分和游戲結(jié)束的字符串在必要的時候也需要繪制:
/** * 繪制窗體內(nèi)容,包括游戲方塊,游戲積分或結(jié)束字符串 */ @Override public void paint(Graphics g) { super.paint(g); for (int i = 0; i < rows; i++) { for (int j = 0; j < columns; j++) { if (map[i][j] == State.ACTIVE) { // 繪制活動塊 g.setColor(activeColor); g.fillRoundRect(j * BLOCK_SIZE, i * BLOCK_SIZE + 25, BLOCK_SIZE - 1, BLOCK_SIZE - 1, BLOCK_SIZE / 5, BLOCK_SIZE / 5); } else if (map[i][j] == State.STOPED) { // 繪制靜止塊 g.setColor(stopedColor); g.fillRoundRect(j * BLOCK_SIZE, i * BLOCK_SIZE + 25, BLOCK_SIZE - 1, BLOCK_SIZE - 1, BLOCK_SIZE / 5, BLOCK_SIZE / 5); } } } /* 打印得分 */ g.setColor(scoreColor); g.setFont(new Font("Times New Roman", Font.BOLD, 30)); g.drawString("SCORE : " + totalScore, 5, 70); // 游戲結(jié)束,打印結(jié)束字符串 if (!isGoingOn) { g.setColor(Color.RED); g.setFont(new Font("Times New Roman", Font.BOLD, 40)); g.drawString("GAME OVER !", this.getWidth() / 2 - 140, this.getHeight() / 2); } }
通過隨機數(shù)的方式產(chǎn)生方塊所組成的幾種圖形,一般七種圖形:條形、田形、正7形、反7形、T形、Z形和反Z形,如生成條形:
map[0][randPos] = map[0][randPos - 1] = map[0][randPos + 1] = map[0][randPos + 2] = State.ACTIVE;
生成圖形后,實現(xiàn)下落的操作。如果遇到阻礙,則不能再繼續(xù)下落:
isFall = true; // 是否能夠下落 // 從當(dāng)前行檢查,如果遇到阻礙,則停止下落 for (int i = 0; i < blockRows; i++) { for (int j = 0; j < columns; j++) { // 遍歷到行中塊為活動塊,而下一行塊為靜止塊,則遇到阻礙 if (map[rowIndex - i][j] == State.ACTIVE && map[rowIndex - i + 1][j] == State.STOPED) { isFall = false; // 停止下落 break; } } if (!isFall) break; }
如果未遇到阻礙,則下落的時候,方塊圖形整體向下移動一行:
// 圖形下落一行 for (int i = 0; i < blockRows; i++) { for (int j = 0; j < columns; j++) { if (map[rowIndex - i][j] == State.ACTIVE) { // 活動塊向下移動一行 map[rowIndex - i][j] = State.EMPTY; // 原活動塊變成空塊 map[rowIndex - i + 1][j] = State.ACTIVE; // 下一行塊變成活動塊 } } }
向左、向右方向移動時是類似的操作:
/** * 向左走 */ private void left() { // 標(biāo)記左邊是否有阻礙 boolean hasBlock = false; /* 判斷是否左邊有阻礙 */ for (int i = 0; i < blockRows; i++) { if (map[rowIndex - i][0] == State.ACTIVE) { // 判斷左邊是否為墻 hasBlock = true; break; // 有阻礙,不用再循環(huán)判斷行 } else { for (int j = 1; j < columns; j++) { // 判斷左邊是否有其它塊 if (map[rowIndex - i][j] == State.ACTIVE && map[rowIndex - i][j - 1] == State.STOPED) { hasBlock = true; break; // 有阻礙,不用再循環(huán)判斷列 } } if (hasBlock) break; // 有阻礙,不用再循環(huán)判斷行 } } /* 左邊沒有阻礙,則將圖形向左移動一個塊的距離 */ if (!hasBlock) { for (int i = 0; i < blockRows; i++) { for (int j = 1; j < columns; j++) { if (map[rowIndex - i][j] == State.ACTIVE) { map[rowIndex - i][j] = State.EMPTY; map[rowIndex - i][j - 1] = State.ACTIVE; } } } // 重繪 repaint(); } }
向下加速移動時,就是減小每次正常狀態(tài)下落的時間間隔:
/** * 向下直走 */ private void down() { // 標(biāo)記可以加速下落 immediate = true; }
如何變換圖形方向,這里僅使用了非常簡單的方法來實現(xiàn)方向變換,當(dāng)然可以有更優(yōu)的算法實現(xiàn)方向變換操作,大家可以自己研究:
/** * 旋轉(zhuǎn)方塊圖形 */ private void rotate() { try { if (shape == 4) { // 方形,旋轉(zhuǎn)前后是同一個形狀 return; } else if (shape == 0) { // 條狀 // 臨時數(shù)組,放置旋轉(zhuǎn)后圖形 State[][] tmp = new State[4][4]; int startColumn = 0; // 找到圖形開始的第一個方塊位置 for (int i = 0; i < columns; i++) { if (map[rowIndex][i] == State.ACTIVE) { startColumn = i; break; } } // 查找旋轉(zhuǎn)之后是否有阻礙,如果有阻礙,則不旋轉(zhuǎn) for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if (map[rowIndex - 3 + i][j + startColumn] == State.STOPED) { return; } } } if (map[rowIndex][startColumn + 1] == State.ACTIVE) { // 橫向條形,變換為豎立條形 for (int i = 0; i < 4; i++) { tmp[i][0] = State.ACTIVE; for (int j = 1; j < 4; j++) { tmp[i][j] = State.EMPTY; } } blockRows = 4; } else { // 豎立條形,變換為橫向條形 for (int j = 0; j < 4; j++) { tmp[3][j] = State.ACTIVE; for (int i = 0; i < 3; i++) { tmp[i][j] = State.EMPTY; } } blockRows = 1; } // 將原地圖中圖形修改為變換后圖形 for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { map[rowIndex - 3 + i][startColumn + j] = tmp[i][j]; } } } else { // 臨時數(shù)組,放置旋轉(zhuǎn)后圖形 State[][] tmp = new State[3][3]; int startColumn = columns; // 找到圖形開始的第一個方塊位置 for (int j = 0; j < 3; j++) { for (int i = 0; i < columns; i++) { if (map[rowIndex - j][i] == State.ACTIVE) { startColumn = i < startColumn ? i : startColumn; } } } // 判斷變換后是否會遇到阻礙 for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (map[rowIndex - 2 + j][startColumn + 2 - i] == State.STOPED) return; } } // 變換 for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { tmp[2 - j][i] = map[rowIndex - 2 + i][startColumn + j]; } } // 將原地圖中圖形修改為變換后圖形 for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { map[rowIndex - 2 + i][startColumn + j] = tmp[i][j]; } } // 重繪 repaint(); // 重新修改行指針 for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (map[rowIndex - i][startColumn + j] != null || map[rowIndex - i][startColumn + j] != State.EMPTY) { rowIndex = rowIndex - i; blockRows = 3; return; } } } } } catch (Exception e) { // 遇到數(shù)組下標(biāo)越界,說明不能變換圖形形狀,不作任何處理 } }
當(dāng)圖形下落遇到阻礙時停止,我們就需要判斷這時是否有某一行或幾行可以消除掉,這時可以先獲取每行中方塊的個數(shù),然后再進行判斷:
int[] blocksCount = new int[rows]; // 記錄每行有方塊的列數(shù) int eliminateRows = 0; // 消除的行數(shù) /* 計算每行方塊數(shù)量 */ for (int i = 0; i < rows; i++) { blocksCount[i] = 0; for (int j = 0; j < columns; j++) { if (map[i][j] == State.STOPED) blocksCount[i]++; } }
如果有滿行的方塊,則消除掉該行方塊:
/* 實現(xiàn)有滿行的方塊消除操作 */ for (int i = 0; i < rows; i++) { if (blocksCount[i] == columns) { // 清除一行 for (int m = i; m >= 0; m--) { for (int n = 0; n < columns; n++) { map[m][n] = (m == 0) ? State.EMPTY : map[m - 1][n]; } } eliminateRows++; // 記錄消除行數(shù) } }
最后我們再重繪顯示積分就可以了。
重復(fù)以上的生成圖形、圖形下落、左右下移動、判斷消除行的操作,一個簡單的俄羅斯方塊就完成了。
運行效果:
完整示例代碼:俄羅斯方塊
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
基于SpringBoot核心原理(自動配置、事件驅(qū)動、Condition)
這篇文章主要介紹了基于SpringBoot核心原理(自動配置、事件驅(qū)動、Condition),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08MyBatis異常java.sql.SQLSyntaxErrorException的問題解決
使用mybatis插入數(shù)據(jù)時出現(xiàn)java.sql.SQLSyntaxErrorException異常,本文就來介紹一下MyBatis異常的問題解決,具有一定的參考價值,感興趣的可以了解一下2023-08-08關(guān)于@ComponentScan?TypeFilter自定義指定掃描bean的規(guī)則
這篇文章主要介紹了關(guān)于@ComponentScan?TypeFilter自定義指定掃描bean的規(guī)則,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-09-09