android开发 自定义view实现黑白棋子游戏

1.最近写了个棋盘游戏,大致实现如下功能:棋盘设定(n子棋,m*m棋盘)、重玩、后退(悔棋)、前进、保存、加载保存的棋盘状态。

2.先上最终效果图:

 

3.思路:第一步先画棋盘

int width = getWidth();
int height = getHeight();
mChildWidth = width / Max;
mChildHeight = height / Max;

//for (int i = 1; i < Max; i++) {
   canvas.drawLine(mChildWidth * i, 0, mChildWidth * i, height, mLinePaint);
}
//for (int i = 1; i < Max; i++) {
   canvas.drawLine(0, mChildHeight * i, width, mChildHeight * i, mLinePaint);
}
第二步,画黑白棋子,此时必须监听onTouch事件,将棋手所点的坐标转化为二维数组(比如第一行第一列即为0,0位置。。。以此类推)

/**
 * 获取下棋子的位置(数组下标)
 *
 * @param event
 */
private Point getPoint(MotionEvent event) {
   float fx = event.getX();
   float fy = event.getY();
   int x = 0, y = 0;
   for (int i = 0; i < Max; i++) {
      if (fx < mChildWidth * (i + 1)) {
         x = i;
         break;
      }
   }
   for (int i = 0; i < Max; i++) {
      if (fy < mChildHeight * (i + 1)) {
         y = i;
         break;
      }
   }

   Point point = new Point(x, y);
   return point;
}

/**
 * 获取下棋子的位置(坐标)
 *
 * @param point
 */
private Position getPosition(Point point) {
   float x = 0, y = 0;

   for (int i = 0; i < Max; i++) {
      if (point.getX() == i) {
         x = i * mChildWidth + mChildWidth / 2;
      }
   }

   for (int i = 0; i < Max; i++) {
      if (point.getY() == i) {
         y = i * mChildHeight + mChildHeight / 2;
      }
   }
   Position position = null;
   if (mChessPieces == -1) {
      position = new Position(x, y, Color.BLACK, mChessPieces, point);
   } else {
      position = new Position(x, y, Color.WHITE, mChessPieces, point);
   }

   return position;
}

public class Point implements Serializable {
   public int x;
   public int y;

   public Point(int x, int y) {
      this.x = x;
      this.y = y;
   }

   public int getX() {
      return x;
   }

   public Point setX(int x) {
      this.x = x;
      return this;
   }

   public int getY() {
      return y;
   }

   public Point setY(int y) {
      this.y = y;
      return this;
   }

   // @Override
   // public int hashCode() {
   //    return x + y;
   // }

   @Override
   public boolean equals(Object obj) {
      if (this == obj)
         return true;
      Point other = (Point) obj;
      if (x != other.x)
         return false;
      if (y != other.y)
         return false;
      return true;
   }

}

@Override
public boolean onTouchEvent(MotionEvent event) {
   if (canTouch && event.getAction() == MotionEvent.ACTION_DOWN) {
      mPositionUndoList.clear();//一旦重新下棋子,不能redo
      Point point = getPoint(event);
      if (mScore[point.getY()][point.getX()].getScore() == 0) {
         Position position = getPosition(point);
         mPositionList.add(position);
         mScore[point.getY()][point.getX()] = position;
         if (mChessPieces == -1) {
            mBlackPositionList.add(position);
            calculator(mBlackPositionList, "黑子");
         } else if (mChessPieces == 1) {
            mWhitePositionList.add(position);
            calculator(mWhitePositionList, "白子");
         }
         postInvalidate();
         exchangeChess();
         event.setAction(MotionEvent.ACTION_CANCEL);
      } else {
         //有棋子
         onResultListener.onError("当前位置有棋子,换个位置!");
      }
   }


   return super.onTouchEvent(event);
}

/**
 * 换棋手
 */
private void exchangeChess() {
   if (mChessPieces == -1) {
      mChessPieces = 1;
      mCirclePaint.setColor(Color.WHITE);
      if (onCurrentColorListener != null)
         onCurrentColorListener.onCurrentColor("白子");
   } else {
      mChessPieces = -1;
      mCirclePaint.setColor(Color.BLACK);
      if (onCurrentColorListener != null)
         onCurrentColorListener.onCurrentColor("黑子");
   }
}

第三步,也就是本游戏的核心,计算输赢的算法。基本思想就是每次棋手下棋子后,就去计算横、竖、斜杠、反斜杠四个方向同一颜色的棋子的个数是否到达n子棋,若到达,即为相应的棋手方获胜,反之继续,直至棋子总数==棋盘m*m数时,平手。

/**
 * 计算输赢
 */
private void calculator(List<Position> myChessPosition, String color) {
   mSuccessPointList.clear();
   if (myChessPosition.size() < COUNT) {
      return;
   }
   if (color.equals("黑子")) {
      mColor = Color.BLACK;
   } else {
      mColor = Color.WHITE;
   }
   List<Point> myPointList = new ArrayList<>();
   Point temPoint = new Point(0, 0);
   for (int i = 0; i < myChessPosition.size(); i++) {
      myPointList.add(myChessPosition.get(i).getPoint());
   }
   int count = 0;
   Point point = myPointList.get(myPointList.size() - 1);//获取最后下的棋子的位置
   int x = point.getX();
   int y = point.getY();
   temPoint.setX(x).setY(y);
   //   while (myPointList.contains(temPoint)) {
      mSuccessPointList.add(new Point(temPoint.getX(), temPoint.getY()));
      count++;
      int tempX = temPoint.getX();
      if (!(tempX < Max) || count >= COUNT) {
         break;
      }
      temPoint.setX(tempX + 1);
   }
   if (count == COUNT) {
      resultMsg = "本局结束," + color + "胜!!!";
      new Thread(new CircleThread()).start();
      return;
   }
   temPoint.setX(x).setY(y);
   mSuccessPointList.remove(0);
   count--;//重复算了x,y这点
   while (myPointList.contains(temPoint)) {
      mSuccessPointList.add(new Point(temPoint.getX(), temPoint.getY()));
      count++;
      int tempX = temPoint.getX();
      if (tempX < 1 || count >= COUNT) {
         break;
      }
      temPoint.setX(tempX - 1);
   }
   if (count == COUNT) {
      resultMsg = "本局结束," + color + "胜!!!";
      new Thread(new CircleThread()).start();
      return;
   }
   //   mSuccessPointList.clear();
   count = 0;
   temPoint.setX(x).setY(y);
   while (myPointList.contains(temPoint)) {
      mSuccessPointList.add(new Point(temPoint.getX(), temPoint.getY()));
      count++;
      int tempY = temPoint.getY();
      Log.d("+", "count=" + count + ",tempY=" + tempY);
      if (!(tempY < Max) || count >= COUNT) {
         break;
      }
      temPoint.setY(tempY + 1);
   }
   if (count == COUNT) {
      resultMsg = "本局结束," + color + "胜!!!";
      new Thread(new CircleThread()).start();
      return;
   }
   mSuccessPointList.remove(0);
   temPoint.setX(x).setY(y);
   count--;//重复算了x,y这点
   while (myPointList.contains(temPoint)) {
      mSuccessPointList.add(new Point(temPoint.getX(), temPoint.getY()));
      count++;
      int tempY = temPoint.getY();
      Log.d("-", "count=" + count + ",tempY=" + tempY);
      if (tempY < 1 || count >= COUNT) {
         break;
      }
      temPoint.setY(tempY - 1);
   }
   if (count == COUNT) {
      resultMsg = "本局结束," + color + "胜!!!";
      new Thread(new CircleThread()).start();
      return;
   }
   //正斜向 /
   mSuccessPointList.clear();
   count = 0;
   temPoint.setX(x).setY(y);
   while (myPointList.contains(temPoint)) {
      mSuccessPointList.add(new Point(temPoint.getX(), temPoint.getY()));
      count++;
      int tempX = temPoint.getX();
      int tempY = temPoint.getY();
      Log.d("+", "count=" + count + ",tempY=" + tempY);
      if (tempY < 1 || !(tempX < Max) || count >= COUNT) {
         break;
      }
      temPoint.setX(tempX + 1).setY(tempY - 1);
   }
   if (count == COUNT) {
      resultMsg = "本局结束," + color + "胜!!!";
      new Thread(new CircleThread()).start();
      return;
   }
   mSuccessPointList.remove(0);
   temPoint.setX(x).setY(y);
   count--;
   while (myPointList.contains(temPoint)) {
      mSuccessPointList.add(new Point(temPoint.getX(), temPoint.getY()));
      count++;
      int tempX = temPoint.getX();
      int tempY = temPoint.getY();
      Log.d("+", "count=" + count + ",tempY=" + tempY);
      if (tempX < 1 || !(tempY < Max) || count >= COUNT) {
         break;
      }
      temPoint.setX(tempX - 1).setY(tempY + 1);
   }
   if (count == COUNT) {
      resultMsg = "本局结束," + color + "胜!!!";
      new Thread(new CircleThread()).start();
      return;
   }
   //反斜向 \
   mSuccessPointList.clear();
   count = 0;
   temPoint.setX(x).setY(y);
   while (myPointList.contains(temPoint)) {
      mSuccessPointList.add(new Point(temPoint.getX(), temPoint.getY()));
      count++;
      int tempX = temPoint.getX();
      int tempY = temPoint.getY();
      Log.d("+", "count=" + count + ",tempY=" + tempY);
      if (!(tempY < Max) || !(tempX < Max) || count >= COUNT) {
         break;
      }
      temPoint.setX(tempX + 1).setY(tempY + 1);
   }
   if (count == COUNT) {
      resultMsg = "本局结束," + color + "胜!!!";
      new Thread(new CircleThread()).start();
      return;
   }
   mSuccessPointList.remove(0);
   temPoint.setX(x).setY(y);
   count--;
   while (myPointList.contains(temPoint)) {
      mSuccessPointList.add(new Point(temPoint.getX(), temPoint.getY()));
      count++;
      int tempX = temPoint.getX();
      int tempY = temPoint.getY();
      Log.d("+", "count=" + count + ",tempY=" + tempY);
      if (tempX < 1 || tempY < 1 || count >= COUNT) {
         break;
      }
      temPoint.setX(tempX - 1).setY(tempY - 1);
   }
   if (count == COUNT) {
      resultMsg = "本局结束," + color + "胜!!!";
      new Thread(new CircleThread()).start();
      return;
   }
   if (mPositionList.size() == Max * Max) {
      resultMsg = "本局结束,平局!!!";
      mSuccessPointList.clear();
      onResultListener.onComplete(resultMsg);
   }
}

第四步,就是判断胜负出结果后,棋子闪动动画,这里用的是改变圆半径,通过线程控制重绘次数的方法。

private Handler circleHandler = new Handler() {

   public void handleMessage(Message msg) {
      super.handleMessage(msg);
      if (msg.what == 1) {
         canTouch = false;
         mCircleSize = (Integer) msg.obj;//圆半径
         invalidate();//重新绘制
      } else if (msg.what == 2) {
         onResultListener.onComplete(resultMsg);
      }
   }
};
    private class CircleThread implements Runnable {

      @Override
      public void run() {
         while (!Thread.currentThread().isInterrupted()) {
            try {
               Thread.sleep(200);
               m++;
               Message msg = new Message();
               if (m > 6) {
                  msg.what = 2;
                  circleHandler.sendMessage(msg);
                  return;
               }
               msg.what = 1;
               if (m % 2 == 0) {
                  msg.obj = 45;
               } else {
                  msg.obj = 40;
               }

               circleHandler.sendMessage(msg);
            } catch (InterruptedException e) {
               e.printStackTrace();
            }
         }
      }

   }
}


第五步,如前进、后退(悔棋)、重玩功能,重点是通过List的增删等动作,思路清晰的话应该不难。

private ArrayList<Position> mPositionList = new ArrayList<>();//棋盘共下的子
private ArrayList<Position> mBlackPositionList = new ArrayList<>();//棋盘下的黑子
private ArrayList<Position> mWhitePositionList = new ArrayList<>();//棋盘共下的白子
private ArrayList<Position> mPositionUndoList = new ArrayList<>();//存储悔棋的步骤
// private ArrayList<Position> mSuccessPositionList = new ArrayList<>();
private ArrayList<Point> mSuccessPointList = new ArrayList<>();

上面就是为了实现如上功能所设置的List,虽然有点多。。。


第六步,保存棋盘及加载棋盘状态,利用就是将序列化后的相关数据保存到外存,当需要时再由外存读入。重绘保存的数据信息,即可重现保存棋盘的模样。

/**
 * 保存棋盘
 */
public void save() {
   if (mPositionList.size() == 0) {
      return;
   }
   Map<String, Object> map = new HashMap<>();
   map.put("mScore", mScore);
   map.put("mPositionList", mPositionList);
   map.put("mBlackPositionList", mBlackPositionList);
   map.put("mWhitePositionList", mWhitePositionList);

   File filePath = new File(BaseApplication.instance.getCacheDir(), "gameTTT");
   FileSaveHandler.saveObject(filePath.getPath(), map);

   reset();
}

/**
 * 加载保存的棋盘
 */
public void load() {
   File filePath = new File(BaseApplication.instance.getCacheDir(), "gameTTT");
   Map<String, Object> map = (Map<String, Object>) FileSaveHandler.readObject(filePath.getPath());

   mScore = (Position[][]) map.get("mScore");
   mPositionList = (ArrayList<Position>) map.get("mPositionList");
   mBlackPositionList = (ArrayList<Position>) map.get("mBlackPositionList");
   mWhitePositionList = (ArrayList<Position>) map.get("mWhitePositionList");

   if (mPositionList.size() == 0) {
      return;
   }
   Position position = mPositionList.get(mPositionList.size() - 1);
   if (position.getColor() == Color.WHITE) {
      calculator(mWhitePositionList, "白子");
   } else {
      calculator(mBlackPositionList, "黑子");
   }
   postInvalidate();
}

最后,已上传代码至github。https://github.com/Selina592732122/GameTTT



  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值