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
- Android实现五子棋游戏(一) 游戏基本逻辑
- Android实现五子棋游戏(二) 人机对战实现
- Android实现拖拉功能(五子棋等游戏中用到)
- Android五子棋游戏设计与实现
- oc 实现五子棋游戏
- C++实现五子棋游戏
- android五子棋游戏源码
- android studio 五子棋游戏
- 五子棋游戏开发点滴-(一)
- Android游戏开发学习(一):游戏的基本架构
- Android实训案例(八)——单机五子棋游戏,自定义棋盘,线条,棋子,游戏逻辑,游戏状态存储,再来一局
- Android实训案例(八)——单机五子棋游戏,自定义棋盘,线条,棋子,游戏逻辑,游戏状态存储,再来一局
- 谈谈我们的游戏逻辑服务器实现(一)
- 自定义View实现五子棋游戏
- Android五子棋游戏源码详解
- 五子棋游戏
- 五子棋游戏
- 五子棋游戏
- 每个Linux开发者都应该知道的一些知识
- Spring IOC笔记
- Linux学习之关机重启命令|系统运行级别|退出登陆
- 一起学JAVA之《spring boot》05
- 记录c3p0连接池有趣的小错误
- Android实现五子棋游戏(一) 游戏基本逻辑
- 走进Java Android 的线程世界
- 39《黑客与画家 : 硅谷创业之父Paul Graham文集》 -豆瓣评分8.8
- struts2 2.1升级到2.3需要注意的一个小细节
- 自旋锁总结
- GC对象的判定和GC算法
- C++笔记之【Webservice调用】的那些事
- 为什么要使用SLF4J而不是Log4J
- 放假感悟