Android实现五子棋游戏(一) 游戏基本逻辑

来源:互联网 发布:网络诈骗手段方式 编辑:程序博客网 时间:2024/06/03 05:50

转载请注明出处:http://blog.csdn.net/a512337862/article/details/74165085

最近,写一个简单的五子棋游戏,效果如下:

这里写图片描述

现在其实还算不上一个真正的游戏,因为只是实现了在同一设备上五子棋最基本的逻辑。下面简单介绍一下逻辑,仅供参考。

思路

1.五子棋游戏的基本逻辑:包括棋盘棋子的绘制,游戏胜利判断,重开游戏。
2.添加背景图片,游戏胜负信息,重开按钮等。

代码分析

FiveChessView

/** * Created by ZhangHao on 2017/6/27. * 五子棋 View */public class FiveChessView extends View implements View.OnTouchListener {    //画笔    private Paint paint;    //棋子数组    private int[][] chessArray;    //当前下棋顺序(默认白棋先下)    private boolean isWhite = true;    //游戏是否结束    private boolean isGameOver = false;    //bitmap    private Bitmap whiteChess;    private Bitmap blackChess;    //Rect    private Rect rect;    //棋盘宽高    private float len;    //棋盘格数    private int GRID_NUMBER = 10;    //每格之间的距离    private float preWidth;    //边距    private float offset;    //回调    private GameCallBack callBack;    //当前黑白棋胜利次数    private int whiteChessCount, blackChessCount;    /**     * 一些常量     */    //白棋    public static final int WHITE_CHESS = 1;    //黑棋    public static final int BLACK_CHESS = 2;    //白棋赢    public static final int WHITE_WIN = 101;    //黑棋赢    public static final int BLACK_WIN = 102;    //平局    public static final int NO_WIN = 103;    public FiveChessView(Context context) {        this(context, null);    }    public FiveChessView(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public FiveChessView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        //初始化Paint        paint = new Paint();        //设置抗锯齿        paint.setAntiAlias(true);        paint.setColor(Color.BLACK);        //初始化chessArray        chessArray = new int[GRID_NUMBER][GRID_NUMBER];        //初始化棋子图片bitmap        whiteChess = BitmapFactory.decodeResource(context.getResources(), R.drawable.white_chess);        blackChess = BitmapFactory.decodeResource(context.getResources(), R.drawable.black_chess);        //初始化胜利局数        whiteChessCount = 0;        blackChessCount = 0;        //初始化Rect        rect = new Rect();        //设置点击监听        setOnTouchListener(this);    }    /**     * 重新测量宽高,确保宽高一样     */    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        //获取高宽值        int width = MeasureSpec.getSize(widthMeasureSpec);        int height = MeasureSpec.getSize(heightMeasureSpec);        //获取宽高中较小的值        int len = width > height ? height : width;        //重新设置宽高        setMeasuredDimension(len, len);    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        //棋盘为一个GRID_NUMBER*GRID_NUMBER的正方形,所有棋盘宽高必须一样        len = getWidth() > getHeight() ? getHeight() : getWidth();        preWidth = len / GRID_NUMBER;        //边距        offset = preWidth / 2;        //棋盘线条        for (int i = 0; i < GRID_NUMBER; i++) {            float start = i * preWidth + offset;            //横线            canvas.drawLine(offset, start, len - offset, start, paint);            //竖线            canvas.drawLine(start, offset, start, len - offset, paint);        }        //绘制棋子        for (int i = 0; i < GRID_NUMBER; i++) {            for (int j = 0; j < GRID_NUMBER; j++) {                //rect中点坐标                float rectX = offset + i * preWidth;                float rectY = offset + j * preWidth;                //设置rect位置                rect.set((int) (rectX - offset), (int) (rectY - offset),                        (int) (rectX + offset), (int) (rectY + offset));                //遍历chessArray                switch (chessArray[i][j]) {                    case WHITE_CHESS:                        //绘制白棋                        canvas.drawBitmap(whiteChess, null, rect, paint);                        break;                    case BLACK_CHESS:                        //绘制黑棋                        canvas.drawBitmap(blackChess, null, rect, paint);                        break;                }            }        }    }    /**     * 判断是否结束     */    private void checkGameOver() {        //获取落子的颜色(如果当前是白棋,则落子是黑棋)        int chess = isWhite ? BLACK_CHESS : WHITE_CHESS;        //棋盘是否填满        boolean isFull = true;        //遍历chessArray        for (int i = 0; i < GRID_NUMBER; i++) {            for (int j = 0; j < GRID_NUMBER; j++) {                //判断棋盘是否填满                if (chessArray[i][j] != BLACK_CHESS && chessArray[i][j] != WHITE_CHESS) {                    isFull = false;                }                //只需要判断落子是否五连即可                if (chessArray[i][j] == chess) {                    //判断五子相连                    if (isFiveSame(i, j)) {                        //五子相连游戏结束                        isGameOver = true;                        if (callBack != null) {                            if (chess == WHITE_CHESS) {                                whiteChessCount++;                            } else {                                blackChessCount++;                            }                            callBack.GameOver(chess == WHITE_CHESS ? WHITE_WIN : BLACK_WIN);                        }                        return;                    }                }            }        }        //如果棋盘填满,平局结束        if (isFull) {            isGameOver = true;            if (callBack != null) {                callBack.GameOver(NO_WIN);            }        }    }    /**     * 重置游戏     */    public void resetGame() {        isGameOver = false;        //重置棋盘状态        for (int i = 0; i < GRID_NUMBER; i++) {            for (int j = 0; j < GRID_NUMBER; j++) {                chessArray[i][j] = 0;            }        }        //更新UI        postInvalidate();    }    /**     * 判断是否存在五子相连     *     * @return     */    private boolean isFiveSame(int x, int y) {        //判断横向        if (x + 4 < GRID_NUMBER) {            if (chessArray[x][y] == chessArray[x + 1][y] && chessArray[x][y] == chessArray[x + 2][y]                    && chessArray[x][y] == chessArray[x + 3][y] && chessArray[x][y] == chessArray[x + 4][y]) {                return true;            }        }        //判断纵向        if (y + 4 < GRID_NUMBER) {            if (chessArray[x][y] == chessArray[x][y + 1] && chessArray[x][y] == chessArray[x][y + 2]                    && chessArray[x][y] == chessArray[x][y + 3] && chessArray[x][y] == chessArray[x][y + 4]) {                return true;            }        }        //判断斜向(左上到右下)        if (y + 4 < GRID_NUMBER && x + 4 < GRID_NUMBER) {            if (chessArray[x][y] == chessArray[x + 1][y + 1] && chessArray[x][y] == chessArray[x + 2][y + 2]                    && chessArray[x][y] == chessArray[x + 3][y + 3] && chessArray[x][y] == chessArray[x + 4][y + 4]) {                return true;            }        }        //判断斜向(左下到右上)        if (y - 4 > 0 && x + 4 < GRID_NUMBER) {            if (chessArray[x][y] == chessArray[x + 1][y - 1] && chessArray[x][y] == chessArray[x + 2][y - 2]                    && chessArray[x][y] == chessArray[x + 3][y - 3] && chessArray[x][y] == chessArray[x + 4][y - 4]) {                return true;            }        }        return false;    }    @Override    public boolean onTouch(View v, MotionEvent event) {        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN:                if (!isGameOver) {                    //获取按下时的位置                    float downX = event.getX();                    float downY = event.getY();                    //点击的位置在棋盘上                    if (downX >= offset / 2 && downX <= len - offset / 2                            && downY >= offset / 2 && downY <= len - offset / 2) {                        //获取棋子对应的位置                        int x = (int) (downX / preWidth);                        int y = (int) (downY / preWidth);                        //判断当前位置是否已经有子                        if (chessArray[x][y] != WHITE_CHESS &&                                chessArray[x][y] != BLACK_CHESS) {                            //给数组赋值                            chessArray[x][y] = isWhite ? WHITE_CHESS : BLACK_CHESS;                            //修改当前执子                            isWhite = !isWhite;                            //更新棋盘                            postInvalidate();                            //判断是否结束                            checkGameOver();                            //回调当前执子                            if (callBack != null) {                                callBack.ChangeGamer(isWhite);                            }                        }                    }                } else {                    Toast.makeText(mContext, "游戏已经结束,请重新开始!",                            Toast.LENGTH_SHORT).show();                }                break;        }        return false;    }    public void setCallBack(GameCallBack callBack) {        this.callBack = callBack;    }    public int getWhiteChessCount() {        return whiteChessCount;    }    public int getBlackChessCount() {        return blackChessCount;    }}

FiveChessView主要实现五子棋游戏的基本逻辑,包括棋子绘制,游戏胜利判断,重开游戏等逻辑:

构造方法

FiveChessView构造方法主要是对一些参数的初始化:
1.int[][] chessArray来保存游戏的棋子信息,chessArray[x][y] = 0/1/2(0->无子,1->白棋,2->黑棋)表示在(x,y)点的棋子信息,并通过判断chessArray各个位置的棋子来判断胜利。
2.whiteChess,blackChess黑白棋子图片对应的Bitmap。
3.rect用来指定绘制的棋子的大小。
4.添加OnTouchListener

 public FiveChessView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        //初始化Paint        paint = new Paint();        //设置抗锯齿        paint.setAntiAlias(true);        paint.setColor(Color.BLACK);        //初始化chessArray        chessArray = new int[GRID_NUMBER][GRID_NUMBER];        //初始化棋子图片bitmap        whiteChess = BitmapFactory.decodeResource(context.getResources(), R.drawable.white_chess);        blackChess = BitmapFactory.decodeResource(context.getResources(), R.drawable.black_chess);        //初始化胜利局数        whiteChessCount = 0;        blackChessCount = 0;        //初始化Rect        rect = new Rect();        //设置点击监听        setOnTouchListener(this);    }

onMeasure

因为五子棋棋盘是一个N*N的网格,必须确保FiveChessView的宽高一致,所以必须在onMeasure对宽高进行重绘。

/**     * 重新测量宽高,确保宽高一样     */    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        //获取高宽值        int width = MeasureSpec.getSize(widthMeasureSpec);        int height = MeasureSpec.getSize(heightMeasureSpec);        //获取宽高中较小的值        int len = width > height ? height : width;        //重新设置宽高        setMeasuredDimension(len, len);    }

onDraw

onDraw主要用来绘制棋盘网格线条和所有位置的棋子。

@Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        //棋盘为一个GRID_NUMBER*GRID_NUMBER的正方形,所有棋盘宽高必须一样        len = getWidth() > getHeight() ? getHeight() : getWidth();        preWidth = len / GRID_NUMBER;        //边距        offset = preWidth / 2;        //棋盘线条        for (int i = 0; i < GRID_NUMBER; i++) {            float start = i * preWidth + offset;            //横线            canvas.drawLine(offset, start, len - offset, start, paint);            //竖线            canvas.drawLine(start, offset, start, len - offset, paint);        }        //绘制棋子        for (int i = 0; i < GRID_NUMBER; i++) {            for (int j = 0; j < GRID_NUMBER; j++) {                //rect中点坐标                float rectX = offset + i * preWidth;                float rectY = offset + j * preWidth;                //设置rect位置                rect.set((int) (rectX - offset), (int) (rectY - offset),                        (int) (rectX + offset), (int) (rectY + offset));                //遍历chessArray                switch (chessArray[i][j]) {                    case WHITE_CHESS:                        //绘制白棋                        canvas.drawBitmap(whiteChess, null, rect, paint);                        break;                    case BLACK_CHESS:                        //绘制黑棋                        canvas.drawBitmap(blackChess, null, rect, paint);                        break;                }            }        }    }

isFiveSame

isFiveSame用于判断指定位置是否存在五子相连,因为在checkGameOver中从左到右,从上到下遍历,所以判断五子相连,只需要判断以下四种情况:
1.横向(从左到右)
2.纵向(从下往上)
3.斜向(左上到右下)
4.斜向(左下到右上)
其他的类似横向(从右往左)这种情况就可以无需判断了。

/**     * 判断是否存在五子相连     *     * @return     */    private boolean isFiveSame(int x, int y) {        //判断横向(从左到右)        if (x + 4 < GRID_NUMBER) {            if (chessArray[x][y] == chessArray[x + 1][y] && chessArray[x][y] == chessArray[x + 2][y]                    && chessArray[x][y] == chessArray[x + 3][y] && chessArray[x][y] == chessArray[x + 4][y]) {                return true;            }        }        //判断纵向(从下往上)        if (y + 4 < GRID_NUMBER) {            if (chessArray[x][y] == chessArray[x][y + 1] && chessArray[x][y] == chessArray[x][y + 2]                    && chessArray[x][y] == chessArray[x][y + 3] && chessArray[x][y] == chessArray[x][y + 4]) {                return true;            }        }        //判断斜向(左上到右下)        if (y + 4 < GRID_NUMBER && x + 4 < GRID_NUMBER) {            if (chessArray[x][y] == chessArray[x + 1][y + 1] && chessArray[x][y] == chessArray[x + 2][y + 2]                    && chessArray[x][y] == chessArray[x + 3][y + 3] && chessArray[x][y] == chessArray[x + 4][y + 4]) {                return true;            }        }        //判断斜向(左下到右上)        if (y - 4 > 0 && x + 4 < GRID_NUMBER) {            if (chessArray[x][y] == chessArray[x + 1][y - 1] && chessArray[x][y] == chessArray[x + 2][y - 2]                    && chessArray[x][y] == chessArray[x + 3][y - 3] && chessArray[x][y] == chessArray[x + 4][y - 4]) {                return true;            }        }        return false;    }

checkGameOver

checkGameOver用于判断游戏是否结束,即是否存在五子相连或者平局的情况,如果游戏结束回调游戏结果。这里判断五子相连,是通过遍历chessArray判断是否存在落子的五子相连。平局则是遍历chessArray,判断是否存在空(chessArray[x][y] = 0)的情况,如果不存在则棋盘已满,平局。

/**     * 判断是否结束     */    private void checkGameOver() {        //获取落子的颜色(如果当前是白棋,则落子是黑棋)        int chess = isWhite ? BLACK_CHESS : WHITE_CHESS;        //棋盘是否填满        boolean isFull = true;        //遍历chessArray        for (int i = 0; i < GRID_NUMBER; i++) {            for (int j = 0; j < GRID_NUMBER; j++) {                //判断棋盘是否填满                if (chessArray[i][j] != BLACK_CHESS && chessArray[i][j] != WHITE_CHESS) {                    isFull = false;                }                //只需要判断落子是否五连即可                if (chessArray[i][j] == chess) {                    //判断五子相连                    if (isFiveSame(i, j)) {                        //五子相连游戏结束                        isGameOver = true;                        if (callBack != null) {                            if (chess == WHITE_CHESS) {                                whiteChessCount++;                            } else {                                blackChessCount++;                            }                            callBack.GameOver(chess == WHITE_CHESS ? WHITE_WIN : BLACK_WIN);                        }                        return;                    }                }            }        }        //如果棋盘填满,平局结束        if (isFull) {            isGameOver = true;            if (callBack != null) {                callBack.GameOver(NO_WIN);            }        }    }

resetGame

这里没有太多的东西,就是重置游戏状态。

/**     * 重置游戏     */    public void resetGame() {        isGameOver = false;        //重置棋盘状态        for (int i = 0; i < GRID_NUMBER; i++) {            for (int j = 0; j < GRID_NUMBER; j++) {                chessArray[i][j] = 0;            }        }        //更新UI        postInvalidate();    }

onTouch

onTouch通过点击位置实现落子功能。通过点击位置的坐标,来获取落子的位置,并判断当前位置是否已经有落子(chessArray[x][y] == 0 ?),落子后判断游戏是否结束并更新UI。游戏结束则无法落子。

@Override    public boolean onTouch(View v, MotionEvent event) {        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN:                if (!isGameOver) {                    //获取按下时的位置                    float downX = event.getX();                    float downY = event.getY();                    //点击的位置在棋盘上                    if (downX >= offset / 2 && downX <= len - offset / 2                            && downY >= offset / 2 && downY <= len - offset / 2) {                        //获取棋子对应的位置                        int x = (int) (downX / preWidth);                        int y = (int) (downY / preWidth);                        //判断当前位置是否已经有子                        if (chessArray[x][y] != WHITE_CHESS &&                                chessArray[x][y] != BLACK_CHESS) {                            //给数组赋值                            chessArray[x][y] = isWhite ? WHITE_CHESS : BLACK_CHESS;                            //修改当前执子                            isWhite = !isWhite;                            //更新棋盘                            postInvalidate();                            //判断是否结束                            checkGameOver();                            //回调当前执子                            if (callBack != null) {                                callBack.ChangeGamer(isWhite);                            }                        }                    }                } else {                    Toast.makeText(mContext, "游戏已经结束,请重新开始!",                            Toast.LENGTH_SHORT).show();                }                break;        }        return false;    }

GameCallBack

游戏回调

/** * Created by ZhangHao on 2017/6/27. * 游戏相关回调 */public interface GameCallBack {    //游戏结束回调    void GameOver(int winner);    //游戏更换执子回调    void ChangeGamer(boolean isWhite);}

布局文件

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/back_ground"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:background="@mipmap/bg"    android:orientation="vertical">    <!--游戏信息-->    <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_marginBottom="10dp"        android:orientation="horizontal">        <!--白棋信息-->        <LinearLayout            android:layout_width="0dp"            android:layout_height="wrap_content"            android:layout_weight="2"            android:orientation="vertical">            <ImageView                android:layout_width="20dp"                android:layout_height="20dp"                android:layout_gravity="center_horizontal"                android:layout_marginTop="5dp"                android:background="@drawable/white_chess"                android:contentDescription="@null" />            <TextView                android:id="@+id/white_count_tv"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_gravity="center_horizontal"                android:layout_marginTop="10dp"                android:text="0"                android:textColor="#ffffff"                android:textSize="16sp" />        </LinearLayout>        <RelativeLayout            android:layout_width="0dp"            android:layout_height="wrap_content"            android:layout_weight="1">            <ImageView                android:id="@+id/current_gamer"                android:layout_width="30dp"                android:layout_height="30dp"                android:layout_centerInParent="true"                android:layout_marginTop="30dp"                android:background="@mipmap/vs"                android:contentDescription="@null" />        </RelativeLayout>        <LinearLayout            android:layout_width="0dp"            android:layout_height="wrap_content"            android:layout_weight="2"            android:orientation="vertical">            <ImageView                android:layout_width="20dp"                android:layout_height="20dp"                android:layout_gravity="center_horizontal"                android:layout_marginTop="5dp"                android:background="@drawable/black_chess"                android:contentDescription="@null" />            <TextView                android:id="@+id/black_count_tv"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_gravity="center_horizontal"                android:layout_marginTop="10dp"                android:text="0"                android:textColor="#ffffff"                android:textSize="16sp" />        </LinearLayout>    </LinearLayout>    <!--游戏界面-->    <com.sona.udv.FiveChessView        android:id="@+id/five_chess_view"        android:layout_width="match_parent"        android:layout_height="match_parent" />    <!--重新开始-->    <RelativeLayout        android:layout_width="match_parent"        android:layout_height="match_parent">        <ImageButton            android:id="@+id/restart_game"            android:layout_width="30dp"            android:layout_height="30dp"            android:layout_centerInParent="true"            android:background="@mipmap/restart"            android:contentDescription="@null"            android:onClick="onClick" />    </RelativeLayout></LinearLayout>

Activity

public class MainActivity extends AppCompatActivity implements GameCallBack {    private FiveChessView fiveChessView;    private TextView whiteWinTv,blackWinTv;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        fiveChessView = (FiveChessView) findViewById(R.id.five_chess_view);        fiveChessView.setCallBack(this);        whiteWinTv = (TextView) findViewById(R.id.white_count_tv);        blackWinTv = (TextView) findViewById(R.id.black_count_tv);    }    @Override    public void GameOver(int winner) {        //更新游戏胜利局数        updateWinInfo();        switch (winner) {            case FiveChessView.BLACK_WIN:                showToast("黑棋胜利!");                break;            case FiveChessView.NO_WIN:                showToast("平局!");                break;            case FiveChessView.WHITE_WIN:                showToast("白棋胜利!");                break;        }    }    //更新游戏胜利局数    private void updateWinInfo(){        whiteWinTv.setText(fiveChessView.getWhiteChessCount()+" ");        blackWinTv.setText(fiveChessView.getBlackChessCount()+" ");    }    @Override    public void ChangeGamer(boolean isWhite) {    }    private void showToast(String str) {        Toast.makeText(this, str, Toast.LENGTH_SHORT).show();    }    public void onClick(View view) {        switch (view.getId()) {            case R.id.restart_game:                //重新开始游戏                fiveChessView.resetGame();                break;        }    }}

Activity和布局文件,主要实现了一下功能:
1.添加背景图片
2.增加游戏信息(胜利局数)
3.游戏重开按钮
这里没什么特别需要注意的东西,布局或者背景其实也可以自己重新设置。

结语

1.因为文字功底有限,所以介绍性的文字不多,但是基本上每句代码都加了注释,理解起来应该不难,如果有任何问题,可以留言。
2.人机五子棋对战已经实现:http://blog.csdn.net/a512337862/article/details/76166049