迦瓦栈队 团队第六周项目总结
贪吃蛇小游戏的Java实现_项目总结
小组成员:
·20145201 李子璇
·20145204 张亚军
·20145211 黄志远
·20145212 罗天晨
·20145224 陈颢文
团队项目总结
案例提出及工程用时
经过两天在图书馆以及网络上查阅的资料,然后提出这次课程设计的目标《贪吃蛇游戏》的设计。然后接下来就是查阅更多的资料,制定接下来的详细计划,最终计划用一个半月的时间完成此次课程设计提出的案例。
设计思路
贪吃蛇游戏设计与实现,主要分为以下二个模块:游戏主界面模块和游戏控制模块。在此只实现游戏的游戏主界面模块、游戏控制模块。并且只是实现移动速度、暂停、重新开始等功能。
游戏主界面模块:
游戏的主界面是进入游戏后,能够给玩家第一感官的部分,主要包括游戏图形区域界面、游戏的速度选择更新界面、新生方块的预览、暂停游戏按钮、重新开始游戏按钮。从很大程度上决定了玩家对游戏的兴趣问题,因此,游戏的主界面应该力求美观,爽心悦目。
游戏控制模块:
这个模块是游戏的中心环节,主要完成控制游戏的开始、暂停、重新开始等功能。为了能够给玩家一个很好的游戏环境,这部分应该做到易懂、易操作。
本设计所开发的是基于Java的一个贪吃蛇游戏软件,主要实现游戏的速度控制、游戏的暂停、游戏的退出等功能,分为: 游戏主界面模块、游戏控制模块以二个模块。
项目实现情况
我们的项目实现以基本达到了我们的既定目标:可运行、可玩、能暂停及调整难度。唯一没做到的一点就是对游戏界面的优化,我们目前的程序还是简单的方块式的视图,若是能继续改进成为动画式的相信我们的这个贪吃蛇游戏就能更加吸引人了。
窗口视图如下:
项目测试
1.窗口界面的调整:
- 制作窗口化的程序需要调用
java.awt.*
和javax.swing.*
两个包。
·JMenuBar-创建一个水平菜单栏
·JMenu-构造菜单
·JMenuItem-添加菜单项(使用add的方法添加到菜单)
·JRadioButton-添加单选按钮(使用add的方法添加到菜单)
·JPanel-Java容器面板
·GridLayout-网格化界面布局
·BorderLayout-边界布局管理器
- 下面以添加一个“帮助”按钮为例简述菜单设计:
JMenuBar caidan = new JMenuBar();
JMenu bangzhu = new JMenu("帮助");
bangzhu.setFont(f);
caidan.add(bangzhu);
Font f = new Font("宋体", Font.PLAIN, 12);
-
利用
GridLayout
进行网格化布局:
p.setLayout(new GridLayout(1, 2));
一排两列
p.setLayout(new GridLayout(2, 1));
两排一列
-
BorderLayout把窗口划分为“东(EAST)、南(SOUTH)、西(WEST)、北(NORTH)、中(CENTER)”五个区域。
- 将刚刚设计的网格放到底下:
JPanel p = new JPanel();
p.setLayout(new GridLayout(1, 2));
bt1 = new JLabel(" 长度:0");
bt1.setFont(f);
bt2 = new JLabel(" 移速:5");
bt2.setFont(f);
p.add(bt1);
p.add(bt2);
this.add(p, BorderLayout.SOUTH);
2.贪吃蛇的实现
- 蛇头与食物
· 蛇头坐标:x,y
· 食物坐标:sjx,sjy
· 在import javax.swing.*包中实现图形可视化。
蛇头与食物部分代码如下:
public void paint(Graphics g) {
//fillrect用指定的画刷填充矩形。
zx[cs] = x + "/" + y;
super.paint(g);
g.setColor(Color.BLACK);
g.drawRect(0, 0, 300, 300);//边框
g.setColor(Color.ORANGE);
g.fillRect(sjx, sjy, 10, 10);//食物
g.setColor(Color.RED);
g.fillRect(x, y, 10, 10);//头
- 蛇体的实现与绘制
· 该部分代码实现了贪吃蛇身体部分的绘制和部分游戏操作规定,其中,变量sw是身体的长度,初始值为0,当蛇头与食物的坐标重合时sw增加1。
· 其中规定:若蛇撞到自己的身体则dead=true,停止运行。
· 代码如下:
g.setColor(Color.BLACK);
for (int i = 1; i <= sw; i++) {
int j = cs - i;
if (j < 0) {
j = cs-i+1000;//
}
String[] s = zx[j].split("/");//s身体
if (x == Integer.parseInt(s[0]) && y == Integer.parseInt(s[1])) {//蛇撞到自己身上了
dead = true;
shenti = true;
}
if (sjx == Integer.parseInt(s[0]) && sjy == Integer.parseInt(s[1])) {//食物在蛇身体上
g.setColor(Color.PINK);
g.fillRect(sjx, sjy, 10, 10);
g.setColor(Color.RED);
continue;
}
g.fillRect(Integer.parseInt(s[0]), Integer.parseInt(s[1]), 10, 10);
}
}
· 贪吃蛇的方向控制:可以通过上下左右键控制贪吃蛇的移动
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case 38:
direction = 1;//上
break;
case 40:
direction = 2;//下
break;
case 37:
direction = 3;//左
break;
case 39:
direction = 4;//右
break;
case KeyEvent.VK_F3:
this.setStop();
break;
case KeyEvent.VK_F2:
this.setKaiShi();
break;
case KeyEvent.VK_F4:
this.setTuiChu();
break;
}
}
- 对贪吃蛇移动速度的设定
1.在实现贪吃蛇基本功能的基础上我们制定了新的游戏规则:每当蛇吃到一个食物的时候,蛇的移速会变快,以增加游戏的趣味性与挑战性:
if (speed <= d) {//d=150
speed = d;
}
else {
speed-=10;//速度变快
}
s.setDS(sw, 305 - speed);
}
2.在窗口界面的工具栏的难度中设置速度
else if (e.getSource() == low) {
mp.setSD(300, 110);
this.setDS(0, 305 - 300);
}
else if (e.getSource() == mid) {
mp.setSD(250, 80);
this.setDS(0, 305 - 250);
}
else if (e.getSource() == high) {
mp.setSD(200, 50);
this.setDS(0, 305 - 200);
}
else if (e.getSource() == sup) {
mp.setSD(150, 30);
this.setDS(0, 305 - 150);
}
- 游戏结束判断
1.当蛇头碰到边框时游戏结束:
2.当蛇头碰到自己身体时游戏结束:
3.贪吃蛇死亡后,可以重新开始游戏:
完整代码
package snack;
import java.awt.*;// 抽象窗口
import javax.swing.*;//图形可视包
import java.util.*;//包含日期转换、字符串处理和scanner()
import java.awt.event.*;//定义了事件和事件侦听器
public class She extends JFrame implements ActionListener, WindowListener {
JLabel bt1;
JLabel bt2;
MyPaint mp;
JMenuItem start;
JMenuItem stop;
JMenuItem end;
JRadioButton low;//创建一个初级难度选择状态的单选按钮。
JRadioButton mid;
JRadioButton high;
JRadioButton sup;
JMenuItem guanyu;
Font f;//Font f = new Font(String 字体,int 风格,int 字号);
public She() {
f = new Font("宋体", Font.PLAIN, 12);
mp = new MyPaint();
mp.setShe(this);
JMenuBar caidan = new JMenuBar();
JMenu shezhi = new JMenu("设置");
shezhi.setFont(f);
JMenu nandu = new JMenu("难度");
nandu.setFont(f);
JMenu bangzhu = new JMenu("帮助");
bangzhu.setFont(f);
caidan.add(shezhi);
caidan.add(nandu);
caidan.add(bangzhu);
start = new JMenuItem("开始游戏 F2");
start.setFont(f);
start.addActionListener(this);
stop = new JMenuItem("暂停/继续 F3");
stop.setFont(f);
stop.addActionListener(this);
end = new JMenuItem("退出 F4");
end.setFont(f);
end.addActionListener(this);
low = new JRadioButton("初级,最快速度195", true);
low.setFont(f);
low.addActionListener(this);
mid = new JRadioButton("中级,最快速度225");
mid.setFont(f);
mid.addActionListener(this);
high = new JRadioButton("高级,最快速度255");
high.setFont(f);
high.addActionListener(this);
sup = new JRadioButton("超级,最快速度275");
sup.setFont(f);
sup.addActionListener(this);
ButtonGroup bg = new ButtonGroup();
bg.add(low);
bg.add(mid);
bg.add(high);
bg.add(sup);
nandu.add(low);
nandu.add(mid);
nandu.add(high);
nandu.add(sup);
guanyu = new JMenuItem("关于");
guanyu.setFont(f);
guanyu.addActionListener(this);
shezhi.add(start);
shezhi.add(stop);
shezhi.add(end);
bangzhu.add(guanyu);
bt1 = new JLabel(" 长度:0");
bt1.setFont(f);
bt2 = new JLabel("移速:5");
bt2.setFont(f);
JPanel p = new JPanel(); //Java面板容器
p.setLayout(new GridLayout(1, 2)); //设置用户界面上的屏幕组件的格式布局,一行两列。
p.add(bt1);
p.add(bt2);
this.addWindowListener(this);//添加窗体监听器
this.setJMenuBar(caidan);//添加菜单条
this.add(p, BorderLayout.NORTH);//边界布局管理器把容器的的布局分为五个位置
this.add(mp);
this.add(new JLabel(" "), BorderLayout.WEST);
this.setSize(335, 385);
this.setResizable(false);
this.setLocation(300, 150);
this.setTitle("迦瓦栈队");
this.setVisible(true);//使窗口可见
}
public void setDS(int a, int b) {//吃到食物
bt1.setText(" 长度:" + a);
bt2.setText("移速:" + b);
}
public void setChu() {
low.setSelected(true);
mp.setSD(300, 110);
this.setDS(0, 305 - 300);
low.setEnabled(true);//选项选择权限
mid.setEnabled(true);
high.setEnabled(true);
sup.setEnabled(true);
}
public void setNanDu() {
low.setEnabled(false);
mid.setEnabled(false);
high.setEnabled(false);
sup.setEnabled(false);
}
public void actionPerformed(ActionEvent e) {//动作事件
if (e.getSource() == stop) { //菜单选项触发
mp.setStop();
}
else if (e.getSource() == start) {
mp.setKaiShi();
}
else if (e.getSource() == end) {
mp.setTuiChu();
}
else if (e.getSource() == guanyu) {
JLabel gy = new JLabel("感谢使用!小组成员:黄志远、陈颢文、张亚军、罗天晨、李子璇");
gy.setFont(f);
JOptionPane.showMessageDialog(this, gy);
}
else if (e.getSource() == low) {
mp.setSD(300, 110);
this.setDS(0, 305 - 300);
}贪吃蛇的方向控制
else if (e.getSource() == mid) {
mp.setSD(250, 80);
this.setDS(0, 305 - 250);
}
else if (e.getSource() == high) {
mp.setSD(200, 50);
this.setDS(0, 305 - 200);
}
else if (e.getSource() == sup) {
mp.setSD(150, 30);
this.setDS(0, 305 - 150);
}
}
public static void main(String[] args) {
new She();
}
public void windowOpened(WindowEvent e) {//它是在窗口首次出现时发生该事件
// TODO: Add your code here
}
public void windowClosing(WindowEvent e) {//是java swing中窗口关闭事件的监听
System.exit(0);
}
public void windowClosed(WindowEvent e) {
}
public void windowIconified(WindowEvent e) {
}
public void windowDeiconified(WindowEvent e) {
}
public void windowActivated(WindowEvent e) {
}
public void windowDeactivated(WindowEvent e) {
// TODO: Add your code here
}
}
class MyPaint extends JPanel implements Runnable, KeyListener, FocusListener {
She s;//蛇?
Font f = new Font("楷体", Font.PLAIN, 12);
int x = 150;
int y = 0;
Random r = new Random();
int sjx = r.nextInt(300)/10*10;
int sjy = r.nextInt(300)/10*10;
int direction = 2;//方向
int sw = 0;
int speed = 300;
int cs = 0;
int d = 150;
String[] zx = new String[1000];
boolean stop = false;
boolean stop1 = false;
boolean dead = false;
boolean shenti = false;
public MyPaint() {
Thread t = new Thread(this);
t.start();
this.addKeyListener(this);
this.setFocusable(true);
this.addFocusListener(this);
}
public void setShe(She ss) {
s = ss;
}
public void setStop() {
if (stop) {
stop = false;
}
else {
stop = true;
}
}
public void setKaiShi() {
JLabel ks = new JLabel("是否重新开始?");
ks.setFont(f);
int a = JOptionPane.showConfirmDialog(s, ks);//s为Component parentComponent
if (a == 0) {
x = 150;//蛇
y = 0;
sjx = r.nextInt(300)/10*10;//食物x
sjy = r.nextInt(300)/10*10;
direction = 2;//方向???
sw = 0;//重置长度
speed = 300;//重置速度
cs = 0;
stop = false;
dead = false;
shenti = false;
s.setChu();
s.setDS(sw, 305 - speed);//重置长度、移动
}
}
public void setTuiChu() {
JLabel tc = new JLabel("你确定退出吗?");
tc.setFont(f);//字体(之前定义)
int a = JOptionPane.showConfirmDialog(s, tc);//s为Component parentComponent
if (a == 0) {
s.dispose();//释放组件COMPOENT使其内存返回到操作系统
System.exit(0);
}
}
public void setSD(int a, int b) {
speed = a;
d = b;
}
public void paint(Graphics g) {
//fillrect用指定的画刷填充矩形。
zx[cs] = x + "/" + y;
super.paint(g);
g.setColor(Color.BLACK);
g.drawRect(0, 0, 300, 300);//边框
g.setColor(Color.ORANGE);
g.fillRect(sjx, sjy, 10, 10);//食物
g.setColor(Color.RED);
g.fillRect(x, y, 10, 10);//头
g.setColor(Color.BLACK);
for (int i = 1; i <= sw; i++) {
int j = cs - i;
if (j < 0) {
j = cs-i+1000;//
}
String[] s = zx[j].split("/");//s身体
if (x == Integer.parseInt(s[0]) && y == Integer.parseInt(s[1])) {//蛇撞到自己身上了
dead = true;
shenti = true;
}
if (sjx == Integer.parseInt(s[0]) && sjy == Integer.parseInt(s[1])) {//食物在蛇身体上
g.setColor(Color.PINK);
g.fillRect(sjx, sjy, 10, 10);
g.setColor(Color.RED);
continue;
}
g.fillRect(Integer.parseInt(s[0]), Integer.parseInt(s[1]), 10, 10);
}
}
public void run() {
while (true) {
cs++;
if (cs == 1000) {
cs = 0;
}
switch (direction) {//方向
case 1:
y-=10;//上
break;
case 2:
y+=10;//下
break;
case 3:
x-=10;//左
break;
case 4:
x+=10;//右
break;
}
this.repaint();//更新视图区域的请求
if (x == sjx && y == sjy) {
sjx = r.nextInt(300)/10*10;
sjy = r.nextInt(300)/10*10;
sw++;//身体长度
if (sw == 1) {
s.setNanDu();
}
if (speed <= d) {//d=150
speed = d;
}
else {
speed-=10;//速度变快
}
s.setDS(sw, 305 - speed);
}
if (x < 0 || x > 290 || y < 0 || y > 290 || shenti) {
JOptionPane.showMessageDialog(s, "游戏结束!");
dead = true;
shenti = false;
}
while (stop || stop1) {
try {
Thread.sleep(100);
}
catch (Exception e) {
}
}
while (dead) {
try {
Thread.sleep(100);
}
catch (Exception e) {
}
}
try {
Thread.sleep(speed);
}
catch (Exception e) {
}
}
}
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case 38:
direction = 1;//上
break;
case 40:
direction = 2;//下
break;
case 37:
direction = 3;//左
break;
case 39:
direction = 4;//右
break;
case KeyEvent.VK_F3:
this.setStop();
break;
case KeyEvent.VK_F2:
this.setKaiShi();
break;
case KeyEvent.VK_F4:
this.setTuiChu();
break;
}
}
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
}
public void focusGained(FocusEvent e) {
stop1 = false;
}
public void focusLost(FocusEvent e) {
stop1 = true;
}
}
项目实现中的问题与解决
一开始的时候我们也全都是一头雾水,去图书管里看到书上有关贪吃蛇的程序简介,便匆匆决定要做这个项目。结果当我们把书上代码全都抄上去后发现程序并不能很好运行。最后只能到网上寻求帮助,找了些简单易懂的代码逐条语句的去看,遇到不懂的语句就百度,直到能将每一行代码都看会。然后在结合书上的代码对网上的程序进行改进,不断尝试,最终才写成了以上的全部代码。
项目的展望与改进
其实贪吃蛇已经是个很经典的小游戏了,可改进的空间也不是很大,如果非要改进的话我觉得可以打破传统,加入两条蛇,让他们去抢同一个食物。或者将程序在安卓平台上实现。
团队成员成绩分配
学号 | 姓名 | 成绩 |
---|---|---|
20145224 | 陈颢文 | 30 |
20145211 | 黄志远 | 27 |
20145204 | 张亚军 | 25 |
20145212 | 罗天晨 | 23 |
20145201 | 李子璇 | 20 |