9 Star 31 Fork 13

雨碎江南 / JavaSimpleFiveCheese

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
MIT

Java五子棋项目

该项目主要使用到了swing编程和ImageIO类。

  • 游戏开始:清空棋盘,重新绘图
  • 游戏设置:设置倒计时
  • 游戏说明:说明游戏规则
  • 认输:某一方放弃游戏,另一方直接获胜
  • 关于:显示程序的作者、版本等
  • 退出:结束程序

##五子棋游戏的功能##

  1. 用户点击鼠标时,将棋子渲染到相应的位置;
  2. 自动判断游戏是否结束。
  3. 对游戏时间进行设置,判断是否超出规定时间。

实现步骤

  1. 渲染游戏界面(swing)。新建一个类FiveCheeseFrame继承JFrame并实现MouseListener接口。在构造方法中初始化界面(大小、位置等)。
  2. 在鼠标事件的mouseClick()方法中响应相关事件。
  3. 为了实现倒计时,需要单独引入一个线程。

该游戏的难点:

1.如何让游戏界面居中显示?

获取屏幕的宽高和游戏的宽高。设置显示位置为屏幕的宽高减去游戏的宽高除以2就行了。

// 取得屏幕的宽高
int screenWidth = Toolkit.getDefaultToolkit().getScreenSize().width;
int screenHeight = Toolkit.getDefaultToolkit().getScreenSize().height;
System.out.println("屏幕的尺寸是:" + screenWidth + "*" + screenHeight);
// 设置游戏界面居中显示,jf是自定义的窗体类,继承自JFrame
jf.setLocation((screenWidth - WIDTH) / 2, (screenHeight - HEIGHT) / 2);

2.在鼠标点击的位置显示棋子?

MouseEvent的getX()getY()方法可以获得点击的位置。黑子可以用实心的黑圆表示,白字可以用空心黑圆+实心白圆表示。Graphics类的drawOval()fillOval()方法。

3.之前下过的棋子的位置如何保存?

通过19*19的二维数组allChess保存每个交叉点的状态(黑子(填充1)、白子(填充2)、无子(填充0))。

4.在窗口中绘制网格和图像

Graphics类,类似画笔,可以在窗口中绘制文字和图像。通过覆写JFrame中的paint()方法来使用,通过repaint()方法(表示重新执行一次paint方法)来调用。

Graphics g;
BufferedImage image;
try {
	image = ImageIO.read(new File("images/title.jpg")); //解决窗体透明问题只需要在窗体中绘制一张图片,在此基础上绘制其他
	g.drawImage(image, 20, 40,this);
} catch (IOException e) {
	e.printStackTrace();
}

5.如何让棋子对齐到十字线?

棋盘绘制完成后,实际上就建立了如下的一个坐标轴为018的坐标系坐标系。 对于每一个鼠标点击的点,我们可以取离鼠标点击位置最近的点。关键就是得到018的整数。将大坐标转换为小坐标。

思考

每个单元格是25*25像素,我们惠子棋盘的时候采用的是for循环。

for (int i = 0; i < 19; i++) {
	g.drawLine(15, 70 + 25 * i, 465, 70 + 25 * i); // 绘制横线
	g.drawLine(15 + 25 * i, 70, 15 + 25 * i, 520);// 绘制竖线
}

我们通过鼠标监听器得到点击位置的坐标,然后分别减去起始位置,再除以25,就可以得到一个0~18的数,然后通过这个数字,就可以让其精确对准棋盘。

x = e.getX(); // 得到鼠标的点击位置
y = e.getY();

x = (x - 15) / 25; // 得到最近的十字点,可以先得到0~18的整数,然后通过这个整数乘以每行的间距完成
y = (y - 70) / 25;

6.如何判断游戏胜负?

依据:五子棋的游戏规则:是否有同一种颜色的棋子连成5个。判断当前棋子周围是否有5个连续一样棋子——一共需要判断4种情况:水平方向、垂直方向和两条对角线的方向。其中每一种情况又需要判断两次,例如水平方向需要从左向右和从右向左进行判断。 首先得到当前的棋子的颜色color即二维数组allCheese[x][y]

水平方向:y坐标不变,x坐标每次增加1或者减去1.

// 横向判断
int i = 1;
while (color==allChess[x+i][y]) { // 向右判断
	count++;
	i++;
}
i = 1; // i归位,特别注意这里
while (color == allChess[x-i][y]) { // 向左判断
	count++;
	i++;
}
if (count >= 5) {
	flag = true; // flag表示是否有5子相连
}

同理,垂直方向仅仅是x坐标不变,y的坐标每次加1,减1。和上面的算法几乎一样

左上-右下方向:如果判断方向是“左上--->右下”,则x和y的坐标每次都加1;如果从右下判断到左上,则x和y的坐标每次加1,这里同样要注意变量i要复位。

// 左下-右上判断
int i3 = 1;
int count3 = 1;
while (color==allChess[x+i3][y-i3]) { // 左下--->右上---> allChess[x+i3][y-i3]
	count3++;
	i3++;
}
i3 = 1; // i3归位
while (color == allChess[x-i3][y+i3]) { // 右上--->左下---> allChess[x-i3][y+i3]
	count3++;
	i3++;
}
if (count3 >= 5) {
	flag = true;
}

右上-左下方向的判断和左上-右下的判断类似,只是x,y的变化是异号的。

思考

上面的4个方向的算法大体类似,我们可以将其抽象为一个方法。观察到以上4方向上的算法的不同之处就是x坐标和y轴的变化率,同时它们都需要判断当前棋子的颜色color,得到当前相同棋子相连的最大数count,将算法加以抽象得到如下的方法:

/**
 * 得到相同的棋子连接的数量,由上面的4次判断抽象出来的
 * @param xChange:x的变化
 * @param yChange:y的变化
 * @param color:当前棋子状态(黑?白)
 * @return
 */
private int checkCount(int xChange,int yChange,int color){
	
	int count = 1;
	int tempX = xChange, tempY = yChange; // 保存传过来的xChange和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;
}

得到棋子数只需要这样调用以上的方法:

/* 判断4个方向上的棋子数,判断的顺序不重要 */
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;
			}
		}
	}
}

7. 如何设置倒计时##

让窗体类实现Runnable接口,在构造方法中启动线程,并将其挂起。因为游戏刚刚启动的时候并不需要倒计时,只需要用户点击设置游戏时间之后倒计时才开始生效。声明两个全局变量blackTimewhiteTime分别保存黑方和白方额度剩余时间(初始值为0)。用户点击”游戏设置“按钮后,将时间赋值给这两个变量,重新开始游戏(清空棋盘),并将挂起的线程恢复(resume()方法)。在run()方法中首先判断是否有时间的限制,再进一步判断,当前棋子的状态,给对应的时间每隔1秒减去1,当时间变为0的时候,给用户提示,显示输赢信息,并设置不能够再次下棋。需要将时间同步显示到界面。

8.背景音乐的控制

新引入一个背景音乐的线程,在主界面渲染(窗体的构造方法中)的时候启动线程。引入一个全局变量布尔控制背景音乐的开关,当用户点击音乐的时候改变状态,并将背景音乐挂起或者恢复。

The MIT License (MIT) Copyright (c) 2015 高鹏飞 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

简介

java实现的简单五子棋游戏,主要用到了JFrame、鼠标事件和线程的相关知识。 展开 收起
Java
MIT
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
Java
1
https://gitee.com/gaopengfei/JavaSimpleFiveCheese.git
git@gitee.com:gaopengfei/JavaSimpleFiveCheese.git
gaopengfei
JavaSimpleFiveCheese
JavaSimpleFiveCheese
master

搜索帮助

14c37bed 8189591 565d56ea 8189591