一.棋盘
我们一看就知道,我们必须自定义View,这里我们定义一个GameView来做游戏主类,第一步,先测量,我们这里不难知道,五子棋他的棋盘是一个正方形,所以我们需要去测量
/**
*测量
*
*@paramwidthMeasureSpec
*@paramheightMeasureSpec
*/
@Override
protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){
//获取高宽值
intwidthSize=(widthMeasureSpec);
intwidthMode=(widthMeasureSpec);
inthightSize=(heightMeasureSpec);
inthightMode=(heightMeasureSpec);
//拿到宽和高的最小值,也就是宽
intwidth=(widthSize,heightMeasureSpec);
//根据测量模式细节处理
if(widthMode==){
width=hightSize;
}elseif(hightMode==){
width=widthSize;
}
//设置这样就是一个正方形了
setMeasuredDimension(width,width);
}
这里的逻辑还是十分简单的,我们拿到长和宽去比较一下,设置这个View的长宽Wie最小值,就是一个正方形了,所以我们的layout_是这样写的
?xmlversion="1.0"encoding="utf-8"?
LinearLayoutxmlns:android=""
xmlns:tools=""
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@drawable/main_bg"
android:layout_width="match_parent"
android:layout_height="match_parent"/
/LinearLayout
这里我在构造方法中设置了一个半透明的红色背景,是在我们调试的时候可以更加清晰的看清楚GameView的大小,所以,运行的结果
二.线条
这个应该也算是棋盘的一部分吧,就是棋盘上的线条,我们应该怎么去画,首先,我们要去定义一些属性
//线条数量
privatestaticfinalintMAX_LINE=10;
//线条的宽度
privateintmPanelWidth;
//线条的高度
privatefloatmLineHeight;
然后,我们要去确定大小
/**
*测量大小
*
*@paramw
*@paramh
*@paramoldw
*@paramoldh
*/
@Override
protectedvoidonSizeChanged(intw,inth,intoldw,intoldh){
(w,h,oldw,oldh);
//拿到宽
mPanelWidth=w;
//分割
mLineHeight=mPanelWidth*1.0f/MAX_LINE;
}
不要着急,这些都只是一些准备的工作,我们画线条是必须要在onDraw(0方法里的,但是前期我们要准备一只画笔,对吧,所以我们要初始化画笔
/**
*初始化画笔
*/
privatevoidinitPaint(){
//设置颜色
(0x88000000);
//抗锯齿
(true);
//设置防抖动
(true);
//设置Style
();
}
现在我们可以去绘制了,我们在OnDraw(0方法里写一个drawLine方法来专门绘制线条
/**
*绘制棋盘的方法
*
*@paramcanvas
*/
privatevoiddrawLine(Canvascanvas){
//获取高宽
intw=mPanelWidth;
floatlineHeight=mLineHeight;
//遍历,绘制线条
for(inti=0;iMAX_LINE;i++){
//横坐标
intstartX=(int)(lineHeight/2);
intX=(int)(w-lineHeight/2);
//纵坐标
inty=(int)((0.5+i)*lineHeight);
//绘制横
(startX,y,X,y,mPaint);
//绘制纵
(y,startX,y,X,mPaint);
}
}
我们运行一下
好的,这里,注意一下,我在activity_中定义了一个
android:gravity="center"
属性,所以让他居中,同样的,我们在initPaint中加上点代码让我们看的更加直观一点
//设置颜色
();
//设置线条宽度
(3);
同样的,我们把构造法里的设置背景的测试代码注释掉
//测试代码
//setBackgroundColor(0x44ff0000);
这样,我们运行一下
得,我们现在有模有样了
三.棋子
棋子我们事先准备好了两张图片,但是这里我们要考虑他的大小的问题了,我们的思路是让他是行高的四分之三大小,所以先声明
//黑棋子
privateBitmapmBlack;
//白棋子
privateBitmapmWhite;
//比例,棋子的大小是高的四分之三
privatefloatrowSize=3*1.0f/4;
然后我们定义一个方法区初始化Bitmap
/**
*初始化棋子
*/
privatevoidinitBitmap(){
//拿到图片资源
mBlack=(getResources(),_black);
mWhite=(getResources(),_white);
}
拿到资源之后我们就可以设置大小了,我们在onSizeChanged()里面设置
//棋子宽度
intmWhiteWidth=(int)(mLineHeight*rowSize);
//修改棋子大小
mWhite=(mWhite,mWhiteWidth,mWhiteWidth,false);
mBlack=(mBlack,mWhiteWidth,mWhiteWidth,false);
privateListPointmWhiteArray=newArrayList();
privateListPointmBlackArray=newArrayList();
//标记,是执黑子还是白子,白棋先手
privatebooleanmIsWhite=true;
这样才和触摸事件相得映彰
/**
*触摸事件
*
*@paramevent
*@return
*/
@Override
publicbooleanonTouchEvent(MotionEventevent){
switch(()){
//按下事件
_UP:
intx=(int)();
inty=(int)();
//封装成一个Point
Pointp=getValidPoint(x,y);
//判断当前这个点是否有棋子了
if((p)||(p)){
returnfalse;
}
//判断如果是白子就存白棋集合,反之则黑棋集合
if(mIsWhite){
(p);
}else{
(p);
}
//刷新
invalidate();
//改变值
mIsWhite=!mIsWhite;
break;
}
returntrue;
}
这样,有几点是要说明一下的,首先我们newPoint的时候为了避免重复绘制我们是实现了一个方法
/**
*不能重复点击
*
*@paramx
*@paramy
*@return
*/
privatePointgetValidPoint(intx,inty){
returnnewPoint((int)(x/mLineHeight),(int)(y/mLineHeight));
}
/**
*绘制棋子的方法
*
*@paramcanvas
*/
privatevoiddrawPieces(Canvascanvas){
for(inti=0;();i++){
//获取白棋子的坐标
PointwhitePoint=(i);
(mBlack,(+(1-rowSize)/2)*mLineHeight,(+(1-rowSize)/2)*mLineHeight,null);
}
for(inti=0;();i++){
//获取黑棋子的坐标
PointblackPoint=(i);
(mWhite,(+(1-rowSize)/2)*mLineHeight,(+(1-rowSize)/2)*mLineHeight,null);
}
}
OK,我们实际运行一下
四.游戏逻辑
现在什么都有了,基本上都可用玩了,但是还少了重要的一点就是游戏结束,你到了五颗也需要判断是否胜利呀,对吧,我们写一个方法,在每次绘制完成之后就去判断是否有赢家
/**
*判断是否胜利
*/
privatevoidcheckWin(){
//判断白棋是否有五个相同的棋子相连
booleanmWhiteWin=checkFiveLine(mWhiteArray);
//判断黑棋是否有五个相同的棋子相连
booleanmBlackWin=checkFiveLine(mBlackArray);
//只要有一个胜利,游戏就结束
if(mWhiteWin||mBlackWin){
mIsGameOver=true;
mIsWhiteWin=mWhiteWin;
(getContext(),mIsWhiteWin?"白棋胜利":"黑棋胜利",_SHORT).show();
}
}
好的,我们重点逻辑就在checkFiveLine这个方法上了,这里,我们所知道的胜利有四种情况
我们先定义一个常量
//胜利棋子数量
privatestaticfinalintMAX_COUNT_IN_LINE=5;
OK,接下来我们可以实现以下胜利的逻辑了
/**
*//判断棋子是否有五个相同的棋子相连
*
*@parampoints
*@return
*/
privatebooleancheckFiveLine(ListPointpoints){
//遍历棋子
for(Pointp:points){
//拿到棋盘上的位置
intx=;
inty=;
/**
*四种情况胜利,横,竖,左斜,右斜
*/
//横
booleanwin=checkHorizontal(x,y,points);
if(win)returntrue;
//竖
win=checkVertical(x,y,points);
if(win)returntrue;
//左斜
win=checkLeft(x,y,points);
if(win)returntrue;
//右斜
win=checkRight(x,y,points);
if(win)returntrue;
}
returnfalse;
}
我们不管哪个方向只要返回true就返回true,然后弹Toast,这里,四个方向的逻辑
-横
/**
*判断横向的棋子
*
*@paramx
*@paramy
*@parampoints
*/
privatebooleancheckHorizontal(intx,inty,ListPointpoints){
//棋子标记,记录是否有五个=1是因为自身是一个
intcount=1;
//左
for(inti=1;iMAX_COUNT_IN_LINE;i++){
//如果有
if((newPoint(x-i,y))){
count++;
}else{
break;
}
}
//有五个就为true
if(count==MAX_COUNT_IN_LINE){
returntrue;
}
//右
for(inti=1;iMAX_COUNT_IN_LINE;i++){
//如果有
if((newPoint(x+i,y))){
count++;
}else{
break;
}
}
//有五个就为true
if(count==MAX_COUNT_IN_LINE){
returntrue;
}
returnfalse;
}
-横
/**
*判断纵向的棋子
*
*@paramx
*@paramy
*@parampoints
*/
privatebooleancheckVertical(intx,inty,ListPointpoints){
//棋子标记,记录是否有五个=1是因为自身是一个
intcount=1;
//上
for(inti=1;iMAX_COUNT_IN_LINE;i++){
//如果有
if((newPoint(x,y-i))){
count++;
}else{
break;
}
}
//有五个就为true
if(count==MAX_COUNT_IN_LINE){
returntrue;
}
//下
for(inti=1;iMAX_COUNT_IN_LINE;i++){
//如果有
if((newPoint(x,y+i))){
count++;
}else{
break;
}
}
//有五个就为true
if(count==MAX_COUNT_IN_LINE){
returntrue;
}
returnfalse;
}
-左斜
/**
*判断左斜向的棋子
*
*@paramx
*@paramy
*@parampoints
*/
privatebooleancheckLeft(intx,inty,ListPointpoints){
//棋子标记,记录是否有五个=1是因为自身是一个
intcount=1;
for(inti=1;iMAX_COUNT_IN_LINE;i++){
//如果有
if((newPoint(x-i,y+i))){
count++;
}else{
break;
}
}
//有五个就为true
if(count==MAX_COUNT_IN_LINE){
returntrue;
}
for(inti=1;iMAX_COUNT_IN_LINE;i++){
//如果有
if((newPoint(x+i,y-i))){
count++;
}else{
break;
}
}
//有五个就为true
if(count==MAX_COUNT_IN_LINE){
returntrue;
}
returnfalse;
}
-右斜
/**
*判断右斜向的棋子
*
*@paramx
*@paramy
*@parampoints
*/
privatebooleancheckRight(intx,inty,ListPointpoints){
//棋子标记,记录是否有五个=1是因为自身是一个
intcount=1;
for(inti=1;iMAX_COUNT_IN_LINE;i++){
//如果有
if((newPoint(x-i,y-i))){
count++;
}else{
break;
}
}
//有五个就为true
if(count==MAX_COUNT_IN_LINE){
returntrue;
}
for(inti=1;iMAX_COUNT_IN_LINE;i++){
//如果有
if((newPoint(x+i,y+i))){
count++;
}else{
break;
}
}
//有五个就为true
if(count==MAX_COUNT_IN_LINE){
returntrue;
}
returnfalse;
}
这样,我们运行一下
嘿嘿,好玩吧!
五.游戏状态存储
这个就是当我们游戏挂后台之后,再回来游戏就没了,我们应该存储他的状态,让他每一次进入的时候要是上一句没有下完接着下,那我们该怎么去实现呢?和Activity一样,我们View也有存储状态的方法
/**
*存储状态
*
*@return
*/
@Override
protectedParcelableonSaveInstanceState(){
Bundlebundle=newBundle();
(INSTANCE,());
(INSTANCE_GAMEOVER,mIsGameOver);
(INSTANCE_WHITE_ARRAY,mWhiteArray);
(INSTANCE_BLACK_ARRAY,mBlackArray);
returnbundle;
}
/**
*重新运行
*
*@paramstate
*/
@Override
protectedvoidonRestoreInstanceState(Parcelablestate){
//取值
if(stateinstanceofBundle){
Bundlebundle=(Bundle)state;
mIsGameOver=(INSTANCE_GAMEOVER);
mWhiteArray=(INSTANCE_WHITE_ARRAY);
mBlackArray=(INSTANCE_BLACK_ARRAY);
//调用
((INSTANCE));
return;
}
(state);
}
这样就可以了,但是,有一点要知道,不要忘记在布局文件上给控件加上ID,不然状态不会存储哦
android:id="@+id/mGameView"
android:layout_width="match_parent"
android:layout_height="match_parent"/
六.再来一局
既然我们的游戏逻辑差不多了,那我们应该考虑一下当你胜利的时候,你是不是应该再来一局,所以我们还要实现这个逻辑,这个很简单
/**
*再来一局
*/
publicvoidRestartGame(){
();
();
mIsGameOver=false;
mIsWhiteWin=false;
invalidate();
}
这样,我们就可以直接调用了,我们来看看MainActivity
;
;
;
;
;
/**
*五子棋游戏
*/
publicclassMainActivityextsAppCompatActivity{
//重来按钮
privateFloatingActionButtonfab;
//游戏
privateGameViewgame;
@Override
protectedvoidonCreate(BundlesavedInstanceState){
(savedInstanceState);
setContentView(_main);
game=(GameView)findViewById();
fab=(FloatingActionButton)findViewById();
((){
@Override
publicvoidonClick(Viewv){
();
}
});
}
}
OK,我们最终运行一下
OK,到这里,就算开发完成了Demo下载::555974449欢迎你加入!