前面連連看之游戲界面(一)中已設計出游戲界面的布局文件,該布局文件需要使用一個Activity來負責顯示,除此以外,Activity還需要為游戲界面的按鈕、GameView組件的事件提供事件監聽器。
尤其是對於GameView組件,程序需要監聽用戶的觸碰動作,當用戶觸碰屏幕時,程序需要獲取用戶觸碰的是哪個方塊,並判斷是否需要“消除”該方塊。為了判斷能否消除該方塊,程序需要進行如下判斷:
·如果程序之前已經選中了某個方塊,就判斷當前觸碰多的方塊是否能與之前的方塊“相連”,如果可以相連,則消除兩個方塊;如果兩個方塊不可以相連,則把當前方塊設置為選中方塊。
·如果程序之前沒有選中方塊,直接將當前方塊設置為選中方塊。
Activity的代碼如下:src\org\crazyit\link\Link.java
1 public class Link extends Activity 2 { 3 // 游戲配置對象 4 private GameConf config; 5 // 游戲業務邏輯接口 6 private GameService gameService; 7 // 游戲界面 8 private GameView gameView; 9 // 開始按鈕 10 private Button startButton; 11 // 記錄剩余時間的TextView 12 private TextView timeTextView; 13 // 失敗后彈出的對話框 14 private AlertDialog.Builder lostDialog; 15 // 游戲勝利后的對話框 16 private AlertDialog.Builder successDialog; 17 // 定時器 18 private Timer timer = new Timer(); 19 // 記錄游戲的剩余時間 20 private int gameTime; 21 // 記錄是否處於游戲狀態 22 private boolean isPlaying; 23 // 振動處理類 24 private Vibrator vibrator; 25 // 記錄已經選中的方塊 26 private Piece selected = null; 27 private Handler handler = new Handler() 28 { 29 public void handleMessage(Message msg) 30 { 31 switch (msg.what) 32 { 33 case 0x123: 34 timeTextView.setText("剩余時間: " + gameTime); 35 gameTime--; 36 // 時間小於0, 游戲失敗 37 if (gameTime < 0) 38 { 39 stopTimer(); 40 // 更改游戲的狀態 41 isPlaying = false; 42 lostDialog.show(); 43 return; 44 } 45 break; 46 } 47 } 48 }; 49 50 @Override 51 public void onCreate(Bundle savedInstanceState) 52 { 53 super.onCreate(savedInstanceState); 54 setContentView(R.layout.main); 55 // 初始化界面 56 init(); 57 } 58 59 // 初始化游戲的方法 60 private void init() 61 { 62 config = new GameConf(8, 9, 2, 10 , 100000, this); 63 // 得到游戲區域對象 64 gameView = (GameView) findViewById(R.id.gameView); 65 // 獲取顯示剩余時間的文本框 66 timeTextView = (TextView) findViewById(R.id.timeText); 67 // 獲取開始按鈕 68 startButton = (Button) this.findViewById(R.id.startButton); 69 // 獲取振動器 70 vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE); 71 gameService = new GameServiceImpl(this.config); 72 gameView.setGameService(gameService); 73 // 為開始按鈕的單擊事件綁定事件監聽器 74 startButton.setOnClickListener(new View.OnClickListener() 75 { 76 @Override 77 public void onClick(View source) 78 { 79 startGame(GameConf.DEFAULT_TIME); 80 } 81 }); 82 // 為游戲區域的觸碰事件綁定監聽器 83 this.gameView.setOnTouchListener(new View.OnTouchListener() 84 { 85 public boolean onTouch(View view, MotionEvent e) 86 { 87 if (e.getAction() == MotionEvent.ACTION_DOWN) 88 { 89 gameViewTouchDown(e); 90 } 91 if (e.getAction() == MotionEvent.ACTION_UP) 92 { 93 gameViewTouchUp(e); 94 } 95 return true; 96 } 97 }); 98 // 初始化游戲失敗的對話框 99 lostDialog = createDialog("Lost", "游戲失敗! 重新開始", R.drawable.lost) 100 .setPositiveButton("確定", new DialogInterface.OnClickListener() 101 { 102 public void onClick(DialogInterface dialog, int which) 103 { 104 startGame(GameConf.DEFAULT_TIME); 105 } 106 }); 107 // 初始化游戲勝利的對話框 108 successDialog = createDialog("Success", "游戲勝利! 重新開始", 109 R.drawable.success).setPositiveButton("確定", 110 new DialogInterface.OnClickListener() 111 { 112 public void onClick(DialogInterface dialog, int which) 113 { 114 startGame(GameConf.DEFAULT_TIME); 115 } 116 }); 117 } 118 @Override 119 protected void onPause() 120 { 121 // 暫停游戲 122 stopTimer(); 123 super.onPause(); 124 } 125 @Override 126 protected void onResume() 127 { 128 // 如果處於游戲狀態中 129 if (isPlaying) 130 { 131 // 以剩余時間重寫開始游戲 132 startGame(gameTime); 133 } 134 super.onResume(); 135 } 136 137 // 觸碰游戲區域的處理方法 138 private void gameViewTouchDown(MotionEvent event) 139 { 140 // 獲取GameServiceImpl中的Piece[][]數組 141 Piece[][] pieces = gameService.getPieces(); 142 // 獲取用戶點擊的x座標 143 float touchX = event.getX(); 144 // 獲取用戶點擊的y座標 145 float touchY = event.getY(); 146 // 根據用戶觸碰的座標得到對應的Piece對象 147 Piece currentPiece = gameService.findPiece(touchX, touchY); 148 // 如果沒有選中任何Piece對象(即鼠標點擊的地方沒有圖片), 不再往下執行 149 if (currentPiece == null) 150 return; 151 // 將gameView中的選中方塊設為當前方塊 152 this.gameView.setSelectedPiece(currentPiece); 153 // 表示之前沒有選中任何一個Piece 154 if (this.selected == null) 155 { 156 // 將當前方塊設為已選中的方塊, 重新將GamePanel繪制, 並不再往下執行 157 this.selected = currentPiece; 158 this.gameView.postInvalidate(); 159 return; 160 } 161 // 表示之前已經選擇了一個 162 if (this.selected != null) 163 { 164 // 在這里就要對currentPiece和prePiece進行判斷並進行連接 165 LinkInfo linkInfo = this.gameService.link(this.selected, 166 currentPiece); 167 // 兩個Piece不可連, linkInfo為null 168 if (linkInfo == null) 169 { 170 // 如果連接不成功, 將當前方塊設為選中方塊 171 this.selected = currentPiece; 172 this.gameView.postInvalidate(); 173 } 174 else 175 { 176 // 處理成功連接 177 handleSuccessLink(linkInfo, this.selected 178 , currentPiece, pieces); 179 } 180 } 181 } 182 // 觸碰游戲區域的處理方法 183 private void gameViewTouchUp(MotionEvent e) 184 { 185 this.gameView.postInvalidate(); 186 } 187 188 // 以gameTime作為剩余時間開始或恢復游戲 189 private void startGame(int gameTime) 190 { 191 // 如果之前的timer還未取消,取消timer 192 if (this.timer != null) 193 { 194 stopTimer(); 195 } 196 // 重新設置游戲時間 197 this.gameTime = gameTime; 198 // 如果游戲剩余時間與總游戲時間相等,即為重新開始新游戲 199 if(gameTime == GameConf.DEFAULT_TIME) 200 { 201 // 開始新的游戲游戲 202 gameView.startGame(); 203 } 204 isPlaying = true; 205 this.timer = new Timer(); 206 // 啟動計時器 , 每隔1秒發送一次消息 207 this.timer.schedule(new TimerTask() 208 { 209 public void run() 210 { 211 handler.sendEmptyMessage(0x123); 212 } 213 }, 0, 1000); 214 // 將選中方塊設為null。 215 this.selected = null; 216 } 217 218 /** 219 * 成功連接后處理 220 * 221 * @param linkInfo 連接信息 222 * @param prePiece 前一個選中方塊 223 * @param currentPiece 當前選擇方塊 224 * @param pieces 系統中還剩的全部方塊 225 */ 226 private void handleSuccessLink(LinkInfo linkInfo, Piece prePiece, 227 Piece currentPiece, Piece[][] pieces) 228 { 229 // 它們可以相連, 讓GamePanel處理LinkInfo 230 this.gameView.setLinkInfo(linkInfo); 231 // 將gameView中的選中方塊設為null 232 this.gameView.setSelectedPiece(null); 233 this.gameView.postInvalidate(); 234 // 將兩個Piece對象從數組中刪除 235 pieces[prePiece.getIndexX()][prePiece.getIndexY()] = null; 236 pieces[currentPiece.getIndexX()][currentPiece.getIndexY()] = null; 237 // 將選中的方塊設置null。 238 this.selected = null; 239 // 手機振動(100毫秒) 240 this.vibrator.vibrate(100); 241 // 判斷是否還有剩下的方塊, 如果沒有, 游戲勝利 242 if (!this.gameService.hasPieces()) 243 { 244 // 游戲勝利 245 this.successDialog.show(); 246 // 停止定時器 247 stopTimer(); 248 // 更改游戲狀態 249 isPlaying = false; 250 } 251 } 252 253 // 創建對話框的工具方法 254 private AlertDialog.Builder createDialog(String title, String message, 255 int imageResource) 256 { 257 return new AlertDialog.Builder(this).setTitle(title) 258 .setMessage(message).setIcon(imageResource); 259 } 260 private void stopTimer() 261 { 262 // 停止定時器 263 this.timer.cancel(); 264 this.timer = null; 265 } 266 }
上面代碼中的gameViewTouchDown()方法負責處理觸碰事件。它會先根據觸碰點計算出觸碰的方法。接下來該方法會判斷是否之前已有選中的方塊,如果沒有,直接將當前方塊設為選中方塊,如果有,判斷兩個方塊是否可以相連。
如果兩個方塊可以相連,程序將會從Piece[][]數組中刪除這兩個方塊,該邏輯由handleSuccessLink()方法完成。
除此之外,該程序為了控制時間流失,定義了一個計時器,該計時器每隔1秒發送一條消息,程序將會根據該消息減少游戲剩余的時間。
該Link Activity用的兩個類如下:
·GameConf:負責管理游戲的初始化設置信息。
·GameService:負責游戲的邏輯實現。
上面兩個工具類中GameConf只是一個簡單的設置類,代碼如下:
1 public class GameConf 2 { 3 // 設置連連看的每個方塊的圖片的寬、高 4 public static final int PIECE_WIDTH = 40; 5 public static final int PIECE_HEIGHT = 40; 6 // 記錄游戲的總事件(100秒). 7 public static int DEFAULT_TIME = 100; 8 // Piece[][]數組第一維的長度 9 private int xSize; 10 // Piece[][]數組第二維的長度 11 private int ySize; 12 // Board中第一張圖片出現的x座標 13 private int beginImageX; 14 // Board中第一張圖片出現的y座標 15 private int beginImageY; 16 // 記錄游戲的總時間, 單位是秒 17 private long gameTime; 18 private Context context; 19 20 /** 21 * 提供一個參數構造器 22 * 23 * @param xSize Piece[][]數組第一維長度 24 * @param ySize Piece[][]數組第二維長度 25 * @param beginImageX Board中第一張圖片出現的x座標 26 * @param beginImageY Board中第一張圖片出現的y座標 27 * @param gameTime 設置每局的時間, 單位是秒 28 * @param context 應用上下文 29 */ 30 public GameConf(int xSize, int ySize, int beginImageX, 31 int beginImageY, long gameTime, Context context) 32 { 33 this.xSize = xSize; 34 this.ySize = ySize; 35 this.beginImageX = beginImageX; 36 this.beginImageY = beginImageY; 37 this.gameTime = gameTime; 38 this.context = context; 39 } 40 41 public long getGameTime() 42 { 43 return gameTime; 44 } 45 46 public int getXSize() 47 { 48 return xSize; 49 } 50 51 public int getYSize() 52 { 53 return ySize; 54 } 55 56 public int getBeginImageX() 57 { 58 return beginImageX; 59 } 60 61 public int getBeginImageY() 62 { 63 return beginImageY; 64 } 65 66 public Context getContext() 67 { 68 return context; 69 } 70 }
具體實現步驟連接: