欢迎光临
我们一直在努力

Android五子棋游戏源码详解

最近看了鸿洋大牛的五子棋教程,受益匪浅,讲的非常好,关于五子棋的游戏原理非常清楚,并且学到了不少知识,在这里感谢鸿洋大神的分享。我觉得我的源码注释写的非常清楚了,希望能给你带来不少的收获。点击获取源码下载地址
布局

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@mipmap/bg" android:paddingTop="80dp" android:orientation="vertical" tools:context="com.yaodan.fivechessdemo.MainActivity"> <com.yaodan.fivechessdemo.view.ChessView android:id="@+id/custon_chess_main" android:layout_width="match_parent" android:layout_height="match_parent" /> <Button android:id="@+id/bt_restart" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="再来一局"/> </LinearLayout> 

自定义View的工具类

package com.yaodan.fivechessdemo.view; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Point; import android.os.Bundle; import android.os.Parcelable; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import com.yaodan.fivechessdemo.R; import com.yaodan.fivechessdemo.contral.IsChessWin; import java.util.ArrayList; /** * Created by swk on 2016/12/5. */ public class ChessView extends View { private int mPanelWith; //棋盘的宽度(棋盘使方形的) private float mLineHeigth; //棋盘每一个空格的高度 private int MAX_LINE = 10; //棋盘的行数 private Paint mPint = new Paint(); //创建画笔 private Bitmap wPieces; //白棋 private Bitmap bPieces; //黑棋 private ArrayList<Point> wPoints = new ArrayList<>(); //白棋坐标的集合 private ArrayList<Point> bPoints = new ArrayList<>(); //黑棋坐标的集合 private float radioPoeces = 1.0f * 3 / 4; //棋子与棋格的大小比例 private boolean mIsWitch = true; //判断是否白子画在棋盘上 private boolean isGameOver = false; //判读是否游戏结束 private Context mContext; private IsChessWin isChessWin; //一个用来处理胜利与否的逻辑 private String TAG = "CHESSVIEW"; public ChessView(Context context) { super(context); mContext = context; init(); } public ChessView(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; init(); } public ChessView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mContext = context; init(); } /** * 再来一局方法 */ public void myreStart() { wPoints.clear(); bPoints.clear(); isGameOver = false; Log.i(TAG, "myreStart: " + wPoints.size() + ":::" + bPoints.size()); invalidate(); } /** * 初始化 */ public void init() { //设置画笔的颜色 mPint.setColor(Color.BLACK); //设置抗锯齿功能(图像边缘相对清晰一点,锯齿痕迹不那么明显) mPint.setAntiAlias(true); //设置防抖动功能(使图像更柔和一点) mPint.setDither(true); //设置画笔的风格为空心 mPint.setStyle(Paint.Style.STROKE); //获取棋子的资源文件 wPieces = BitmapFactory.decodeResource(getResources(), R.drawable.stone_w2); bPieces = BitmapFactory.decodeResource(getResources(), R.drawable.stone_b1); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); /** * 获取view的宽度和mode * mode分为: * EXACTLY:EXACTLY是精确尺寸,当我们将控件的layout_width或layout_height指定为具体数值时如andorid:layout_width="50dip",或者为FILL_PARENT是,都是控件大小已经确定的情况,都是精确尺寸。 * AT_MOST:最大尺寸,当控件的layout_width或layout_height指定为WRAP_CONTENT时,控件大小一般随着控件的子空间或内容进行变化,此时控件尺寸只要不超过父控件允许的最大尺寸即可。因此,此时的mode是AT_MOST,size给出了父控件允许的最大尺寸。 * UNSPECIFIED:未指定尺寸,这种情况不多,一般都是父控件是AdapterView,通过measure方法传入的模式 */ int widthSize = MeasureSpec.getSize(widthMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); //由于棋盘是正方形,所以要从长和宽选取最短的 int width = Math.min(widthSize, heightSize); //如果上方有一个是UNSPECIFIED,相对应的有一个尺寸是0,如果有一个是0,那么width就是0显示不出来 if (widthMode == MeasureSpec.UNSPECIFIED) { width = heightSize; } else if (heightMode == MeasureSpec.UNSPECIFIED) { width = widthSize; } //设置实际的长和宽设置上去 setMeasuredDimension(width, width); } /** * 当view的尺寸改变时,会回掉这个方法 * * @param w * @param h * @param oldw * @param oldh */ @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mPanelWith = w; mLineHeigth = mPanelWith * 1.0f / MAX_LINE; int piecesWidth = (int) (mLineHeigth * radioPoeces); //按照以前存在的位图按照一定的比例构建一个新的位图 wPieces = Bitmap.createScaledBitmap(wPieces, piecesWidth, piecesWidth, true); bPieces = Bitmap.createScaledBitmap(bPieces, piecesWidth, piecesWidth, true); } /** * 获取坐标的集合 * * @param event * @return */ @Override public boolean onTouchEvent(MotionEvent event) { isGameOver = isChessWin.isGameOverMethod(wPoints, bPoints); if (isGameOver) { showDialog(); return false; } int action = event.getAction(); if (action == MotionEvent.ACTION_UP) { int x = (int) event.getX(); int y = (int) event.getY(); Point point = getSimulatePoint(x, y); //如果黑棋的集合或者白棋的集合包含这个坐标,那么返回false //contains和eequals比较的不是内存空间的地址,而是x,y值是否一致 if (wPoints.contains(point) || bPoints.contains(point)) { return false; } if (mIsWitch) { wPoints.add(point); } else { bPoints.add(point); } mIsWitch = !mIsWitch; //刷新View invalidate(); return true; } return true; } /** * 根据真实的坐标模拟出绝对值坐标 * * @param x * @param y * @return */ public Point getSimulatePoint(int x, int y) { return new Point((int) (x / mLineHeigth), (int) (y / mLineHeigth)); } /** * 显示白棋或者黑棋获胜的提示的对话框 */ public void showDialog() { String successText = isChessWin.isWhiteWinFlag() ? "白棋获胜!" : "黑棋获胜!"; new AlertDialog.Builder(mContext) .setMessage("恭喜" + successText + ",是否再来一局?") .setCancelable(false) .setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { myreStart(); } }) .setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { } }) .show(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); drawBoard(canvas); drawPieces(canvas); //下面的实例用来判断是否胜利 isChessWin = new IsChessWin(mContext); isGameOver = isChessWin.isGameOverMethod(wPoints, bPoints); //判断是否结束游戏 if(isGameOver){ showDialog(); } } /** * 画棋盘的线 * * @param canvas */ public void drawBoard(Canvas canvas) { int w = mPanelWith; float lineHeight = mLineHeigth; //画十条线 for (int i = 0; i < MAX_LINE; i++) { //设置起点横坐标为半个棋盘空格的宽度 int startX = (int) (lineHeight / 2); //设置终点X横坐标为宽度减去半个lineHeight(棋盘空格宽度) int endX = (int) (w - lineHeight / 2); int y = (int) ((0.5 + i) * lineHeight); //画横线 canvas.drawLine(startX, y, endX, y, mPint); //画纵线,坐标反过来 canvas.drawLine(y, startX, y, endX, mPint); } } /** * 画棋子 */ public void drawPieces(Canvas canvas) { Log.i(TAG, "drawPieces: " + wPoints.size() + ":::" + bPoints.size()); for (int i = 0; i < wPoints.size(); i++) { Point point = wPoints.get(i); //drawBitmap是将图片的右下角为坐标 canvas.drawBitmap(wPieces, ((point.x + (1 - radioPoeces) / 2) * mLineHeigth), (point.y + (1 - radioPoeces) / 2) * mLineHeigth, null); } for (int i = 0; i < bPoints.size(); i++) { Point point = bPoints.get(i); canvas.drawBitmap(bPieces, ((point.x + (1 - radioPoeces) / 2) * mLineHeigth), (point.y + (1 - radioPoeces) / 2) * mLineHeigth, null); } } private static final String INSTANCE = "instance"; private static final String INSTANCE_GAMEOVER = "instance_gameover"; private static final String INSTANCE_WHITEARRAY = "instance_whitearray"; private static final String INSTANCE_BLACKARRAY = "instance_blackarray"; /** * 当view因为某种原因(比如系统回收)销毁时,保存状态 * * @return */ @Override protected Parcelable onSaveInstanceState() { Bundle bundle = new Bundle(); //保存系统默认状态 bundle.putParcelable(INSTANCE, super.onSaveInstanceState()); //保存是否游戏结束的值 bundle.putBoolean(INSTANCE_GAMEOVER, isGameOver); //保存白棋的子数 bundle.putParcelableArrayList(INSTANCE_WHITEARRAY, wPoints); //保存黑棋的子数 bundle.putParcelableArrayList(INSTANCE_BLACKARRAY, bPoints); return bundle; } /** * 取出保存的值 * * @param state */ @Override protected void onRestoreInstanceState(Parcelable state) { if (state instanceof Bundle) { Bundle bundle = (Bundle) state; isGameOver = bundle.getBoolean(INSTANCE_GAMEOVER); wPoints = bundle.getParcelableArrayList(INSTANCE_WHITEARRAY); bPoints = bundle.getParcelableArrayList(INSTANCE_BLACKARRAY); super.onRestoreInstanceState(bundle.getParcelable(INSTANCE)); return; } super.onRestoreInstanceState(state); } } 

判断是否胜利的类

package com.yaodan.fivechessdemo.contral; import android.content.Context; import android.graphics.Point; import java.util.List; /** * Created by swk on 2016/12/5. * 判断是否胜利类 */ public class IsChessWin { private boolean isGameOver = false; //判断是否游戏结束 private boolean isWhiteWin; //判断是否白棋胜 private int MAX_NUMWIN = 5; //设置5子连在一起胜利 private int CURRENT_NUM = 0; private Context mContext; private boolean isRestart=false; public IsChessWin(Context context) { super(); mContext = context; } /** * 判断是否胜利 * @param whitePoints * @param blackPoints * @return */ public boolean isGameOverMethod(List<Point> whitePoints, List<Point> blackPoints) { boolean whiteWin = isWhiteWin(whitePoints); boolean blackWin = isBlackWin(blackPoints); if (whiteWin || blackWin) { isGameOver = true; isWhiteWin = whiteWin; } return isGameOver; } /** * 返回是否白棋取胜 * @return */ public boolean isWhiteWinFlag(){ return isWhiteWin; } /** * 判断是否白棋取胜 * @param points * @return */ private boolean isWhiteWin(List<Point> points) { if (isFiveConnect(points)) { return true; } return false; } /** * 判断是否黑棋取胜 * @param points * @return */ private boolean isBlackWin(List<Point> points) { if (isFiveConnect(points)) { return true; } return false; } /** * 判断是否五子连珠 * @param points * @return */ private boolean isFiveConnect(List<Point> points) { for (Point p : points) { int x = p.x; int y = p.y; if (isHorizontalFive(x, y, points)) { return true; } else if (isVerticalFive(x, y, points)) { return true; } else if (isSkewFive(x, y, points)) { return true; } } return false; } /** * 判断是否横向五子连珠 * @param x * @param y * @param points * @return */ private boolean isHorizontalFive(int x, int y, List<Point> points) { //判断横向向右是否练成5子,points里面存的值为int类型,所以可以进行加一或者减一的运算 for (int i = 0; i < MAX_NUMWIN; i++) { if (points.contains(new Point(x + i, y))) { CURRENT_NUM++; } else { break; } } if (MAX_NUMWIN == CURRENT_NUM) { return true; } else { CURRENT_NUM = 0; } //判断横向向左是否连成5子 for (int i = 0; i < MAX_NUMWIN; i++) { if (points.contains(new Point(x - i, y))) { CURRENT_NUM++; } else { break; } } if (MAX_NUMWIN == CURRENT_NUM) { return true; } else { CURRENT_NUM = 0; } return false; } /** * 判断是否竖直五子连珠 * @param x * @param y * @param points * @return */ private boolean isVerticalFive(int x, int y, List<Point> points) { for (int i = 0; i < MAX_NUMWIN; i++) { //判断向下是否5子连珠 if (points.contains(new Point(x, y + i))) { CURRENT_NUM++; } else { break; } } if (MAX_NUMWIN == CURRENT_NUM) { return true; } else { CURRENT_NUM = 0; } //判断向上是否5子连珠 for (int i = 0; i < MAX_NUMWIN; i++) { if (points.contains(new Point(x, y - i))) { CURRENT_NUM++; if (5 == CURRENT_NUM) { return true; } } else { CURRENT_NUM = 0; break; } } if (MAX_NUMWIN == CURRENT_NUM) { return true; } else { CURRENT_NUM = 0; } return false; } /** * 判断斜着是否五子连珠 * @param x * @param y * @param points * @return */ private boolean isSkewFive(int x, int y, List<Point> points) { //判断左斜下是否5子连珠 for (int i = 0; i < MAX_NUMWIN; i++) { if (points.contains(new Point(x - i, y + i))) { CURRENT_NUM++; } else { break; } } if (MAX_NUMWIN == CURRENT_NUM) { return true; } else { CURRENT_NUM = 0; } //判断左上是否5子连珠 for (int i = 0; i < MAX_NUMWIN; i++) { if (points.contains(new Point(x - i, y - i))) { CURRENT_NUM++; } else { break; } } if (MAX_NUMWIN == CURRENT_NUM) { return true; } else { CURRENT_NUM = 0; } //判断右上是否5子连珠 for (int i = 0; i < MAX_NUMWIN; i++) { if (points.contains(new Point(x + i, y - i))) { CURRENT_NUM++; } else { break; } } if (MAX_NUMWIN == CURRENT_NUM) { return true; } else { CURRENT_NUM = 0; } //判断右斜上是否5子连珠 for (int i = 0; i < MAX_NUMWIN; i++) { if (points.contains(new Point(x + i, y - i))) { CURRENT_NUM++; } else { break; } } if(MAX_NUMWIN==CURRENT_NUM){ return true; }else{ CURRENT_NUM=0; } //判断右斜下是否5子连珠 for (int i = 0; i < MAX_NUMWIN; i++) { if (points.contains(new Point(x + i, y + i))) { CURRENT_NUM++; } else { break; } } if(MAX_NUMWIN==CURRENT_NUM){ return true; }else{ CURRENT_NUM=0; } return false; } } 

主方法的调用

package com.yaodan.fivechessdemo; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import com.yaodan.fivechessdemo.view.ChessView; public class MainActivity extends AppCompatActivity implements View.OnClickListener { private Button btn_reatart; private ChessView chessView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn_reatart = (Button) findViewById(R.id.bt_restart); chessView= (ChessView) findViewById(R.id.custon_chess_main); btn_reatart.setOnClickListener(this); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.bt_restart: chessView.myreStart(); break; } } } 

  • 海报
海报图正在生成中...
赞(0) 打赏
声明:
1、本博客不从事任何主机及服务器租赁业务,不参与任何交易,也绝非中介。博客内容仅记录博主个人感兴趣的服务器测评结果及一些服务器相关的优惠活动,信息均摘自网络或来自服务商主动提供;所以对本博客提及的内容不作直接、间接、法定、约定的保证,博客内容也不具备任何参考价值及引导作用,访问者需自行甄别。
2、访问本博客请务必遵守有关互联网的相关法律、规定与规则;不能利用本博客所提及的内容从事任何违法、违规操作;否则造成的一切后果由访问者自行承担。
3、未成年人及不能独立承担法律责任的个人及群体请勿访问本博客。
4、一旦您访问本博客,即表示您已经知晓并接受了以上声明通告。
文章名称:《Android五子棋游戏源码详解》
文章链接:https://www.456zj.com/40977.html
本站资源仅供个人学习交流,请于下载后24小时内删除,不允许用于商业用途,否则法律问题自行承担。

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址