java實(shí)現(xiàn)俄羅斯方塊小游戲
本文實(shí)例為大家分享了java實(shí)現(xiàn)俄羅斯方塊的具體代碼,供大家參考,具體內(nèi)容如下
使用一個(gè)二維數(shù)組保存游戲的地圖:
// 游戲地圖格子,每個(gè)格子保存一個(gè)方塊,數(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é)束的字符串在必要的時(shí)候也需要繪制:
/** * 繪制窗體內(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) { // 繪制活動(dòng)塊 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); } }
通過隨機(jī)數(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;
生成圖形后,實(shí)現(xiàn)下落的操作。如果遇到阻礙,則不能再繼續(xù)下落:
isFall = true; // 是否能夠下落 // 從當(dāng)前行檢查,如果遇到阻礙,則停止下落 for (int i = 0; i < blockRows; i++) { for (int j = 0; j < columns; j++) { // 遍歷到行中塊為活動(dòng)塊,而下一行塊為靜止塊,則遇到阻礙 if (map[rowIndex - i][j] == State.ACTIVE && map[rowIndex - i + 1][j] == State.STOPED) { isFall = false; // 停止下落 break; } } if (!isFall) break; }
如果未遇到阻礙,則下落的時(shí)候,方塊圖形整體向下移動(dòng)一行:
// 圖形下落一行 for (int i = 0; i < blockRows; i++) { for (int j = 0; j < columns; j++) { if (map[rowIndex - i][j] == State.ACTIVE) { // 活動(dòng)塊向下移動(dòng)一行 map[rowIndex - i][j] = State.EMPTY; // 原活動(dòng)塊變成空塊 map[rowIndex - i + 1][j] = State.ACTIVE; // 下一行塊變成活動(dòng)塊 } } }
向左、向右方向移動(dòng)時(shí)是類似的操作:
/** * 向左走 */ 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)判斷行 } } /* 左邊沒有阻礙,則將圖形向左移動(dòng)一個(gè)塊的距離 */ 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(); } }
向下加速移動(dòng)時(shí),就是減小每次正常狀態(tài)下落的時(shí)間間隔:
/** * 向下直走 */ private void down() { // 標(biāo)記可以加速下落 immediate = true; }
如何變換圖形方向,這里僅使用了非常簡單的方法來實(shí)現(xiàn)方向變換,當(dāng)然可以有更優(yōu)的算法實(shí)現(xiàn)方向變換操作,大家可以自己研究:
/** * 旋轉(zhuǎn)方塊圖形 */ private void rotate() { try { if (shape == 4) { // 方形,旋轉(zhuǎn)前后是同一個(gè)形狀 return; } else if (shape == 0) { // 條狀 // 臨時(shí)數(shù)組,放置旋轉(zhuǎn)后圖形 State[][] tmp = new State[4][4]; int startColumn = 0; // 找到圖形開始的第一個(gè)方塊位置 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í)數(shù)組,放置旋轉(zhuǎn)后圖形 State[][] tmp = new State[3][3]; int startColumn = columns; // 找到圖形開始的第一個(gè)方塊位置 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; } } } // 判斷變換后是否會(huì)遇到阻礙 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í)停止,我們就需要判斷這時(shí)是否有某一行或幾行可以消除掉,這時(shí)可以先獲取每行中方塊的個(gè)數(shù),然后再進(jìn)行判斷:
int[] blocksCount = new int[rows]; // 記錄每行有方塊的列數(shù) int eliminateRows = 0; // 消除的行數(shù) /* 計(jì)算每行方塊數(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]++; } }
如果有滿行的方塊,則消除掉該行方塊:
/* 實(shí)現(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ù) } }
最后我們?cè)僦乩L顯示積分就可以了。
重復(fù)以上的生成圖形、圖形下落、左右下移動(dòng)、判斷消除行的操作,一個(gè)簡單的俄羅斯方塊就完成了。
運(yùn)行效果:
完整示例代碼:俄羅斯方塊
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
基于SpringBoot核心原理(自動(dòng)配置、事件驅(qū)動(dòng)、Condition)
這篇文章主要介紹了基于SpringBoot核心原理(自動(dòng)配置、事件驅(qū)動(dòng)、Condition),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-08-08MyBatis異常java.sql.SQLSyntaxErrorException的問題解決
使用mybatis插入數(shù)據(jù)時(shí)出現(xiàn)java.sql.SQLSyntaxErrorException異常,本文就來介紹一下MyBatis異常的問題解決,具有一定的參考價(jià)值,感興趣的可以了解一下2023-08-08關(guān)于@ComponentScan?TypeFilter自定義指定掃描bean的規(guī)則
這篇文章主要介紹了關(guān)于@ComponentScan?TypeFilter自定義指定掃描bean的規(guī)則,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09servlet上傳文件實(shí)現(xiàn)代碼詳解(四)
這篇文章主要為大家詳細(xì)介紹了servlet上傳文件的實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09