游戏功能:
1.在点击鼠标时,可以在相应的位置显示棋子
2.可以自动判断游戏是否结束<哪方胜利>.
3.对游戏时间进行设置(单位为分钟),判断玩家是否超出设定时间.超时者失败.
4.玩家可在游戏结束前认输.
开发流程:
开发游戏界面.
系统的游戏界面依靠背景图片处理,在窗体中显示图片的方法:
创建背景图片对象
// 背景图片
BufferedImage bgImage = null;
初始化图片代码
try {
bgImage = ImageIO.read(new File("/images/bkground.jpg"));
} catch (IOException e) {
e.printStackTrace();
}
初重写paint(Graphics gc)方法,在该方法中加入如下代码:
g.drawImage(bgImage, 0, 25, this);
解决窗口显示未加载问题
this.repaint();
计算期盼中每条线的间距:用19*19的围棋棋盘.
总宽度为360像素分成18分.每份20像素.
// 画棋盘<19*19的围棋棋盘>
for (int i = 0; i < 19; i++) {
// 画横线时,横坐标不变,两个点的纵坐标每次增加20个像素
g.drawLine(10, 75 + 20 * i, 370, 75 + 20 * i);
// 画竖线,纵坐标不变,两个点的横坐标每次增加20个像素
g.drawLine(10 + 20 * i, 75, 10 + 20 * i, 435);
}
// 标注棋盘上的点位
g.fillOval(68, 133, 4, 4);
g.fillOval(308, 133, 4, 4);
g.fillOval(308, 373, 4, 4);
g.fillOval(68, 373, 4, 4);
g.fillOval(188, 133, 4, 4);
g.fillOval(68, 253, 4, 4);
g.fillOval(188, 373, 4, 4);
g.fillOval(308, 253, 4, 4);
g.fillOval(188, 253, 4, 4);
在棋盘上相应位置落子
黑子:一个实心的空圆表示
白字: 一个空心的黑圆+一个实心的白圆表示
// 绘制棋子
for (int i = 0; i < 19; i++) {
for (int j = 0; j < 19; j++) {
// 10为棋盘最左边的线条距离窗体的边距为10像素.20为线条之间的宽度
int tempX = i * 20 + 10;
// 75为棋盘最上边的线条距离窗体的边距为75像素.20为线条之间的宽度
int tempY = j * 20 + 15;
if (allChess[i][j] == 1) {// 如果当前落子条件为黑棋
// 绘制黑色实心圆
g.fillOval(tempX - 7, tempY - 7, 16, 16);
}
if (allChess[i][j] == 2) {// 如果当前落子条件为白棋
// 绘制白色空心圆
g.setColor(Color.WHITE);
g.fillOval(tempX - 7, tempY - 7, 16, 16);
g.setColor(Color.BLACK);
// 绘制空心黑圆给白子加边框
g.drawOval(tempX - 7, tempY - 7, 16, 16);
}
}
}
repaint()方法:重新执行一次paint()方法.
保存之前下过的所有棋子
定义19* 19二维数组
// 数组中的内容为0表示没有棋子,为1表示为黑子,为2表示为白子.
int[][] allChess = new int[19][19];
判断胜负
依据五子棋的基本游戏规则,判断是否有同一颜色的棋子连成5个.
五子棋核心算法
private boolean checkWin() {
boolean flag = false;
int color = allChess[x][y];
// 判断横向是否有5个相连的棋子
int i1 = 1; // i为步进变量
int count1 = 1; // count用来统计棋子个数
/** 左 ———>右 */
while (color == allChess[x + i1][y]) {// 横向向右判断
count1++;
i1++;
}
i1 = 1;
/** 右 ———>左 */
while (color == allChess[x - i1][y]) {// 横向向左判断
count1++;
i1++;
}
if (count1 >= 5) {
flag = true;
}
// 判断纵向是否有5个相连的棋子
int count2 = 1;
int i2 = 1;
/** 上———>下 */
while (color == allChess[x][y + i2]) {
count2++;
i2++;
}
i2 = 1;
/** 下———>上 */
while (color == allChess[x][y - i2]) {
count2++;
i2++;
}
if (count2 >= 5) {
flag = true;
}
// 判断左斜向是否有5个相连的棋子
int count3 = 1;
int i3 = 1;
/** 左上———>右下 */
while (color == allChess[x + i3][y + i3]) {
count3++;
i3++;
}
i3 = 1;
/** 右下———>左上 */
while (color == allChess[x - i3][y - i3]) {
count3++;
i3++;
}
if (count3 >= 5) {
flag = true;
}
// 判断右斜向是否有5个相连的棋子
int count4 = 1;
int i4 = 1;
/** 左下———>右上 */
while (color == allChess[x + i4][y - i4]) {
count4++;
i4++;
}
i4 = 1;
/** 右上———>左下 */
while (color == allChess[x - i4][y + i4]) {
count4++;
i4++;
}
if (count4 >= 5) {
flag = true;
}
// }
return flag;
}
上面的算法可以实现,但是重复代码太多需要改进.
private int checkCount(int xChange, int yChange, int color) {
int count = 1;
int tempX = xChange;
int tempY = yChange;
while ((x + xChange >= 0) && (x + xChange <= 18) && (y + yChange >= 0)
&& (y + yChange <= 18)
&& (color == allChess[x + xChange][y + yChange])) {
count++;
if (xChange != 0) {
xChange++;
}
if (yChange != 0) {
if (yChange > 0) {
yChange++;
} else {
yChange--;
}
}
}
xChange = tempX;
yChange = tempY;
while ((x - xChange >= 0) && (x - xChange <= 18) && (y - yChange >= 0)
&& (y - yChange <= 18)
&& (color == allChess[x - xChange][y - yChange])) {
count++;
if (xChange != 0) {
xChange++;
}
if (yChange != 0) {
if (yChange > 0) {
yChange++;
} else {
yChange--;
}
}
}
return count;
}
处理屏幕闪烁问题
双缓冲技术:用在手机游戏中最多,原因是手机内存相对较小,闪烁问题比较明显
public void paint(Graphics gc) {
// 雙緩衝技術,解決屏幕閃爍問題
BufferedImage bi = new BufferedImage(500, 500,
BufferedImage.TYPE_INT_ARGB);
Graphics g = bi.createGraphics();
g.setColor(Color.BLACK);
// 绘制背景
g.drawImage(bgImage, 0, 25, this);
// 输出标题信息
g.setFont(new Font("黑体", Font.BOLD, 20));
g.drawString("游戏信息: " + message, 130, 65);
// 输出时间信息
g.setFont(new Font("宋体", Font.PLAIN, 14));
g.drawString("黑方时间: " + blackMessage, 30, 475);
g.drawString("白方时间: " + whiteMessage, 260, 475);
// 画棋盘<19*19的围棋棋盘>
for (int i = 0; i < 19; i++) {
// 画横线时,横坐标不变,两个点的纵坐标每次增加20个像素
g.drawLine(10, 75 + 20 * i, 370, 75 + 20 * i);
// 画竖线,纵坐标不变,两个点的横坐标每次增加20个像素
g.drawLine(10 + 20 * i, 75, 10 + 20 * i, 435);
}
// 标注棋盘上的点位
g.fillOval(68, 133, 4, 4);
g.fillOval(308, 133, 4, 4);
g.fillOval(308, 373, 4, 4);
g.fillOval(68, 373, 4, 4);
g.fillOval(188, 133, 4, 4);
g.fillOval(68, 253, 4, 4);
g.fillOval(188, 373, 4, 4);
g.fillOval(308, 253, 4, 4);
g.fillOval(188, 253, 4, 4);
// 绘制棋子
for (int i = 0; i < 19; i++) {
for (int j = 0; j < 19; j++) {
// 10为棋盘最左边的线条距离窗体的边距为10像素.20为线条之间的宽度
int tempX = i * 20 + 10;
// 75为棋盘最上边的线条距离窗体的边距为75像素.20为线条之间的宽度
int tempY = j * 20 + 15;
if (allChess[i][j] == 1) {// 如果当前落子条件为黑棋
// 绘制黑色实心圆
g.fillOval(tempX - 7, tempY - 7, 16, 16);
}
if (allChess[i][j] == 2) {// 如果当前落子条件为白棋
// 绘制白色空心圆
g.setColor(Color.WHITE);
g.fillOval(tempX - 7, tempY - 7, 16, 16);
g.setColor(Color.BLACK);
// 绘制空心黑圆给白子加边框
g.drawOval(tempX - 7, tempY - 7, 16, 16);
}
}
}
gc.drawImage(bi, 0, 0, this);
}
实现各个按钮的功能
开始游戏:从新开始游戏
游戏设置:设置游戏倒计时
游戏说明:用来说明游戏规则和操作
认输:表示某一方放弃游戏,投子认负
关于:用来显示程序的作者或者编写单位的相关说明
退出:退出游戏
游戏代码
package org.avlispu.game.frame;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
/**
*
* @author 苏若年 <a href='dennisit@163.com'> Email:dennisit@163.com</a>
*
* @verson 1.0
*
* @create time:2012-1-15 2012-1-15
*
* @TODO
*
*/
public class FiveChessFrame extends JFrame implements MouseListener, Runnable {
// 获取屏幕宽度
int width = Toolkit.getDefaultToolkit().getScreenSize().width;
// 获取屏幕高度
int height = Toolkit.getDefaultToolkit().getScreenSize().height;
// 背景图片
BufferedImage bgImage = null;
// x,y用来保存棋子落下的坐标
int x = 0, y = 0;
// 保存之前下过的全部棋子的坐标,
// 数组中的内容为0表示没有棋子,为1表示为黑子,为2表示为白子.
int[][] allChess = new int[19][19];
// 定义一个标记表示当前应该黑棋还是白棋下下一步
boolean isBlack = true;
// 标识当前游戏是否可以继续
boolean canPlay = true;
// 顯示提示信息
String message = "黑方先行";
// 保存最多拥有多少时间(秒)
int maxTime = 0;
// 应用线程类做倒计时。
Thread thread = null;
// 保存黑方与白方的剩余时间
int blackTime = 0;
int whiteTime = 0;
// 保存双方剩余时间的现实信息
String blackMessage = "无限制.";
String whiteMessage = "无限制.";
// 定义当前 线程是否运行的标志
boolean flagThread = false;
/**
* 构造方法
*/
public FiveChessFrame() {
//初始化界面
init();
// 构建对象时启动线程,并将线程挂起.
thread.start();
// 挂起线程
thread.suspend();
}
/**
* 初始化界面的方法
*/
public void init() {
// 设置标题
this.setTitle("五子棋");
// 设置窗体的大小
this.setSize(500, 500);
// 设置窗体出现的位置
this.setLocation((width - 500) / 2, (height - 500) / 2);
// 设置窗体的大小为不可变
this.setResizable(false);
// 设置窗体的关闭方式为默认关闭后程序结束
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 为窗体添加监听器
this.addMouseListener(this);
// 设置窗体可以显示出来
this.setVisible(true);
thread = new Thread(this);
// 初始化图片
try {
bgImage = ImageIO.read(new File("images/bkground.jpg"));
} catch (IOException e) {
e.printStackTrace();
}
//在创建窗体时,重新绘制一边,以解决窗体初始显示的未加载问题.
this.repaint();
}
/**
* 覆写paint()方法,绘制背景,棋盘、棋子
*/
public void paint(Graphics gc) {
// 双缓冲技术,解决屏幕闪烁问题.
BufferedImage bi = new BufferedImage(500, 500,
BufferedImage.TYPE_INT_ARGB);
Graphics g = bi.createGraphics();
// 绘制背景
g.drawImage(bgImage, 0, 20, this);
// 输出标题信息
g.setColor(Color.BLACK);
g.setFont(new Font("黑体", Font.BOLD, 20));
g.drawString("游戏信息: " + message, 130, 65);
// 输出时间信息
g.setFont(new Font("宋体", Font.PLAIN, 14));
g.drawString("黑方时间: " + blackMessage, 30, 475);
g.drawString("白方时间: " + whiteMessage, 260, 475);
// 画棋盘<19*19的围棋棋盘>
for (int i = 0; i < 19; i++) {
// 画横线时,横坐标不变,两个点的纵坐标每次增加20个像素
g.drawLine(10, 75 + 20 * i, 370, 75 + 20 * i);
// 画竖线,纵坐标不变,两个点的横坐标每次增加20个像素
g.drawLine(10 + 20 * i, 75, 10 + 20 * i, 435);
}
// 标注棋盘上的点位
g.fillOval(68, 133, 4, 4);
g.fillOval(308, 133, 4, 4);
g.fillOval(308, 373, 4, 4);
g.fillOval(68, 373, 4, 4);
g.fillOval(188, 133, 4, 4);
g.fillOval(68, 253, 4, 4);
g.fillOval(188, 373, 4, 4);
g.fillOval(308, 253, 4, 4);
g.fillOval(188, 253, 4, 4);
// 绘制棋子
for (int i = 0; i < 19; i++) {
for (int j = 0; j < 19; j++) {
// 10为棋盘最左边的线条距离窗体的边距为10像素.20为线条之间的宽度
int tempX = i * 20 + 11;
//75为棋盘最上边的线条距离窗体的边距为75像素.20为线条之间的宽度
int tempY = j * 20 + 76;
if (allChess[i][j] == 1) {// 如果当前落子条件为黑棋
// 绘制黑色实心圆
g.fillOval(tempX - 7, tempY - 7, 16, 16);
}
if (allChess[i][j] == 2) {// 如果当前落子条件为白棋
// 绘制白色空心圆
g.setColor(Color.WHITE);
g.fillOval(tempX - 7, tempY - 7, 16, 16);
g.setColor(Color.BLACK);
// 绘制空心黑圆给白子加边框
g.drawOval(tempX - 7, tempY - 7, 16, 16);
}
}
}
//双缓冲技术的应用.
gc.drawImage(bi, 0, 0, this);
}
/**
* 鼠标按下的方法
*/
@Override
public void mousePressed(MouseEvent e) {
// 获取鼠标按下的x坐标
x = e.getX();
// 获取鼠标按下的y坐标
y = e.getY();
// 如果落子在棋盘范围之内<x(10,370), y(75,435)为了边界下棋,在边界处范围增加5像素>
if (x >= 5 && x <= 375 && y >= 68 && y <= 442) {
if (canPlay == true) {
// 使用算法判断鼠标点击处距离哪个交叉点最近,
x = (x - 11) / 20;
y = (y - 76) / 20;
// 如果当前位置没有棋子
if (allChess[x][y] == 0) {
// 判断下的棋子的颜色
if (isBlack == true) {// 如果为黑棋走子
allChess[x][y] = 1;
isBlack = false;
message = "轮到白方";
} else {// 如果为白棋走子
allChess[x][y] = 2;
isBlack = true;
message = "轮到黑方";
}
// 使用repaint方法表示重新执行一次paint()方法实现棋子绘制的调用
this.repaint();
// 判断该子是否其其他同色子连成5连,即判断游戏是否结束
boolean winFlag = this.checkWin();
if (winFlag) {
String win = (allChess[x][y] == 1 ? "黑方" : "白方") + "获胜";
message = win;
this.repaint();
canPlay = false;
JOptionPane.showMessageDialog(this, "游戏结束:" + win);
}
}/*
* else { JOptionPane.showMessageDialog(this,
* "当前位置已经有棋子,请重新落子"); }
*/
} else {
JOptionPane.showMessageDialog(this, "游戏已经结束!");
}
}
// 如果鼠标落在开始按钮区
if (x >= 397 && x <= 471 && y >= 117 && y <= 146) {
int result = JOptionPane.showConfirmDialog(this, "确认要重新开始游戏?");
if (0 == result) {
// 确认重新开始
allChess = new int[19][19]; // 清除棋盘上所有棋子
message = "黑方先行"; // 回复提示消息为最初状态
isBlack = true; // 设置初始为黑棋先下
canPlay = true; // 初始到默认可以下子状态
blackMessage = "无限制.";
whiteMessage = "无限制.";
if (flagThread) {
thread.suspend();
flagThread = false;
}
thread = new Thread(this);
thread.start();
thread.suspend();
this.repaint(); // 重新绘制棋盘
}
}
// 如果鼠标落在游戏设置按钮区
if (x >= 397 && x <= 471 && y >= 160 && y <= 188) {
String info = "请输入游戏游戏的最大时间(单位:分钟)" + "\n如果输入数字为0表示没有时间限制:";
String input = JOptionPane.showInputDialog(info);
if (input.matches("\\d+")) {
maxTime = Integer.parseInt(input) * 60;
if (maxTime == 0) {
blackMessage = "无限制.";
whiteMessage = "无限制.";
if (flagThread) {
thread.suspend();
flagThread = false;
}
} else {
blackTime = maxTime;
whiteTime = maxTime;
flagThread = true;
thread.resume();
}
int result = JOptionPane.showConfirmDialog(this,
"设置完成,是否重新开始游戏");
if (0 == result) {
// 确认重新开始
allChess = new int[19][19]; // 清除棋盘上所有棋子
message = "黑方先行"; // 回复提示消息为最初状态
isBlack = true; // 设置初始为黑棋先下
canPlay = true; // 初始到默认可以下子状态
}
this.repaint();
} else {
JOptionPane.showMessageDialog(this, "请输入正确的参数.");
}
}
// 如果鼠标落在游戏说明按钮区
if (x >= 397 && x <= 471 && y >= 204 && y <= 234) {
String info = "\n游戏说明:" + "\n |-- 单击版\"五子棋无敌手\""
+ "\n |-- 游戏采用双人对战模式" + "\n |-- 点击设置设置游戏时间";
JOptionPane.showMessageDialog(this, info);
}
// 如果鼠标落在认输按钮区
if (x >= 397 && x <= 471 && y >= 232 && y <= 321) {
int result = JOptionPane.showConfirmDialog(this, "确认要认输吗?");
if (0 == result) {// 如果认输
if (isBlack == true) {
// 如果当前为黑棋下子状态
message = "黑棋认输";
this.repaint();
JOptionPane.showMessageDialog(this, "黑棋认输,游戏结束!");
} else {
message = "白棋认输";
this.repaint();
JOptionPane.showMessageDialog(this, "白棋认输,游戏结束!");
}
// 游戏结束,不能再下子
canPlay = false;
}
}
// 如果鼠标落在关于按钮区
if (x >= 397 && x <= 471 && y >= 336 && y <= 364) {
String info = "\n游戏 : \"五子棋无敌手\" " + "\n |-- 作者 : 墨少白"
+ "\n |-- Q Q : 799089378" + "\n |-- 邮箱 : Alivspu@163.com";
JOptionPane.showMessageDialog(this, info);
}
// 如果鼠标落在退出按钮区
if (x >= 397 && x <= 471 && y >= 384 && y <= 415) {
String info = "\n应用提示" + "\n --- 系统退出 ---";
JOptionPane.showMessageDialog(this, info);
System.exit(0);
}
}
/**
* 五子棋输赢判断的核心算法
*
* @return
*/
private boolean checkWin() {
boolean flag = false;
int color = allChess[x][y];
// 判断横向
int count = checkCount(1, 0, color);
if (count >= 5) {
flag = true;
} else {
// 判读纵向
count = checkCount(0, 1, color);
if (count >= 5) {
flag = true;
} else {
// 判读左下<-->右上方向
count = checkCount(1, -1, color);
if (count >= 5) {
flag = true;
} else {
// 判读左上<-->右下方向
count = checkCount(1, 1, color);
if (count >= 5) {
flag = true;
}
}
}
}
return flag;
}
/**
* 计算与当前落子颜色相同的相连棋子数目
*
* @param xChange棋子x方向的步进量
* @param yChange棋子y方向的步进量
* @param color
* 当前落子的颜色
* @return
*/
private int checkCount(int xChange, int yChange, int color) {
int count = 1;
int tempX = xChange;
int tempY = yChange;
while ((x + xChange >= 0) && (x + xChange <= 18) && (y + yChange >= 0)
&& (y + yChange <= 18)
&& (color == allChess[x + xChange][y + yChange])) {
count++;
if (xChange != 0) {
xChange++;
}
if (yChange != 0) {
if (yChange > 0) {
yChange++;
} else {
yChange--;
}
}
}
xChange = tempX;
yChange = tempY;
while ((x - xChange >= 0) && (x - xChange <= 18) && (y - yChange >= 0)
&& (y - yChange <= 18)
&& (color == allChess[x - xChange][y - yChange])) {
count++;
if (xChange != 0) {
xChange++;
}
if (yChange != 0) {
if (yChange > 0) {
yChange++;
} else {
yChange--;
}
}
}
return count;
}
/**
* 覆写Runnable中的run()方法.实现时间变化
*/
@Override
public void run() {
if (blackTime > 0 || whiteTime > 0) {
if (flagThread) {
while (true) {
if (isBlack == true && canPlay == true) {
// 如果当前为黑棋下子状态
blackTime--;
if (blackTime < 0) {
message = "黑方超时";
canPlay = false;
this.repaint();
JOptionPane.showMessageDialog(this, "黑方超时,游戏结束");
break;
}
} else if (isBlack != true && canPlay == true) {
// 如果当前为白棋下子状态
whiteTime--;
if (whiteTime < 0) {
message = "白方超时";
canPlay = false;
this.repaint();
JOptionPane.showMessageDialog(this, "白方超时,游戏结束");
break;
}
} else {
break;
}
blackMessage = blackTime / 3600 + ":"
+ (blackTime / 60 - blackTime / 3600 * 60) + ":"
+ blackTime % 60;
whiteMessage = whiteTime / 3600 + ":"
+ (whiteTime / 60 - whiteTime / 3600 * 60) + ":"
+ whiteTime % 60;
try {
Thread.sleep(1000);// 睡眠1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
this.repaint();
}
} else {
blackMessage = "无限制.";
whiteMessage = "无限制.";
this.repaint();
}
}
}
@Override
public void mouseClicked(MouseEvent e) {
System.out.println("鼠标单击,当前鼠标的位置为:<"+e.getX()+","+e.getY()+">");
}
@Override
public void mouseReleased(MouseEvent e) {
System.out.println("鼠标松开");
}
@Override
public void mouseEntered(MouseEvent e) {
System.out.println("鼠标进入");
}
@Override
public void mouseExited(MouseEvent e) {
System.out.println("鼠标离开");
}
}
主方法
package org.avlispu.game.main;
import org.avlispu.game.frame.FiveChessFrame;
/**
*
* @author 苏若年 <a href='dennisit@163.com'> Email:dennisit@163.com</a>
*
* @verson 1.0
*
* @create time:2012-1-15 2012-1-15
*
* @TODO
*
*/
public class FiveChess {
public static void main(String[] args) {
FiveChessFrame mf = new FiveChessFrame();
}
}
转载请注明出处:[http://www.cnblogs.com/dennisit/archive/2013/02/16/2913279.html]