一、各个类之间的关系
之前学软件工程不努力,现在徒伤悲啊,不会用visio画类图…大致的类、方法之间的关系就是这样。
二、游戏界面
三、具体实现:
1.游戏窗口
要考虑的问题有:
a.窗口的名字,大小,位置;
b.添加的组件有哪些;
c.设置点击关闭窗口之后不仅要关掉窗口还要消除后台进程;
如何看后台进程是否还在运行?
打开任务管理器:
//首先对于第一个问题:
//便于以后方便修改设置为static final
public static final int WINDOWWIDTH = 640;
public static final int WINDOWHEIGHT = 480;
public static final int GAMETABLEWIDTH = 400;//游戏区域,也就是蓝色部分;
this.setLayout(null);//窗口默认的是边布局管理器,要自定义大小等就需要将参数设置为null,然后自己设置setBounds;
this.setTitle("贪吃蛇");//用这个函数可以设置窗口名字;
this.setBounds(20,20,width,height);//这个函数可以设置窗口大小及其位置;
//x,y为左上角在容器(窗口)中的坐标,我们设置成20,20;
//但是要窗口在屏幕居中显示的话,就要用到另一个函数获取屏幕大小;
Demension d = ToolKit.getDefaultToolKit().getScreenSize();
//于是,上面的setBounds的参数设置:width = (d.width-WINDOWWIDTH )/2;height = (d.height - WINDOWHEIGHT)/2;
//要添加的组件有:
private JButton btStart;//开始
private JButton btPause;//暂停
private JButton btStop;//结束
private label lbScore;//得分
private GreedySnakeGameTable gsgt;//游戏区域,画布(canvas)类型
//添加组件三部曲:
//1.new出来
btStart = new JButton("开始");
//2.设置边界
btStart.setBounds(500,50,60,20);
//3.添加组件到窗口
this.add(btStart);
//4.添加组件触发事件(可有可无)
btStart.addActionListener(new ActionAdapter(){
gsgt.start();//这个start方法在GreedySnakeGameTable类中定义;
//gsgt.pause();
//gsgt.stop();
})
//其他两个按钮类似就不赘述了;
//分数标签的设置
lbScore = new JLabel("0");//初始为0
lbScore.setBounds(500, 200, 60, 20);
this.add(lbScore);
//要是键盘可控显示的数字的话就要用textField,例如在控制计算器的操作数;
//游戏区域
gsgt = new GreedySnakeGameTable();
gsgt.setBounds(20, 20, GAMETABLEWIDTH + 1, GAMETABLEWIDTH + 1);//这里加一我也不记得为什么了哈哈哈
this.add(gsgt);
//对于后台线程的关闭处理:
//第一种方法:添加一个监听器,然后覆盖窗口监听器里的closed方法,这里使用的是匿名类匿名对象的方法,适用于只出现一次的类,还适用于覆盖接口里的方法,因为接口的对象不能new;
this.addWindowListener(new WindowAdapter(){
public void windowClosed(WindowEvent arg0) {
System.exit(0);
}
})
第二种:
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
2.游戏的各个组成
要考虑的问题有:
1.蛇如何产生,大小;
2.食物的产生,而且如何保证不长在蛇身上;
3.蛇定义好之后如何显示;
4.食物的显示;
5.蛇如何移动;
6.如何使键盘可以控制蛇的移动;
7.如何实现多次连续按下键盘,蛇的加速;
8.关于蛇的优化,例如撞墙会GAME OVER,自己撞自己会GAME OVER;
9.对了还有游戏画布的基本设置,颜色等;
//1.蛇的产生:考虑到蛇是变长的,设置成数组不好定义数组大小,因此设置为由点Point组成的集合ArrayList;然后用一个for循环,一段一段显示蛇的身体;
private void initSnakeBody()
{
sb = new ArrayList<Point>();
for(int i = 0;i < 4;i++)
{
sb.add(new Point(GAMEWIDTH/2+1-i,GAMEWIDTH/2));
//游戏画布400,逻辑上分成20个小格子,每个格子的边长是GAMEWIDTH = 20,为了让其居中显示;
}
}
//2.食物的产生:食物是随机产生的,也是一个点,有x和y坐标;还要确保食物不长在蛇身上;
private void initFood()
{
int x = rand.nextInt(GAMEWIDTH);
int y = rand.nextInt(GAMEWIDTH);//这里nextInt(n)产生一个0-n的随机数;
while(true)
{
for(int i = 0;i < sb.size();i++)
{
Point p = sb.get(i);//这里get是Point里的一个方法,返回点的横纵坐标;
if(p.x == x&&p.y == y)
{
break;
}
}
if(i == sb.size())
{//如果是正常退出for循环
break;
}
//如果是因为if语句退出的循环,则重新产生食物点;
int x = rand.nextInt(GAMEWIDTH);
int y = rand.nextInt(GAMEWIDTH);
}
food = new Point(x, y);
}
//3.蛇的显示:设置蛇的颜色,主要由两个函数实现:
public void PaintSnake(Graphics g)
{
for(int i = 0;i < sb.size();i++)
{
g.setColor(new Color(255,0,0));
//这里三个参数为RGB值,可以自己指定;
p = sb.get(i);
g.fillOval(p.x*SNAKEBODYWIDTH,p.y*SNAKEBODYWIDTH,SNAKEBODYWIDTH,SNAKEBODYWIDTH)
}
}
//4.画食物也是一样的:
private void paintFood(Graphics g) {
g.setColor(new Color(200, 0, 0));
g.fillOval(food.x * SNAKEBODYWIDTH, food.y * SNAKEBODYWIDTH, SNAKEBODYWIDTH, SNAKEBODYWIDTH);
}
//显示在画布上
public void paint(Graphics g) {
g.setColor(new Color(55, 196, 185));
g.fillRect(0, 0, Game.GAMETABLEWIDTH, Game.GAMETABLEWIDTH);//覆盖Paint方法,自定义颜色大小;
paintFood(g);
paintSnake(g);
}
//5.关于蛇的移动:思路是这样的,砍蛇尾,长蛇头,如果吃到食物了,就不砍尾;还有,关于撞墙Over的问题;
//还需要考虑的问题是,蛇移动的方向的确定,这里用一个数组来指定;
Private void SnakeMove()
{
int[] xlnc = {1,0,-1,0};//右下左上坐标的变化;
int[] ylnc = {0,-1,0,1};
Ponit h = sb.get(0);//得到蛇头的点的坐标;
if(x >= GAMEWIDTH || y>=GAMEWIDTH || x <0 || y <0)
{
timer.stop();
this.removeKeyListener(mka);
//游戏结束后取消键盘控制;
}
if(x == food.x&&y == food.y)
{
initFood();
}
else
{
sb.remove(sb.size() - 1);
}
sb.add(0,new Point(x,y));
}
6.键盘控制:设置一个keyListener来转换方向,还需要考虑的问题是,加速的问题;
private class MyKeyAdapter extends KeyAdapter {
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_RIGHT:
changeDirection(0);
break;
case KeyEvent.VK_UP:
changeDirection(1);
break;
case KeyEvent.VK_LEFT:
changeDirection(2);
break;
case KeyEvent.VK_DOWN:
changeDirection(3);
break;
}
}
private void changeDirection(int dir) {
if (direction != (dir + 2) % 4) {
if (direction == dir) {
snakeMove();//如果重复按下同一个方向键,就移动两倍
repaint();
}
direction = dir;
}
}
}
//7.对定时器的设置(控制蛇移动的速度,每移动一次,重绘刷新一次)
private void initTimer() {
timer = new Timer(300, new ActionListener() {
public void actionPerformed(ActionEvent e) {
snakeMove();
repaint();
}
});
}
//这里对各个按钮的事件实现:
public void start()
{
this.requestFocus();
timer.start();
}
public void pause()
{
this.requestFocus();
timer.stop();
}
public void stop()
{
this.requestFocus();
timer.stop();
}