【20171210】用java实现简易贪吃蛇

一、各个类之间的关系

之前学软件工程不努力,现在徒伤悲啊,不会用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();
}
import java.awt.*; import javax.swing.*; import java.awt.event.*; import java.util.*; public class snate extends JFrame implements KeyListener,Runnable { JLabel j; Canvas j1; public static final int canvasWidth = 200; public static final int canvasHeight = 300; public static final int nodeWidth = 10; public static final int nodeHeight = 10; //SnakeModel se=null; //222222 // boolean[][] matrix; LinkedList nodeArray = new LinkedList();//表 Node food;//节点 int maxX; int maxY; int direction = 2; boolean running = false; int timeInterval = 200; double speedChangeRate = 0.75; boolean paused = false; int score = 0; int countMove = 0; // UP and DOWN should be even // RIGHT and LEFT should be odd public static final int UP = 2; public static final int DOWN = 4; public static final int LEFT = 1; public static final int RIGHT = 3; snate() { super(); //setSize(500,400); Container c=getContentPane(); j=new JLabel("Score:"); c.add(j,BorderLayout.NORTH); j1=new Canvas(); j1.setSize(canvasWidth+1,canvasHeight+1); j1.addKeyListener(this); c.add(j1,BorderLayout.CENTER); JPanel p1 = new JPanel(); p1.setLayout(new BorderLayout()); JLabel j2; j2 = new JLabel("PageUp, PageDown for speed;", JLabel.CENTER); p1.add(j2, BorderLayout.NORTH); j2 = new JLabel("ENTER or R or S for start;", JLabel.CENTER); p1.add(j2, BorderLayout.CENTER); j2 = new JLabel("SPACE or P for pause",JLabel.CENTER); p1.add(j2, BorderLayout.SOUTH); c.add(p1,BorderLayout.SOUTH); addKeyListener(this); pack(); setResizable(false); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setVisible(true); // begin(); // //2222222 // this.gs = gs; this.maxX = maxX; this.maxY = maxY; // initial matirx matrix = new boolean[maxX][]; for(int i=0; i<maxX; ++i){ matrix[i] = new boolean[maxY]; Arrays.fill(matrix[i],false); } // initial the snake int initArrayLength = maxX > 20 ? 10 : maxX/2; for(int i = 0; i < initArrayLength; ++i){ int x = maxX/2+i; int y = maxY/2; nodeArray.addLast(new Node(x, y)); matrix[x][y] = true; } food = createFood(); matrix[food.x][food.y] = true; } public void keyPressed(KeyEvent e) { if(e.getKeyCode()==KeyEvent.VK_UP) { //se.changeDirection(SnakeModel.UP); } if(e.getKeyCode()==KeyEvent.VK_DOWN) { //se.changeDirection(SnakeModel.DOWN); } if(e.getKeyCode()==KeyEvent.VK_LEFT) { //se.changeDirection(SnakeModel.LEFT); } if(e.getKeyCode()==KeyEvent.VK_RIGHT) { //se.changeDirection(SnakeModel.RIGHT); } if(e.getKeyCode()==KeyEvent.VK_R||e.getKeyCode()==KeyEvent.VK_S||e.getKeyCode()==KeyEvent.VK_ENTER) { } } public void keyTyped(KeyEvent e) {} public void keyReleased(KeyEvent e) {} public void repaint() { Graphics g = j1.getGraphics(); //背景 g.setColor(Color.red); g.fillRect(0,0,canvasWidth,canvasHeight); //蛇 //g.setColor(Color.BLUE); } public void paint(Graphics g) { g.setColor(Color.red); g.fillRect(10,10,10,10); } // //222222 // public void changeDirection(int newDirection){ if (direction % 2 != newDirection % 2){ direction = newDirection; } } public boolean moveOn(){ Node n = (Node)nodeArray.getFirst(); int x = n.x; int y = n.y; switch(direction){ case UP: y--; break; case DOWN: y++; break; case LEFT: x--; break; case RIGHT: x++; break; } if ((0 <= x && x < maxX) && (0 <= y && y < maxY)){ if (matrix[x][y]){ if(x == food.x && y == food.y){ nodeArray.addFirst(food); int scoreGet = (10000 - 200 * countMove) / timeInterval; score += scoreGet > 0? scoreGet : 10; countMove = 0; food = createFood(); matrix[food.x][food.y] = true; return true; } else return false; } else{ nodeArray.addFirst(new Node(x,y)); matrix[x][y] = true; n = (Node)nodeArray.removeLast(); matrix[n.x][n.y] = false; countMove++; return true; } } return false; } public void run(){ running = true; while (running){ try{ Thread.sleep(timeInterval); } catch(Exception e){ break; } if(!paused){ if (moveOn()){ gs.repaint(); } else{ JOptionPane.showMessageDialog( null, "you failed", "Game Over", JOptionPane.INFORMATION_MESSAGE); break; } } } running = false; } private Node createFood(){ int x = 0; int y = 0; do{ Random r = new Random(); x = r.nextInt(maxX); y = r.nextInt(maxY); }while(matrix[x][y]); return new Node(x,y); } public void speedUp(){ timeInterval *= speedChangeRate; } public void speedDown(){ timeInterval /= speedChangeRate; } public void changePauseState(){ paused = !paused; } public String toString(){ String result = ""; for(int i=0; i<nodeArray.size(); ++i){ Node n = (Node)nodeArray.get(i); result += "[" + n.x + "," + n.y + "]"; } return result; } } class Node{ int x; int y; Node(int x, int y){ this.x = x; this.y = y; } } public static void main(String[] args) { //Graphics g=j1.getGraphics(); snate s=new snate(); //s.draw_something(g); //s.setVisible(true); } }
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值