首先說明:這個五子棋使用的是SurfaceView,實際上並不好,因為這個本應該使用View類會更加合適的。但是我為了練習使用SurfaceView,所以才用了它。
SurfaceView和View那個更合適,可以看你開發的程式的性質。總結來說如下:(摘自別人部落格中的一段話)
SurfaceView和View最本質的區別在於,surfaceView是在一個新起的單獨線程中可以重新繪製畫面而View必須在UI的主線程中更新畫面。那麼在UI的主線程中更新畫面 可能會引發問題,比如你更新畫面的時間過長,那麼你的主UI線程會被你正在畫的函數阻塞。那麼將無法響應按鍵,觸屏等訊息。 當使用surfaceView 由於是在新的線程中更新畫面所以不會阻塞你的UI主線程。但這也帶來了另外一個問題,就是事件同步。比如你觸屏了一下,你需要surfaceView中thread處理,一般就需要有一個event queue的設計來儲存touch event,這會稍稍複雜一點,因為涉及到線程同步。 所以基於以上,根據遊戲特點,一般分成兩類。 1 被動更新畫面的。比如棋類,這種用view就好了。因為畫面的更新是依賴於 onTouch 來更新,可以直接使用 invalidate。 因為這種情況下,這一次Touch和下一次的Touch需要的時間比較長些,不會產生影響。 2 主動更新。比如一個人在一直跑動。這就需要一個單獨的thread不停的重繪人的狀態,避免阻塞main UI thread。所以顯然view不合適,需要surfaceView來控制。
好了,那麼下面開始貼代碼,當然,代碼比較多,所以只貼主要的內容,我會把代碼放在我的資源上的,想練手的人可以下載看一下,如果有不好的地方,請不吝賜教。
首先說明:這個五子棋的功能實現如下
1.單人對戰--->也就是人機對戰,這個功能我沒有實現,因為這個需要很複雜的演算法知識。所以,介面裡面雖然有他,但是我沒有實現功能。
2.雙人對戰--->也就是今天的主要內容。
3.音效-->下棋時候的落子的聲音。
4.背景音樂--->我沒有實現,實際上是有點懶了,不想寫了。所以也沒有實現。
我這個程式,還需完善的地方有以下幾點
第一:人機對戰
第二:背景音樂
第三:悔棋功能
第四:其他完善程式的功能。
第五:藍芽的雙人對戰。
想完善的人可以完善。
第二,第三,第四,很簡單,最難的是第一點,因為我想到最難的沒有寫出來,然後就不太想寫了。所以唯寫了一個兩人對戰的。
我就恨我沒有意志力,總是虎頭蛇尾的,不知道什麼時候,我就不喜歡寫部落格了。不管怎樣,我會盡量堅持下去的。
代碼一:棋子類,當時想了好久,要繼承point,擴充功能?還是讓point當做一個屬性?還是自己定義呢?最後覺得自己定義還是最好的。因為用point也只是用x,y座標而已。
package com.example.fivechess;/** * 棋子類 */public class Piece {private int x;private int y;private int properity;// 0,1,2分別代表空,黑,白public int getX() {return x;}public void setX(int x) {this.x = x;}public int getY() {return y;}public void setY(int y) {this.y = y;}public int getProperity() {return properity;}public void setProperity(int properity) {this.properity = properity;}public Piece(int x, int y, int properity) {super();this.x = x;this.y = y;this.properity = properity;}public Piece() {super();}@Overridepublic String toString() {return "Piece [x=" + x + ", y=" + y + ", properity=" + properity + "]";}}
代碼二:這是主要代碼,代碼中的注釋寫的很清楚,不懂的可以問我哈。
package com.example.fivechess;import java.util.ArrayList;import java.util.List;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.media.AudioManager;import android.media.SoundPool;import android.view.MotionEvent;import android.view.SurfaceHolder;import android.view.SurfaceView;public class MainView extends SurfaceView implements SurfaceHolder.Callback,Runnable {private List<Piece> all = new ArrayList<Piece>();// 儲存所有的棋子private SurfaceHolder surfaceHolder;private int flag = 1;private Canvas canvas;private int soundId;// 下棋後的音效private SoundPool soundPool;private float volumnRatio;public MainView(Context context) {super(context);this.initSound(context);System.out.println("MainView.MainView()");surfaceHolder = this.getHolder();surfaceHolder.addCallback(this);}private void initSound(Context context) {soundPool = new SoundPool(1, AudioManager.STREAM_MUSIC, 10);soundId = soundPool.load(context, R.raw.piece_down, 5);AudioManager audioManager = (AudioManager) context.getSystemService(context.AUDIO_SERVICE);float audioMaxVolumn = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);float audioCurrentVolumn = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);volumnRatio = audioCurrentVolumn / audioMaxVolumn;}public void run() {drawDesk();}/** * 初始化介面,繪製棋盤和背景 */public void initDesk() {System.out.println("MainView.initDesk()");// 畫筆Paint paint = new Paint();paint.setColor(Color.RED);// 背景Bitmap bitmap = BitmapFactory.decodeStream(getResources().openRawResource(R.drawable.five_chess_background));// 畫布canvas = surfaceHolder.lockCanvas();canvas.drawBitmap(bitmap, 0, 0, paint);// 棋盤15*15for (int i = 0; i <= Utils.NUMBER; i++) {canvas.drawLine(0, i * Utils.CELL_LENGTH, Utils.NUMBER* Utils.CELL_LENGTH, i * Utils.CELL_LENGTH, paint);canvas.drawLine(i * Utils.CELL_LENGTH, 0, i * Utils.CELL_LENGTH,Utils.NUMBER * Utils.CELL_LENGTH, paint);}}/** * 全部繪製案頭,案頭上的所有的圖片重新繪製 */public void drawDesk() {System.out.println("MainView.drawDesk()");initDesk();drawPieces(all);}@Overridepublic boolean onTouchEvent(MotionEvent event) {System.out.println("MainView.onTouchEvent()");int x = (int) (event.getX() / Utils.CELL_LENGTH) * Utils.CELL_LENGTH;int y = (int) (event.getY() / Utils.CELL_LENGTH) * Utils.CELL_LENGTH;if (isInChessBoard(x, y) == false) {return false;}soundEffect();Piece piece = new Piece(x, y, flag++ % 2);all.add(piece);drawDesk();// 落完子以後,查看是不是有勝利int win = judgeIsWin(piece);if (win != Utils.EMPTY) {// 如果有人勝利了drawWinView(win);// 根據勝利的結果,畫圖}return super.onTouchEvent(event);}/** * 播放音效 */private void soundEffect() {if (Utils.isloud == true) {soundPool.play(soundId, volumnRatio, volumnRatio, 1, 1, 1);}}// 退出private void drawWinView(int win) {drawDesk();Paint paint = new Paint();paint.setColor(Color.RED);Bitmap bitmap = null;if (win == Utils.BLACK) {bitmap = BitmapFactory.decodeStream(getResources().openRawResource(R.drawable.black_win));} else if (win == Utils.WHITE) {bitmap = BitmapFactory.decodeStream(getResources().openRawResource(R.drawable.white_win));}canvas = surfaceHolder.lockCanvas();canvas.drawBitmap(bitmap, Utils.SCREEN_WIDTH / 3,Utils.SCREEN_WIDTH / 3, paint);surfaceHolder.unlockCanvasAndPost(canvas);try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.exit(0);}/** * 判斷是否棋子勝利,0,1,2分別代表,白,黑,空, * * @param piece * 落子點 */private int judgeIsWin(Piece piece) {System.out.println("MainView.judgeIsWin()");if (oneJudge(piece) != Utils.EMPTY || twoJudge(piece) != Utils.EMPTY|| threeJudge(piece) != Utils.EMPTY|| fourJudge(piece) != Utils.EMPTY) {System.out.println("勝利:" + piece.getProperity());drawWinView(piece.getProperity());return piece.getProperity();}return Utils.EMPTY;}/** * 第一種判斷方式,從棋子的縱座標,上到下判斷,傳回值是黑,白,空,分別代表黑勝,白勝,沒有五子連珠 */private int oneJudge(Piece piece) {System.out.println("MainView.oneJudge()");int count = 0;for (int i = 0; i < Utils.NUMBER; i++) {Piece p = new Piece(piece.getX(), i * Utils.CELL_LENGTH,Utils.EMPTY);if (isContainedPiece(p, piece)) {// 如果有這個子,且是一個顏色的count++;System.out.println("****count:" + count);if (count == 5) {// 如果為5的話return piece.getProperity();}} else {count = 0;}}return Utils.EMPTY;}/** * 第二種判斷方式,從棋子的反斜線方向判斷,傳回值是黑,白,空,分別代表黑勝,白勝,沒有五子連珠 */private int twoJudge(Piece piece) {System.out.println("MainView.twoJudge()");int x = piece.getX() / Utils.CELL_LENGTH;int y = piece.getY() / Utils.CELL_LENGTH;while (x > 0 && y > 0) {x--;y--;}int count = 0;for (; x <= Utils.NUMBER && y <= Utils.NUMBER; x++, y++) {Piece p = new Piece(x * Utils.CELL_LENGTH, y * Utils.CELL_LENGTH,Utils.EMPTY);if (isContainedPiece(p, piece)) {// 如果有這個子,且是一個顏色的count++;System.out.println("****count:" + count);if (count == 5) {// 如果為5的話return piece.getProperity();}} else {count = 0;}}return Utils.EMPTY;}/** * 第三種判斷方式,從棋子的橫座標,從左至右判斷,傳回值是黑,白,空,分別代表黑勝,白勝,沒有五子連珠 */private int threeJudge(Piece piece) {System.out.println("MainView.threeJudge()");int count = 0;for (int i = 0; i < Utils.NUMBER; i++) {Piece p = new Piece(i * Utils.CELL_LENGTH, piece.getY(),Utils.EMPTY);if (isContainedPiece(p, piece)) {// 如果有這個子,且是一個顏色的count++;System.out.println("****count:" + count);if (count == 5) {// 如果為5的話return piece.getProperity();}} else {count = 0;}}return Utils.EMPTY;}/** * 第四種判斷方式,從棋子的正斜杠方向判斷,傳回值是黑,白,空,分別代表黑勝,白勝,沒有五子連珠 */private int fourJudge(Piece piece) {System.out.println("MainView.fourJudge()");int x = piece.getX() / Utils.CELL_LENGTH;int y = piece.getY() / Utils.CELL_LENGTH;while (x > 0 && y < Utils.NUMBER) {x--;y++;}int count = 0;for (; x <= Utils.NUMBER && y >= 0; x++, y--) {Piece p = new Piece(x * Utils.CELL_LENGTH, y * Utils.CELL_LENGTH,Utils.EMPTY);if (isContainedPiece(p, piece)) {// 如果有這個子,且是一個顏色的count++;System.out.println("****count:" + count);if (count == 5) {// 如果為5的話return piece.getProperity();}} else {count = 0;}}return Utils.EMPTY;}/** * 判斷集合中是否包含這個棋子的位置,判斷是不是同一種棋子 */public boolean isContainedPiece(Piece p, Piece piece) {for (int i = 0; i < all.size(); i++) {if (p.getX() == all.get(i).getX() && p.getY() == all.get(i).getY()&& piece.getProperity() == all.get(i).getProperity()) {return true;}}return false;}/** * 判斷是否在棋盤,判斷是否符合條件 */private boolean isInChessBoard(int x, int y) {if (x < 0 || x >= Utils.NUMBER * Utils.CELL_LENGTH || y < 0|| y >= Utils.NUMBER * Utils.CELL_LENGTH) {return false;}for (int i = 0; i < all.size(); i++) {Piece piece = all.get(i);if (piece.getX() == x && piece.getY() == y) {return false;}}return true;}void drawPieces(List<Piece> all) {System.out.println("MainView.drawPieces()");Bitmap bitmap = null;for (int i = 0; i < all.size(); i++) {Piece piece = all.get(i);if (piece.getProperity() == Utils.BLACK) {bitmap = BitmapFactory.decodeStream(getResources().openRawResource(R.drawable.black));} else if (piece.getProperity() == Utils.WHITE) {bitmap = BitmapFactory.decodeStream(getResources().openRawResource(R.drawable.write));}Paint paint = new Paint();paint.setColor(Color.RED);paint.setTextSize(20);// 設定字型大小if (flag % 2 == Utils.WHITE) {// 注意flag已經加1了,如果下一步是白canvas.drawText("該白方走:", Utils.SCREEN_WIDTH / 3,Utils.SCREEN_WIDTH+ (Utils.SCREEN_HEIGHT - Utils.SCREEN_WIDTH)/ 3, paint);} else {canvas.drawText("該黑方走:", Utils.SCREEN_WIDTH / 3,Utils.SCREEN_WIDTH+ (Utils.SCREEN_HEIGHT - Utils.SCREEN_WIDTH)/ 3, paint);}canvas.drawBitmap(bitmap, piece.getX(), piece.getY(), paint);}surfaceHolder.unlockCanvasAndPost(canvas);}public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {System.out.println("MainView.surfaceChanged()");}public void surfaceCreated(SurfaceHolder holder) {System.out.println("MainView.surfaceCreated()");new Thread(this).start();}public void surfaceDestroyed(SurfaceHolder holder) {System.out.println("MainView.surfaceDestroyed()");}}
主要就是判斷勝利的方式的演算法,其實,我也知道,五子棋最好是用數組來儲存。這樣代碼和運行效率是最高的,可我當時不知道怎麼就非得使用list,估計腦門被加了。無所謂了,可能是因為貪食蛇也是用的list儲存的,所以就很自然的用了list.有興趣的可以自己試一試那個數組儲存的,我猜一定比我這個簡單,易懂。自認為自己寫的代碼的架構還有待提升。所以最近在狠看設計模式,學習一下人家寫的代碼的品質和擴充性。
好了,想下代碼的,去我的資源下代碼吧。