Android实现五子棋游戏(一) 游戏基本逻辑
2017-07-02 23:56
543 查看
转载请注明出处:http://blog.csdn.net/a512337862/article/details/74165085
最近,写一个简单的五子棋游戏,效果如下:
现在其实还算不上一个真正的游戏,因为只是实现了在同一设备上五子棋最基本的逻辑。下面简单介绍一下逻辑,仅供参考。
2.添加背景图片,游戏胜负信息,重开按钮等。
FiveChessView主要实现五子棋游戏的基本逻辑,包括棋子绘制,游戏胜利判断,重开游戏等逻辑:
1.int[][] chessArray来保存游戏的棋子信息,chessArray[x][y] = 0/1/2(0->无子,1->白棋,2->黑棋)表示在(x,y)点的棋子信息,并通过判断chessArray各个位置的棋子来判断胜利。
2.whiteChess,blackChess黑白棋子图片对应的Bitmap。
3.rect用来指定绘制的棋子的大小。
4.添加OnTouchListener
1.横向(从左到右)
2.纵向(从下往上)
3.斜向(左上到右下)
4.斜向(左下到右上)
其他的类似横向(从右往左)这种情况就可以无需判断了。
Activity和布局文件,主要实现了一下功能:
1.添加背景图片
2.增加游戏信息(胜利局数)
3.游戏重开按钮
这里没什么特别需要注意的东西,布局或者背景其实也可以自己重新设置。
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
最近,写一个简单的五子棋游戏,效果如下:
现在其实还算不上一个真正的游戏,因为只是实现了在同一设备上五子棋最基本的逻辑。下面简单介绍一下逻辑,仅供参考。
思路
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进阶之旅------>Android疯狂连连看游戏的实现之实现游戏逻辑(五)
- Android实训案例(八)——单机五子棋游戏,自定义棋盘,线条,棋子,游戏逻辑,游戏状态存储,再来一局
- 我的Android进阶之旅------>Android疯狂连连看游戏的实现之实现游戏逻辑(五)
- Android实训案例(八)——单机五子棋游戏,自定义棋盘,线条,棋子,游戏逻辑,游戏状态存储,再来一局
- Android实现疯狂连连看游戏之实现游戏逻辑(五)
- Android实训案例(八)——单机五子棋游戏,自定义棋盘,线条,棋子,游戏逻辑,游戏状态存储,再来一局
- 【Android游戏开发二十三】自定义ListView【通用】适配器并实现监听控件!
- Android 3D游戏开发(高级篇)——Opengl ES游戏引擎实现(送源代码)
- 详解如何实现一个基本的Android界面--讲的很好
- Android/Ophone平台2D游戏引擎实现系列文章 推荐
- 【贪吃蛇—Java程序员写Android游戏】系列 3. 用J2ME实现Android的Snake Sample详解
- 【Android游戏开发十六】Android Gesture之【触摸屏手势识别】操作!利用触摸屏手势实现一个简单切换图片的功能!
- Android 3D游戏开发(基础篇)——Opengl ES游戏引擎实现(送源码)
- 飞行射击的最基本算法实现 躲子弹的游戏
- Android扫雷游戏实现
- 【贪吃蛇—Java程序员写Android游戏】系列 2. 用J2ME实现Android的Snake Sample预览
- 【Android游戏开发二十二】(图文详解)游戏中灵活实现动画播放!简述J2me的游戏类库与Android游戏开发!
- 非典型2D游戏引擎 Orx 源码阅读笔记(4) 用C实现的基本容器(List,HashTable,Tree)
- Android游戏开发2种FPS 实现比较
- 【Android游戏开发之五】游戏注册界面Demo-实现两个Activity之间的切换与数据交互!