上分宝:值得大家信赖的下载网站!
时间:2023-08-23 17:12:56来源:互联网
阿法狗让围棋突然就被热议了,鸿洋大神也顺势出了篇五子棋单机游戏的视频,我看到了就像膜拜膜拜,就学习了一下,写篇博客梳理一下自己的思路,加深一下印象
视频链接:http://www.imooc.com/learn/641
一.棋盘
我们一看就知道,我们必须自定义View,这里我们定义一个GameView来做游戏主类,第一步,先测量,我们这里不难知道,五子棋他的棋盘是一个正方形,所以我们需要去测量
/**
* 测量
*
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//获取高宽值
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int hightSize = MeasureSpec.getSize(heightMeasureSpec);
int hightMode = MeasureSpec.getMode(heightMeasureSpec);
//拿到宽和高的最小值,也就是宽
int width = Math.min(widthSize, heightMeasureSpec);
//根据测量模式细节处理
if (widthMode == MeasureSpec.UNSPECIFIED) {
width = hightSize;
} else if (hightMode == MeasureSpec.UNSPECIFIED) {
width = widthSize;
}
//设置这样就是一个正方形了
setMeasuredDimension(width, width);
}
这里的逻辑还是十分简单的,我们拿到长和宽去比较一下,设置这个View的长宽Wie最小值,就是一个正方形了,所以我们的layout_main.xml是这样写的
xmlns:tools="http://schemas.android.com/tools"
android:layout_ >
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@drawable/main_bg"
>
android:layout_ > android:layout_height="match_parent" /> 这里我在构造方法中设置了一个半透明的红色背景,是在我们调试的时候可以更加清晰的看清楚GameView的大小,所以,运行的结果 二.线条 这个应该也算是棋盘的一部分吧,就是棋盘上的线条,我们应该怎么去画,首先,我们要去定义一些属性 //线条数量 private static final int MAX_LINE = 10; //线条的宽度 private int mPanelWidth; //线条的高度 private float mLineHeight; 然后,我们要去确定大小 /** * 测量大小 * * @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); //拿到宽 mPanelWidth = w; //分割 mLineHeight = mPanelWidth * 1.0f / MAX_LINE; } 不要着急,这些都只是一些准备的工作,我们画线条是必须要在onDraw(0方法里的,但是前期我们要准备一只画笔,对吧,所以我们要初始化画笔 /** * 初始化画笔 */ private void initPaint() { //设置颜色 mPaint.setColor(0x88000000); //抗锯齿 mPaint.setAntiAlias(true); //设置防抖动 mPaint.setDither(true); //设置Style mPaint.setStyle(Paint.Style.STROKE); } 现在我们可以去绘制了,我们在OnDraw(0方法里写一个drawLine方法来专门绘制线条 /** * 绘制棋盘的方法 * * @param canvas */ private void drawLine(Canvas canvas) { //获取高宽 int w = mPanelWidth; float lineHeight = mLineHeight; //遍历,绘制线条 for (int i = 0; i < MAX_LINE; i ) { //横坐标 int startX = (int) (lineHeight / 2); int endX = (int) (w - lineHeight / 2); //纵坐标 int y = (int) ((0.5 i) * lineHeight); //绘制横 canvas.drawLine(startX, y, endX, y, mPaint); //绘制纵 canvas.drawLine(y, startX, y, endX, mPaint); } } 我们运行一下 好的,这里,注意一下,我在activity_main.xml中定义了一个 android:gravity="center" 属性,所以让他居中,同样的,我们在initPaint中加上点代码让我们看的更加直观一点 //设置颜色 mPaint.setColor(Color.BLACK); //设置线条宽度 mPaint.setStrokeWidth(3); 同样的,我们把构造法里的设置背景的测试代码注释掉 //测试代码 //setBackgroundColor(0x44ff0000); 这样,我们运行一下 得,我们现在有模有样了 三.棋子 棋子我们事先准备好了两张图片,但是这里我们要考虑他的大小的问题了,我们的思路是让他是行高的四分之三大小,所以先声明 //黑棋子 private Bitmap mBlack; //白棋子 private Bitmap mWhite; //比例,棋子的大小是高的四分之三 private float rowSize = 3 * 1.0f / 4; 然后我们定义一个方法区初始化Bitmap /** * 初始化棋子 */ private void initBitmap() { //拿到图片资源 mBlack = BitmapFactory.decodeResource(getResources(), R.drawable.stone_black); mWhite = BitmapFactory.decodeResource(getResources(), R.drawable.stone_white); } 拿到资源之后我们就可以设置大小了,我们在onSizeChanged()里面设置 //棋子宽度 int mWhiteWidth = (int) (mLineHeight * rowSize); //修改棋子大小 mWhite = Bitmap.createScaledBitmap(mWhite, mWhiteWidth, mWhiteWidth, false); mBlack = Bitmap.createScaledBitmap(mBlack, mWhiteWidth, mWhiteWidth, false); 不过棋子可没我们想象的那么简单,我们要点击一下再去绘制一个棋子,这样的思路该怎么去实现呢?我们实现它的点击事件,这里先定义几个变量 //存储用户点击的坐标 private List mWhiteArray = new ArrayList(); private List mBlackArray = new ArrayList(); //标记,是执黑子还是白子 ,白棋先手 private boolean mIsWhite = true; 这样才和触摸事件相得映彰 /** * 触摸事件 * * @param event * @return */ @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { //按下事件 case MotionEvent.ACTION_UP: int x = (int) event.getX(); int y = (int) event.getY(); //封装成一个Point Point p = getValidPoint(x, y); //判断当前这个点是否有棋子了 if(mWhiteArray.contains(p) || mBlackArray.contains(p)){ //点击不生效 return false; } //判断如果是白子就存白棋集合,反之则黑棋集合 if (mIsWhite) { mWhiteArray.add(p); } else { mBlackArray.add(p); } //刷新 invalidate(); //改变值 mIsWhite = !mIsWhite; break; } return true; } 这样,有几点是要说明一下的,首先我们new Point的时候为了避免重复绘制我们是实现了一个方法 /** * 不能重复点击 * * @param x * @param y * @return */ private Point getValidPoint(int x, int y) { return new Point((int) (x / mLineHeight), (int) (y / mLineHeight)); } 紧接着我们就判断,要是重复点击,返回false,而且我们在action选择也是选择了ACTION_UP,为什么?为什么不是ACTION_DOWN?因为这个毕竟是一个View,父View会拦截(某些场景),所以我们选在UP上才是合情合理的 >好的,当我们点击之后就要绘制棋子了,这里我们也写一个方法 /** * 绘制棋子的方法 * * @param canvas */ private void drawPieces(Canvas canvas) { for (int i = 0; i < mWhiteArray.size(); i ) { //获取白棋子的坐标 Point whitePoint = mWhiteArray.get(i); canvas.drawBitmap(mBlack, (whitePoint.x (1 - rowSize) / 2) * mLineHeight, (whitePoint.y (1 - rowSize) / 2) * mLineHeight, null); } for (int i = 0; i < mBlackArray.size(); i ) { //获取黑棋子的坐标 Point blackPoint = mBlackArray.get(i); canvas.drawBitmap(mWhite, (blackPoint.x (1 - rowSize) / 2) * mLineHeight, (blackPoint.y (1 - rowSize) / 2) * mLineHeight, null); } } OK,我们实际运行一下 四.游戏逻辑 现在什么都有了,基本上都可用玩了,但是还少了重要的一点就是游戏结束,你到了五颗也需要判断是否胜利呀,对吧,我们写一个方法,在每次绘制完成之后就去判断是否有赢家 /** * 判断是否胜利 */ private void checkWin() { //判断白棋是否有五个相同的棋子相连 boolean mWhiteWin = checkFiveLine(mWhiteArray); //判断黑棋是否有五个相同的棋子相连 boolean mBlackWin = checkFiveLine(mBlackArray); //只要有一个胜利,游戏就结束 if (mWhiteWin || mBlackWin) { mIsGameOver = true; mIsWhiteWin = mWhiteWin; Toast.makeText(getContext(), mIsWhiteWin ? "白棋胜利" : "黑棋胜利", Toast.LENGTH_SHORT).show(); } } 好的,我们重点逻辑就在checkFiveLine这个方法上了,这里,我们所知道的胜利有四种情况 我们先定义一个常量 //胜利棋子数量 private static final int MAX_COUNT_IN_LINE = 5; OK,接下来我们可以实现以下胜利的逻辑了 /** * //判断棋子是否有五个相同的棋子相连 * * @param points * @return */ private boolean checkFiveLine(List points) { //遍历棋子 for (Point p : points) { //拿到棋盘上的位置 int x = p.x; int y = p.y; /** * 四种情况胜利,横,竖,左斜,右斜 */ //横 boolean win = checkHorizontal(x, y, points); if (win) return true; //竖 win = checkVertical(x, y, points); if (win) return true; //左斜 win = checkLeft(x, y, points); if (win) return true; //右斜 win = checkRight(x, y, points); if (win) return true; } return false; } 我们不管哪个方向只要返回true就返回true,然后弹Toast,这里,四个方向的逻辑 - 横 /** * 判断横向的棋子 * * @param x * @param y * @param points */ private boolean checkHorizontal(int x, int y, List points) { //棋子标记,记录是否有五个 =1是因为自身是一个 int count = 1; //左 for (int i = 1; i < MAX_COUNT_IN_LINE; i ) { //如果有 if (points.contains(new Point(x - i, y))) { count ; } else { break; } } //有五个就为true if (count == MAX_COUNT_IN_LINE) { return true; } //右 for (int i = 1; i < MAX_COUNT_IN_LINE; i ) { //如果有 if (points.contains(new Point(x i, y))) { count ; } else { break; } } //有五个就为true if (count == MAX_COUNT_IN_LINE) { return true; } return false; } - 横 /** * 判断纵向的棋子 * * @param x * @param y * @param points */ private boolean checkVertical(int x, int y, List points) { //棋子标记,记录是否有五个 =1是因为自身是一个 int count = 1; //上 for (int i = 1; i < MAX_COUNT_IN_LINE; i ) { //如果有 if (points.contains(new Point(x, y - i))) { count ; } else { break; } } //有五个就为true if (count == MAX_COUNT_IN_LINE) { return true; } //下 for (int i = 1; i < MAX_COUNT_IN_LINE; i ) { //如果有 if (points.contains(new Point(x, y i))) { count ; } else { break; } } //有五个就为true if (count == MAX_COUNT_IN_LINE) { return true; } return false; } - 左斜 /** * 判断左斜向的棋子 * * @param x * @param y * @param points */ private boolean checkLeft(int x, int y, List points) { //棋子标记,记录是否有五个 =1是因为自身是一个 int count = 1; for (int i = 1; i < MAX_COUNT_IN_LINE; i ) { //如果有 if (points.contains(new Point(x - i, y i))) { count ; } else { break; } } //有五个就为true if (count == MAX_COUNT_IN_LINE) { return true; } for (int i = 1; i < MAX_COUNT_IN_LINE; i ) { //如果有 if (points.contains(new Point(x i, y - i))) { count ; } else { break; } } //有五个就为true if (count == MAX_COUNT_IN_LINE) { return true; } return false; } - 右斜 /** * 判断右斜向的棋子 * * @param x * @param y * @param points */ private boolean checkRight(int x, int y, List points) { //棋子标记,记录是否有五个 =1是因为自身是一个 int count = 1; for (int i = 1; i < MAX_COUNT_IN_LINE; i ) { //如果有 if (points.contains(new Point(x - i, y - i))) { count ; } else { break; } } //有五个就为true if (count == MAX_COUNT_IN_LINE) { return true; } for (int i = 1; i < MAX_COUNT_IN_LINE; i ) { //如果有 if (points.contains(new Point(x i, y i))) { count ; } else { break; } } //有五个就为true if (count == MAX_COUNT_IN_LINE) { return true; } return false; } 这样,我们运行一下 嘿嘿,好玩吧! 五.游戏状态存储 这个就是当我们游戏挂后台之后,再回来游戏就没了,我们应该存储他的状态,让他每一次进入的时候要是上一句没有下完接着下,那我们该怎么去实现呢?和Activity一样,我们View也有存储状态的方法 /** * 存储状态 * * @return */ @Override protected Parcelable onSaveInstanceState() { Bundle bundle = new Bundle(); bundle.putParcelable(INSTANCE, super.onSaveInstanceState()); bundle.putBoolean(INSTANCE_GAMEOVER, mIsGameOver); bundle.putParcelableArrayList(INSTANCE_WHITE_ARRAY, mWhiteArray); bundle.putParcelableArrayList(INSTANCE_BLACK_ARRAY, mBlackArray); return bundle; } /** * 重新运行 * * @param state */ @Override protected void onRestoreInstanceState(Parcelable state) { //取值 if (state instanceof Bundle) { Bundle bundle = (Bundle) state; mIsGameOver = bundle.getBoolean(INSTANCE_GAMEOVER); mWhiteArray = bundle.getParcelableArrayList(INSTANCE_WHITE_ARRAY); mBlackArray = bundle.getParcelableArrayList(INSTANCE_BLACK_ARRAY); //调用 super.onRestoreInstanceState(bundle.getParcelable(INSTANCE)); return; } super.onRestoreInstanceState(state); } 这样就可以了,但是,有一点要知道,不要忘记在布局文件上给控件加上ID,不然状态不会存储哦 android:id="@ id/mGameView" android:layout_ > android:layout_height="match_parent" /> 六.再来一局 既然我们的游戏逻辑差不多了,那我们应该考虑一下当你胜利的时候,你是不是应该再来一局,所以我们还要实现这个逻辑,这个很简单 /** * 再来一局 */ public void RestartGame() { mWhiteArray.clear(); mBlackArray.clear(); mIsGameOver = false; mIsWhiteWin = false; invalidate(); } 这样,我们就可以直接调用了,我们来看看MainActivity package com.lgl.fiverow; import android.support.design.widget.FloatingActionButton; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; /** * 五子棋游戏 */ public class MainActivity extends AppCompatActivity { //重来按钮 private FloatingActionButton fab; //游戏 private GameView game; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); game = (GameView) findViewById(R.id.mGameView); fab = (FloatingActionButton) findViewById(R.id.fab); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { game.RestartGame(); } }); } } OK,我们最终运行一下 OK,到这里,就算开发完成了Demo下载:http://download.csdn.net/detail/qq_26787115/9521011我的群:555974449欢迎你加入!
上海市初中学生综合素质评价登录入口
宝可梦属性克制表2024最新,18种属性克制关系
王者荣耀排位巅峰赛荣耀战力加分公式规则2024
魔塔50层攻略隐藏道具图解,魔塔50层详细图文攻略
美国zoom动物仙踪林详情介绍
重装机兵攻略隐藏物品图文详解,战车具体位置
推荐几款超污的手游游戏,6个很污的养成类游戏
希尔薇攻略
原神主c排行2023,4.2目前各系最强主c角色排名
action对魔忍
动作游戏
2233盒子
其他游戏
oppo助手2.0
其他游戏
touchitrikka
益智休闲
qq飞车测试服
赛车竞速
青云纪事
角色扮演
泰拉tmodloader
其他游戏
菜菜音乐盒
其他游戏
迷你世界老版本0.44.2版本
模拟经营
极乐园
聊天交友
湖北农村商业银行
金融理财
51品茶
交友约会
极乐园paradise
聊天交友
丝目
社交聊天
七点工具箱
工具应用
酷狗输入法(搜狗输入法)
常用工具
shopee东南亚跨境电商
境外购物
日本大阪usj
生活实用