转载请注明出处:http://blog.csdn.net/sw950729/article/details/51942858
本文出自:马云飞的博客
当初学编程的,都想做一个游戏,俄罗斯方块?贪吃蛇?不不不,今天我所讲的是五子棋双人对战,对比那些来说,应该算比较简单的了。好了,先贴上视频地址http://www.imooc.com/learn/641。
相信很多人都有看过,不过视频中,我发现有些代码是不需要的,而且还存在一些bug,针对视频所存在的bug我都做了调整,经测试,目前无bug。
下面先贴代码:
所需定义的内容:
//画笔
private Paint paint;
//画布的宽度
private int PanelWidth;
//最大行数
private static final int MAX_LINE = 15;
//设置棋子的大小为棋盘格子的3/4
private static final float Size = 3 * 1.0f / 4;
//格子的高度(必须是float)
private float SingelHeight;
//黑白棋的素材
private Bitmap WhiteBitmap;
private Bitmap BlackBitmap;
//判断谁先出棋(一般白先手)
private boolean IsWhite = true;
//存放黑白棋子坐标
private ArrayList WhitePoint = new ArrayList<>();
private ArrayList BlackPoint = new ArrayList<>();
private boolean IsGameOver = false;
//五子连珠算赢
private static final int FIVE_POINT = 5;
对画笔的初始化:
private void inital() {
paint = new Paint();
paint.setColor(0x88000000);
paint.setAntiAlias(true);//抗锯齿
paint.setDither(true);//防抖动
paint.setStyle(Paint.Style.FILL);
WhiteBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.white);
BlackBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.black);
}
对于自定义view有一定基础的小伙伴都知道,自定义一般都要实现2个方法,1:onSizeChanged(视图大小的改变);2:onMeasure(告诉父view,子视图占用多大的空间)
下面上代码,我都有详细注解,就不一个一个解释了:
onMeasure:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
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);
//AT_MOST:specSize 代表的是最大可获得的空间;
//EXACTLY:specSize 代表的是精确的尺寸;
//UNSPECIFIED:对于控件尺寸来说,没有任何参考意义。
//解决嵌套在ScrollView中时等情况出现的问题
if (widthMode == MeasureSpec.UNSPECIFIED) {
width = heightSize;
} else if (heightMode == MeasureSpec.UNSPECIFIED) {
width = widthSize;
}
setMeasuredDimension(width, width);
}
onSizeChanged:
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
PanelWidth = w;
SingelHeight = PanelWidth * 1.0f / MAX_LINE;
//根据实际的棋盘格子的宽度按照一定的比例缩小棋子
int onlyWidth = (int) (SingelHeight * Size);
WhiteBitmap = Bitmap.createScaledBitmap(WhiteBitmap, onlyWidth, onlyWidth, false);
BlackBitmap = Bitmap.createScaledBitmap(BlackBitmap, onlyWidth, onlyWidth, false);
}
好了,下面我们进图画图操作,需要的操作是,先画棋盘,在画棋子,然后判断游戏结束。
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
DrawBoard(canvas);
DrawPiece(canvas);
IsGameOver();
}
绘制棋盘:
//绘制棋盘
//因为棋子的中心是在棋盘的点上的,所以上下左右有个边距,一般设为1/2
private void DrawBoard(Canvas canvas) {
for (int i = 0; i < MAX_LINE; i++) {
int startX = (int) SingelHeight / 2;
int endX = (int) (PanelWidth - SingelHeight / 2);
int y = (int) ((0.5 + i) * SingelHeight);
canvas.drawLine(startX, y, endX, y, paint);//画横线
canvas.drawLine(y, startX, y, endX, paint);//画竖线
}
}
绘制棋子:
private void DrawPiece(Canvas canvas) {
for (int i = 0, n = WhitePoint.size(); i < n; i++) {
Point whitePoint = WhitePoint.get(i);
canvas.drawBitmap(WhiteBitmap,
(whitePoint.x + (1 - Size) / 2) * SingelHeight,
(whitePoint.y + (1 - Size) / 2) * SingelHeight, null);
}
for (int i = 0, n = BlackPoint.size(); i < n; i++) {
Point blackPoint = BlackPoint.get(i);
canvas.drawBitmap(BlackBitmap,
(blackPoint.x + (1 - Size) / 2) * SingelHeight,
(blackPoint.y + (1 - Size) / 2) * SingelHeight, null);
}
}
判断游戏是否结束:
//游戏是否结束
private void IsGameOver() {
boolean WhiteWin = checkFiveInLine(WhitePoint);
boolean BlackWin = checkFiveInLine(BlackPoint);
boolean NoWin = checkNoWin(WhiteWin, BlackWin);
if (WhiteWin) {
IsGameOver = true;
Dialog("白棋获胜!");
} else if (BlackWin) {
IsGameOver = true;
Dialog("黑棋获胜");
} else if (NoWin) {
IsGameOver = true;
Dialog("针锋相对,和棋了!");
}
}
游戏结果,为了开始新的一局(以及悔棋):
public boolean GetGameResult() {
return IsGameOver;
}
public int GetPieceSize() {
if (WhitePoint.size() == 0 && BlackPoint.size() == 0) {
return 0;
}
return 1;
}
private boolean checkNoWin(boolean whiteWin, boolean blackWin) {
if (whiteWin || blackWin) {
return false;
}
int max = MAX_LINE * MAX_LINE;
//如果白棋和黑棋的总数等于棋盘格子数,说明和棋
if (WhitePoint.size() + BlackPoint.size() == max) {
return true;
}
return false;
}
//重新开始
public void restart() {
WhitePoint.clear();
BlackPoint.clear();
IsGameOver = false;
IsWhite=true;
invalidate();
}
//悔棋
public void regret() {
if (BlackPoint.size() > 0 || WhitePoint.size() > 0) {
if (IsWhite) {
BlackPoint.remove(BlackPoint.size() - 1);
IsWhite = !IsWhite;
} else {
WhitePoint.remove(WhitePoint.size() - 1);
IsWhite = !IsWhite;
}
invalidate();
}
}
弹出的dialog,这里你们可以自由改变:
private void Dialog(String string) {
new AlertDialog.Builder(getContext())
.setTitle(string)
.setPositiveButton("重来",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {
restart();
}
})
.setNeutralButton("查看棋盘!", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
}
})
.setNegativeButton("退出",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {
MainActivity.getMainActivity().finish();
}
}).show();
}
判断五子是否相连:
private boolean checkFiveInLine(List point) {
for (Point p : point) {
int x = p.x;
int y = p.y;
boolean checkHorizontal = checkHorizontalFiveInLine(x, y, point);
boolean checkVertical = checkVerticalFiveInLine(x, y, point);
boolean checkLeftDiagonal = checkLeftFiveInLine(x, y, point);
boolean checkRightDiagonal = checkRightFiveInLine(x, y, point);
if (checkHorizontal || checkVertical || checkLeftDiagonal || checkRightDiagonal) {
return true;
}
}
return false;
}
这里我只贴一个,其他一样的,就是改变x,y的坐标而已:
//横向五子连珠
private boolean checkHorizontalFiveInLine(int x, int y, List point) {
int count = 1;
for (int i = 1; i < FIVE_POINT; i++) {
if (point.contains(new Point(x - i, y))) {
count++;
} else {
break;
}
}
if (count == FIVE_POINT) {
return true;
}
return false;
}
这里,你们应该发现,我和视频中的判断是不一样的,比他少了判断右边的,这样只是判断次数多了一点,如果右边也判断的话,在人机对战中,我可以说,你们会疯掉。。。
好了,接下来是点击事件,然后开始下子:
public boolean onTouchEvent(MotionEvent event) {
if (IsGameOver) {
return false;
}
if (event.getAction() == MotionEvent.ACTION_UP) {
int x = (int) event.getX();
int y = (int) event.getY();
Point p = getValidPoint(x, y);
if (WhitePoint.contains(p) || BlackPoint.contains(p)) {
return false;
}
if (IsWhite) {
WhitePoint.add(p);
} else {
BlackPoint.add(p);
}
invalidate();
IsWhite = !IsWhite;
return true;
}
return true;
}
棋子的落点位置:
private Point getValidPoint(int x, int y) {
return new Point((int) (x / SingelHeight), (int) (y / SingelHeight));
}
这样一个简单的五子棋功能就实现了,有人会说为什么不保存数据?其实我们不需要保存数据,我们只要让视图全屏就ok了。
代码如下:
<resources>
<style name="AppTheme" parent="android:Theme.Translucent.NoTitleBar.Fullscreen">
style>
resources>
这里我没有考虑到禁手的问题,感觉如果考虑到禁手的话,算法比较复杂,人机对战,上周我花了一周的时间也差不多解决了,打草稿就花了差不多10多张纸~~~功能没什么问题了,有空还在找bug。后期我会贴上人机对战的算法以及app。不过代码就不贴了。因为我写的比较复杂。而且如果没思路的话,把代码给你,你也要看好半天才能理解。
好了,今天就说到这边把,我的android交流群:232748032。欢迎新手,大神的加入。