网上有很多五子棋的代码,对于新手来说,看别人的代码实在有些费劲,因为很多地方的注释都不全,此文章重点不在代码上而是在代码的分析上,现在直接上代码及其注解。
五子棋中需要用到的知识点有很多:
1.Bitmap
2.Canvas的clipRect方法
3.Matrix
4.surfaceview
5.五子棋算法的问题
6.关于五子棋整个代码运行流程的笔记
package cn.m.xys;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
/**
* QQ:845145080
*/
//手机的屏幕的原点(0,0)在左上角
//在游戏开发中多用到SurfaceView,游戏中的背景、人物、动画等等尽量在画布canvas中画出。 canvas是画布
// Runnable是一个接口 里面只有一个run()方法,是为了实现多线程,因为一个类只能继承一个父类,所以接口实现多线程比继承父类Thread实现多线程更方便。
/**
* package org.demo.runnable;
* class MyThread implements Runnable{
* private int ticket=10;
* public void run(){
* for(int i=0;i<20;i++){
* if(this.ticket>0){
* System.out.println("卖票:ticket"+this.ticket--);
* }
* }
* }
* }
* package org.demo.runnable;
* public class RunnableTicket {
* public static void main(String[] args) {
* MyThread mt=new MyThread();
* new Thread(mt).start();
* }
* }
* //同一个mt,但是在Thread中就不可以,如果用同一 newThread(mt).start();
* //个实例化对象mt,就会出现异常 new Thread(mt).start(); } };
*
*/
public class GameView extends SurfaceView implements Const,
SurfaceHolder.Callback, Runnable {
static GameView sInstance = null;
// 定義界面的大小,通過getwindowmanager
public static void init(Activity mActivity, int screenWidth,
int screenHeight) {
sInstance = new GameView(mActivity, screenWidth, screenHeight);
}
// 返回的是定義好的界面
public static GameView getInstance() {
return sInstance;
}
// 控制循环
boolean mbLoop = false;
// 定义SurfaceHolder对象
SurfaceHolder mSurfaceHolder = null;
public static Paint sPaint = null;
public static Canvas sCanvas = null;
/*
* 我们知道在开发中,需要应用程序资源,如应用工程中assets和res目录下的图片,layout,values等,或者需要系统内置的资源。
* 我们获取这些资源的入口对象都是Resources对象。
*/
public static Resources sResources = null;
private int mGameState = 0;
private int mScreenWidth = 0;
private int mScreenHeight = 0;
public int[][] mGameMap = null;
private int mMapHeightLengh = 0;
private int mMapWidthLengh = 0;
private int mMapIndexX = 0;
private int mMapIndexY = 0;
public int mCampTurn = 0;
public int mCampWinner = 0;
private float mTitleSpace = 0;
private int mTitleHeight = 0;
private float mTitleIndex_x = 0;
private float mTitleIndex_y = 0;
Bitmap bitmapBg = null;
Bitmap mBlack = null;
Bitmap mWhite = null;
Context mContext = null;
public GameView(Activity activity, int screenWidth, int screenHeight) {
super(activity);
sPaint = new Paint();
sPaint.setAntiAlias(true); // 去锯齿
sResources = getResources(); // 获取Resources对象
mContext = activity;
mScreenWidth = screenWidth;
mScreenHeight = screenHeight;
Log.v("--启动getHolder--", "getHolder开始启动");
mSurfaceHolder = this.getHolder();
Log.v("--addCallback--", "调用addCallback");
mSurfaceHolder.addCallback(this);
setFocusable(true); // 设置能否获得焦点
mbLoop = true;
Log.v("--CreatMatrixBitmap--", "调用CreatMatrixBitmap");
bitmapBg = CreatMatrixBitmap(R.drawable.status, mScreenWidth,
mScreenHeight);
mBlack = BitmapFactory.decodeResource(GameView.sResources,
R.drawable.ai);
mWhite = BitmapFactory.decodeResource(GameView.sResources,
R.drawable.human);
mTitleSpace = (float) mScreenWidth / CHESS_WIDTH; // mTitleSpace =screenWidth; 标题的宽就等于棋盘的宽
// CHESS_WIDTH=9
mTitleHeight = mScreenHeight / 3; // mScreenHeight=screenHeight 标题的高等于屏幕的三分之一
mTitleIndex_x = (float) (mTitleSpace / 2); // mTitleSpace=界面的宽度/9
mTitleIndex_y = (float) (mTitleSpace / 2);
Log.v("--setGameState--", "调用setGameState");
setGameState(GS_GAME); // GS_GAME=4
}
public void setGameState(int newState) {
Log.v("--setGameState--", "setGameState");
mGameState = newState;
switch (mGameState) {
case GS_GAME:
mGameMap = new int[CHESS_HEIGHT][CHESS_WIDTH]; // public int[][]
// mGameMap = null;
// CHESS_HEIGHT = 9;
// CHESS_HEIGHT = 9
mMapHeightLengh = mGameMap.length; // 9 二维数组的大小为行数
mMapWidthLengh = mGameMap[0].length; // 9
mCampTurn = CAMP_HERO; // CAMP_HERO = 1
break;
}
}
protected void Draw() {
Log.v("--Draw--", "调用Draw");
sCanvas = mSurfaceHolder.lockCanvas(); // 将画布锁住
if (mSurfaceHolder == null || sCanvas == null) {
return;
}
Log.v("--RenderGame--", "调用RenderGame");
RenderGame();
mSurfaceHolder.unlockCanvasAndPost(sCanvas); // 将画布解锁
}
private void RenderGame() {
switch (mGameState) {
case GS_GAME: // GS_GAME=4
DrawRect(Color.WHITE, 0, 0, mScreenWidth, mScreenHeight);//对整个屏幕进行颜色的变换
RenderMap();
break;
case GS_END: // GS_END = 5
DrawRect(Color.RED, 0, 0, mScreenWidth, mScreenHeight); //对整个屏幕进行颜色的变换
DrawString(Color.WHITE, sResources.getString(mCampWinner) //对字进行颜色的改变
+ "胜利 点击继续游戏", 50, 50);
break;
}
}
private void RenderMap() {
Log.v("--RenderMap--", "调用RenderMap");
int i, j;
DrawImage(bitmapBg, 0, 0, 0); //bitmapBg棋盘背景
for (i = 0; i < mMapHeightLengh; i++) { //mMapHeightLengh=9
for (j = 0; j < mMapWidthLengh; j++) { //mMapWidthLengh=9
int CampID = mGameMap[i][j]; //mGameMap为9*9的数组
float x = (j * mTitleSpace) + mTitleIndex_x; //mTitleSpace为屏幕宽/9 mTitleIndex_x为屏幕宽/18
float y = (i * mTitleSpace) + mTitleHeight + mTitleIndex_y; //mTitleHeight等于屏幕的三分之一 mTitleIndex_y为屏幕宽/18
if (CampID == CAMP_HERO) { //CAMP_HERO
DrawImage(mBlack, x, y, ALIGN_VCENTER | ALIGN_HCENTER); //执黑子 ALIGN_VCENTER | ALIGN_HCENTER=3
} else if (CampID == CAMP_ENEMY) { //CAMP_ENEMY = 2
DrawImage(mWhite, x, y, ALIGN_VCENTER | ALIGN_HCENTER); //执白子 ALIGN_VCENTER=2 ALIGN_HCENTER=1
}
}
}
}
/*
* public boolean clipRect (int left, int top, int right, int bottom)
*
* left:矩形裁剪区的左边位置,可以是浮点型或者整型。 top:矩形裁剪区的上边位置,可以是浮点型或者整型。
* right:矩形裁剪区的右边位置,可以是浮点型或者整型。 bottom:矩形裁剪区的下边位置,可以是浮点型或者整型。
*
* 该方法用于裁剪画布,也就是设置画布的显示区域。在使用时,可以使用Rect对象来指定裁剪区,也可以通过指定矩形的4条边来指定裁剪区。
* 该方法主要用于部分显示以及对画布中的部分对象进行操作的场合。
*/
private void DrawRect(int color, int x, int y, int width, int height) { //对背景颜色进行变换
Log.v("--DrawRect--", "调用DrawRect");
sPaint.setColor(color);
sCanvas.clipRect(x, y, width, height); //对画布进行裁剪
sCanvas.drawRect(x, y, width, height, sPaint); //在画布上进行绘画
}
private void DrawString(int color, String str, int x, int y) { //对字体颜色进行变换
Log.v("--DrawString--", "调用DrawString");
sPaint.setColor(color);
sCanvas.drawText(str, x, y, sPaint);
}
@Override
public boolean onTouchEvent(MotionEvent event) { //当手触摸到屏幕的时候就开始自动执行这个方法
Log.v("--onTouchEvent--", "调用onTouchEvent");
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
UpdateTouchEvent(x, y);
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
break;
}
return super.onTouchEvent(event);
}
public boolean CheckPiecesMeet(int Camp) { //判断是否输赢
Log.v("--CheckPiecesMeet--", "调用CheckPiecesMeet");
int MeetCount = 0;
// 横向
for (int i = 0; i < CALU_ALL_COUNT; i++) { //CALU_ALL_COUNT = 10
int index = mMapIndexX - CALU_SINGLE_COUNT + i; //mMapIndexX=在棋盘上x方向上格数 CALU_SINGLE_COUNT = 5
if (index < 0 || index >= mMapWidthLengh) {
if (MeetCount == CALU_SINGLE_COUNT) {
return true;
}
MeetCount = 0;
continue;
}
if (mGameMap[mMapIndexY][index] == Camp) {
MeetCount++;
if (MeetCount == CALU_SINGLE_COUNT) {
return true;
}
} else {
MeetCount = 0;
}
}
// 纵向
MeetCount = 0;
for (int i = 0; i < CALU_ALL_COUNT; i++) {
int index = mMapIndexY - CALU_SINGLE_COUNT + i;
if (index < 0 || index >= mMapHeightLengh) {
if (MeetCount == CALU_SINGLE_COUNT) {
return true;
}
MeetCount = 0;
continue;
}
if (mGameMap[index][mMapIndexX] == Camp) {
MeetCount++;
if (MeetCount == CALU_SINGLE_COUNT) {
return true;
}
} else {
MeetCount = 0;
}
}
// 右斜
MeetCount = 0;
for (int i = 0; i < CALU_ALL_COUNT; i++) {
int indexX = mMapIndexX - CALU_SINGLE_COUNT + i;
int indexY = mMapIndexY - CALU_SINGLE_COUNT + i;
if ((indexX < 0 || indexX >= mMapWidthLengh)
|| (indexY < 0 || indexY >= mMapHeightLengh)) {
if (MeetCount == CALU_SINGLE_COUNT) {
return true;
}
MeetCount = 0;
continue;
}
if (mGameMap[indexY][indexX] == Camp) {
MeetCount++;
if (MeetCount == CALU_SINGLE_COUNT) {
return true;
}
} else {
MeetCount = 0;
}
}
// 左斜
MeetCount = 0;
for (int i = 0; i < CALU_ALL_COUNT; i++) {
int indexX = mMapIndexX - CALU_SINGLE_COUNT + i;
int indexY = mMapIndexY + CALU_SINGLE_COUNT - i;
if ((indexX < 0 || indexX >= mMapWidthLengh)
|| (indexY < 0 || indexY >= mMapHeightLengh)) {
if (MeetCount == CALU_SINGLE_COUNT) {
return true;
}
MeetCount = 0;
continue;
}
if (mGameMap[indexY][indexX] == Camp) {
MeetCount++;
if (MeetCount == CALU_SINGLE_COUNT) {
return true;
}
} else {
MeetCount = 0;
}
}
return false;
}
private void UpdateTouchEvent(int x, int y) {
Log.v("--UpdateTouchEvent--", "UpdateTouchEvent");
switch (mGameState) {
case GS_GAME:
if (x > 0 && y > mTitleHeight) { //x和y是棋子落下的坐标 此代码的意思是x和y在棋盘上
mMapIndexX = (int) (x / mTitleSpace); //mTitleSpace为一个棋子的宽 mMapIndexX为棋子在棋盘上水平上的格数
mMapIndexY = (int) ((y - mTitleHeight) / mTitleSpace); //mMapIndexY为棋子在棋盘上竖直上的格数
if (mMapIndexX > mMapWidthLengh) {
mMapIndexX = mMapWidthLengh; //如果棋子超出范围时,让棋子在最边缘
}
if (mMapIndexX < 0) {
mMapIndexX = 0;//如果棋子超出范围时,让棋子在最边缘
}
if (mMapIndexY > mMapHeightLengh) {
mMapIndexY = mMapHeightLengh; //如果棋子超出范围时,让棋子在最边缘
}
if (mMapIndexY < 0) {
mMapIndexY = 0;//如果棋子超出范围时,让棋子在最边缘
}
if (mGameMap[mMapIndexY][mMapIndexX] == CAMP_DEFAULT) { //CAMP_DEFAULT = 0
if (mCampTurn == CAMP_HERO) { //CAMP_HERO = 1 在setGameState中如果GS_GAME=4 mCampTurn=CAMP_HERO
mGameMap[mMapIndexY][mMapIndexX] = CAMP_HERO;
if (CheckPiecesMeet(CAMP_HERO)) {
mCampWinner = R.string.Role_black; //黑方
Log.v("--setGameState--", "setGameState");
setGameState(GS_END);
} else {
mCampTurn = CAMP_ENEMY; //CAMP_ENEMY = 2
}
} else {
mGameMap[mMapIndexY][mMapIndexX] = CAMP_ENEMY;
if (CheckPiecesMeet(CAMP_ENEMY)) {
mCampWinner = R.string.Role_white;
Log.v("--setGameState--", "setGameState");
setGameState(GS_END);
} else {
mCampTurn = CAMP_HERO;
}
}
}
}
break;
case GS_END:
setGameState(GS_GAME);
break;
}
}
public boolean isCheckInvite(String body) {
Log.v("--isCheckInvite--", "isCheckInvite");
if (body.indexOf("invite") >= 0) {
if (mGameState != GS_INVITING && mGameState != GS_COMFIRE
&& mGameState != GS_GAME) {
return true;
}
}
return false;
}
/**
* 创建一个缩小或放大的新图片
*
* @param resourcesID
* @param scr_width
* @param res_height
* @return
*/
private Bitmap CreatMatrixBitmap(int resourcesID, float scr_width,
float res_height) { //mScreenWidth屏幕的宽,mScreenHeight屏幕的高
Log.v("--CreatMatrixBitmap--","CreatMatrixBitmap");
Bitmap bitMap = null;
bitMap = BitmapFactory.decodeResource(sResources, resourcesID);
int bitWidth = bitMap.getWidth(); //图片的宽
int bitHeight = bitMap.getHeight();//图片的长
float scaleWidth = scr_width / (float) bitWidth; //缩放比 屏幕的宽比上图片的宽
float scaleHeight = res_height / (float) bitHeight;//缩放比
Matrix matrix = new Matrix(); //对图片进行处理
matrix.postScale(scaleWidth, scaleHeight); //通过缩放比进行调整图片的大小。
bitMap = Bitmap.createBitmap(bitMap, 0, 0, bitWidth, bitHeight, matrix,
true);
return bitMap;
}
/**
* 绘制一个字符串
*
* @param text
* @param x
* @param y
* @param anchor
* @param Canvas
* @param paint
*/
private void DrawString(int color, String text, int x, int y, int anchor) {
Log.v("--DrawString--","DrawString");
Rect rect = new Rect();
sPaint.getTextBounds(text, 0, text.length(), rect);
int w = rect.width();
int h = rect.height();
int tx = 0;
int ty = 0;
if ((anchor & ALIGN_RIGHT) != 0) {
tx = x - w;
} else if ((anchor & ALIGN_HCENTER) != 0) {
tx = x - (w >> 1);
} else {
tx = x;
}
if ((anchor & ALIGN_TOP) != 0) {
ty = y + h;
} else if ((anchor & ALIGN_VCENTER) != 0) {
ty = y + (h >> 1);
} else {
ty = y;
}
sPaint.setColor(color);
sCanvas.drawText(text, tx, ty, sPaint);
}
/**
* 绘制一张图片可以选择图片的锚点位置
*
* @param canvas
* @param paint
* @param bitmap
* @param x
* @param y
* @param angle
*/
private void DrawImage(Bitmap bitmap, float x, float y, int anchor) { //控制棋子在坐标上的显示
//float x = (j * mTitleSpace) + mTitleIndex_x
//float y = (i * mTitleSpace) + mTitleHeight + mTitleIndex_y;
Log.v("--DrawImage--", "DrawImage");
int w = bitmap.getWidth();
int h = bitmap.getHeight();
float tx = 0;
float ty = 0;
if ((anchor & ALIGN_RIGHT) != 0) { //ALIGN_RIGHT=3
tx = x - w;
} else if ((anchor & ALIGN_HCENTER) != 0) {
tx = x - (w >> 1);
} else {
tx = x;
}
if ((anchor & ALIGN_TOP) != 0) { //ALIGN_TOP=1
ty = y + h;
} else if ((anchor & ALIGN_VCENTER) != 0) {
ty = y - (h >> 1);
} else if ((anchor & ALIGN_BOTTOM) != 0) {
ty = y - h;
} else {
ty = y;
}
sCanvas.drawBitmap(bitmap, tx, ty, sPaint); //drawBitmap方法是将图片放到指定的位置
}
@Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
// TODO Auto-generated method stub
Log.v("--surfacechanged--", "surfacechanged");
}
@Override
public void surfaceCreated(SurfaceHolder arg0) {
new Thread(this).start(); // 将GameView这个类实例化成一个线程,然后在每一次启动时启动这个线程
Log.v("--surfaceCreated--", "surfaceCreated");
}
@Override
public void surfaceDestroyed(SurfaceHolder arg0) {
mbLoop = false; // 在初始化时mbLoop为true
Log.v("--surfaceDestroyed--", "surfaceDestroyed");
}
@Override
public void run() {
Log.v("--run--", "run");
while (mbLoop) { // 线程
try {
Thread.sleep(200);
} catch (Exception e) {
}
// 当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,
//一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块
synchronized (mSurfaceHolder) {
Log.v("--执行run方法中的synchronized--", "synchronized");
Draw();
}
}
}
}
package cn.m.xys;
public interface Const {
public static final int ALIGN_TOP = 1; //1
public static final int ALIGN_VCENTER = ALIGN_TOP << 1; //2
public static final int ALIGN_LEFT = ALIGN_TOP << 2; //4
public static final int ALIGN_RIGHT = ALIGN_TOP << 3; //8
public static final int ALIGN_HCENTER = ALIGN_TOP << 4; //16
public static final int ALIGN_BOTTOM = ALIGN_TOP << 5; //32
public final static int GS_WAIT = 0;
public final static int GS_INVITING = 1;
public final static int GS_COMFIRE = 2;
public final static int GS_DECLINE = 3;
public final static int GS_GAME = 4; //开启游戏
public final static int GS_END = 5; //结束游戏
public final static int GS_AWAY = 6;
public final static int GS_ERROR = 7;
public final static int MAP_SPACE = 15;
public final static int TILE_WIDTH = 24;
public final static int TILE_HEIGHT = 25;
public final static int CHESS_WIDTH = 9; //棋盘宽有9个格
public final static int CHESS_HEIGHT = 9; //棋盘高有9个格
public final static int RADIUS_SPACE = TILE_WIDTH >>1;
public final static int CAMP_DEFAULT = 0;
public final static int CAMP_HERO = 1; //棋盘上下黑棋的地方为1
public final static int CAMP_ENEMY = 2; //棋盘上下白棋的地方为2
public final static int CALU_ALL_COUNT = 10;
public final static int CALU_SINGLE_COUNT = 5; //连成5个
}
package cn.m.xys;
import android.app.Activity;
import android.os.Bundle;
import android.view.Display;
import android.view.KeyEvent;
import android.view.Window;
import android.view.WindowManager;
public class mainActivity extends Activity {
GameView gameView = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 隐藏标题栏
requestWindowFeature(Window.FEATURE_NO_TITLE);
// 全屏显示
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
// 获取屏幕宽高
Display display = getWindowManager().getDefaultDisplay();
// 现实GameView
GameView.init(this, display.getWidth(), display.getHeight()); // 实例化GameView
gameView = GameView.getInstance();
setContentView(gameView); //在界面上显示布局文件,一般xml的文件是用R.layout.xxx.xml
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
return super.onKeyDown(keyCode, event);
}
}