承接上篇 (界面與數據結構),本文聚焦遊戲核心邏輯:方塊下落、移動、變形、碰撞檢測、消行算法、遊戲結束判定、速度控制。
public void brickDown() {
if (reachBottom()) { // 觸底
addBrickToMap();
gernerateBrick();
clearAllFullRow();
} else {
cRow++; // 正常下落一行
if (collisWithMap()) { // 落地碰撞 (疊加在現有方塊上)
cRow--; // 回退
addBrickToMap(); // 固化到地圖
gernerateBrick(); // 生成新方塊
clearAllFullRow(); // 檢查消行
}
}
}
雙重觸底判定:reachBottom() 檢測遊戲區域底部,collisWithMap() 檢測疊加碰撞。
private boolean reachBottom() {
int bottomRow = getBottomNotEmptyRow(); // 方塊最底部非空行 (0-3)
return (cRow + bottomRow >= MAXROW - 1); // 方塊底部行號 >= 遊戲區最後一行
}
private void addBrickToMap() {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (brick[brickType][index][i][j] == 1) { // 只處理實心格子
int mapRow = cRow + i;
int mapCol = cCol + j;
if (mapCol >= 0 && mapCol < MAXCOL && mapRow < MAXROW) { // 邊界保護
map[mapRow][mapCol] = 1;
}
}
}
}
}
public void gernerateBrick() {
brickType = nextBrickType; // 下一個變當前
nextBrickType = Math.abs(r.nextInt() % brick.length); // 隨機下一個
index = 0; // 重置形狀
cRow = -3; // 初始在可視區上方
cCol = 3; // 水平居中
}
public void changeShape() {
index++; // 切換到下一種旋轉態
if (index == brick[brickType].length) index = 0; // 循環
if (testNewPosition()) { // 新姿態不可用?
index--; // 回退
if (index < 0) index = brick[brickType].length - 1;
}
}
public void moveToLeft() {
cCol--;
if (testNewPosition()) cCol++; // 不可用則回退
}
public void moveToRight() {
cCol++;
if (testNewPosition()) cCol--;
}
private boolean testNewPosition() {
// 1. 左邊界
if (getLeftNotEmptyLine() + cCol < 0) return true;
// 2. 右邊界
if (cCol + getRightNotEmptyLine() > MAXCOL - 1) return true;
// 3. 底部邊界
if (getBottomNotEmptyRow() + cRow >= MAXROW - 1) return true;
// 4. 與地圖現有方塊碰撞
if (collisWithMap()) return true;
return false; // 完全可用
}
| 方法 | 邏輯 | 返回值 |
|---|---|---|
getLeftNotEmptyLine() | 掃描 4x4 矩陣,最左非空列 (col 0→3) | 0-3 |
getRightNotEmptyLine() | 掃描 4x4 矩陣,最右非空列 (col 3→0) | 0-3 |
getBottomNotEmptyRow() | 掃描 4x4 矩陣,最底非空行 (row 3→0) | 0-3 |
這三個方法配合 cRow/cCol 實現精確邊界碰撞,支持異形方塊 (如 L/T/Z 型) 的不規則外緣。
private boolean collisWithMap() {
for (int col = 0; col < 4; col++) {
for (int row = 3; row >= 0; row--) { // 從底部向上掃描,優化
if (brick[brickType][index][row][col] == 1) {
int mapRow = cRow + row;
int mapCol = cCol + col;
if (mapRow >= 0 && mapRow < MAXROW &&
mapCol >= 0 && mapCol < MAXCOL) {
if (map[mapRow][mapCol] == 1) return true; // 重疊
}
break; // 同列只需檢查最底部的方塊
}
}
}
return false;
}
優化:每列只檢查最底部的實心格子 (內層循環 break),減少無效比較。
private boolean isFullRow(int row) {
int count = 0;
for (int col = 0; col < MAXCOL; col++)
if (map[row][col] == 1) count++;
return count == MAXCOL;
}
private void clearOneFullRow(int row) {
// 上方所有行整體下移一行
for (int i = row - 1; i >= 0; i--) {
for (int col = 0; col < MAXCOL; col++) {
map[i + 1][col] = map[i][col];
}
}
// 頂行清零
for (int col = 0; col < MAXCOL; col++) map[0][col] = 0;
}
private void clearAllFullRow() {
for (int row = MAXROW - 1; row >= 0; row--) {
if (isFullRow(row)) {
score += 10; // 加分
clearOneFullRow(row); // 消除
row++; // 關鍵:同一行下落後可能再次滿行,需重新檢查
}
}
}
關鍵細節:row++ 抵消外層循環的 row--,實現原地重新檢查,正確處理連續消行 (如雙消/三消/四消)。
public boolean isGameOver() {
cRow++; // 嘗試下落一步
if (collisWithMap()) { // 立即碰撞
cRow--; // 還原
if (cRow < 0) { // 方塊還在可視區上方 (未完全進入)
for (int row = 0; row < 4; row++) {
for (int col = 0; col < 4; col++) {
if (row + cRow >= 0) break; // 已進入可視區停止檢查
if (brick[brickType][index][row][col] == 1)
return true; // 有方塊卡在頂部不可見區域
}
}
}
} else {
cRow--;
}
return false;
}
邏輯:新方塊生成位置 cRow=-3,若頂部已堆疊導致新方塊一生成即碰撞,且方塊主體仍在可視區上方 (cRow < 0),判定遊戲結束。
public void highSpeed() { times = 5; } // 5倍速 (下落間隔 /5)
public void normalSpeed() { times = 1; } // 正常速
主循環中 Thread.sleep(speed / times) 實現加速效果 (按住下鍵觸發)。
public void paintBrick(Graphics g) {
for (int row = 0; row < 4; row++)
for (int col = 0; col < 4; col++)
if (brick[brickType][index][row][col] == 1) {
int cx = (cCol + col) * CELLWIDTH;
int cy = (cRow + row) * CELLWIDTH;
paintCell(g, cx, cy);
}
}
public void paintNextBrick(Graphics g) {
g.drawString("下一個方塊", nextBrickX, nextBrickY, Graphics.LEFT | Graphics.TOP);
for (int row = 0; row < 4; row++)
for (int col = 0; col < 4; col++)
if (brick[nextBrickType][0][row][col] == 1) { // 固定顯示第0種旋轉態
int cx = nextBrickX + col * CELLWIDTH;
int cy = nextBrickY + 20 + row * CELLWIDTH;
paintCell(g, cx, cy);
}
}
public void paintLevelAndScore(Graphics g) {
g.drawString("當前得分:", nextBrickX, 100, Graphics.TOP | Graphics.LEFT);
g.drawString(String.valueOf(score), nextBrickX, 115, Graphics.LEFT | Graphics.TOP);
g.drawString("第" + level + "關", nextBrickX, 150, Graphics.TOP | Graphics.LEFT);
}
這套邏輯實現了標準俄羅斯方塊核心規則:
testNewPosition() 守護代碼結構清晰、邊界條件處理完善,是 J2ME 遊戲邏輯層的優秀參考實現。