您的位置:首页 > 移动开发 > Android开发

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

2017-07-02 23:56 543 查看
转载请注明出处: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)
11d71
;
//获取宽高中较小的值
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

3. Demo下载地址:http://download.csdn.net/detail/a512337862/9911855

4. Github:https://github.com/LuoChen-Hao/GameFiveChess
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 五子棋 游戏
相关文章推荐