小游戏---贪吃蛇

来源:互联网 发布:c语言 stdio.h 编辑:程序博客网 时间:2024/06/16 19:00

这几天一直在写贪吃蛇小游戏,ACM都没时间去做,但是收获还是很大的。程序中包函了线程、监听、界面的应用,最大的困难的如何实现动画,经过反复修改,感觉采用线程的方法是最好控制的。动画是通过JPanel.updateUI()函数来重画,每各一定时间重画一下,就形成了动画效果。很假O(∩_∩)O哈哈~….

//package 课后练习.d1104;import java.awt.Color;import java.awt.Container;import java.awt.FlowLayout;import java.awt.Graphics;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.event.KeyEvent;import java.awt.event.KeyListener;import java.util.Iterator;import java.util.LinkedList;import java.util.Random;import javax.swing.JFrame;import javax.swing.JLabel;import javax.swing.JMenuBar;import javax.swing.JMenuItem;import javax.swing.JOptionPane;import javax.swing.JPanel;public class Snake extends JFrame implements KeyListener{    private final int JPS_WIDTH = 400;//蛇活动范围的宽度    private final int JPS_HEIGHT = 400;//蛇活动范围的高度    private JLabel lbScore;//显示得分的标签    private SnakeJPanel snakePanel;//蛇活动的面板    private SnakeList S_List;//实现Runnable接口成动画的关键    //存放蛇身体每个节点的链表    private LinkedList<Point> snakeList = new LinkedList<Point>();    Point food;//食物    public Snake() {        setTitle("贪吃蛇");        setBounds(450, 150, 410, 485);//窗口大小需要自己调...        setDefaultCloseOperation(EXIT_ON_CLOSE);        setResizable(false);//设置不可改变大小        addKeyListener(this);//添加键盘监听        Container contentPane = getContentPane();//获取内容窗格        contentPane.setLayout(null);//设置空布局        //从新开始        JMenuBar bar = new JMenuBar();//创建菜单栏        setJMenuBar(bar);//给框架添加菜单栏        bar.setLayout(new FlowLayout(FlowLayout.LEFT));        JMenuItem afresh = new JMenuItem("从新开始");//创建菜单项        afresh.addActionListener(new ActionListener() {//添加监听            @Override            public void actionPerformed(ActionEvent e) {                S_List.running=false;                snakeList = new LinkedList<Point>();                begin();            }        });        bar.add(afresh);//菜单栏添加菜单项        //帮助        JMenuItem help = new JMenuItem("帮助");//创建菜单项        help.addActionListener(new ActionListener() {//添加监听            @Override            public void actionPerformed(ActionEvent e) {                S_List.changeCondition();                JOptionPane.showMessageDialog(Snake.this, "向上按 ↑ 键\n向下按 ↓ 键\n"                                                        + "向左按 ← 键\n向右按 → 键\n按空格键暂停");                S_List.changeCondition();            }        });        bar.add(help);//菜单栏添加菜单项        JPanel scorePanel = new JPanel();//创建存放得分标签的面板        scorePanel.setSize(JPS_WIDTH, 30);//设置位置和大小        lbScore = new JLabel("得分:0");//创建得分标签        scorePanel.add(lbScore);//添加得分标签到面板上        contentPane.add(scorePanel);//得分面板添加到内容窗格        snakePanel = new SnakeJPanel();//创建蛇活动面板        snakePanel.setBounds(0, 30 , JPS_WIDTH, JPS_HEIGHT);//设置位置和大小        contentPane.add(snakePanel);//蛇活动面板添加到内容窗格        setVisible(true);        begin();//实现动画的关键    }    public JLabel getLbScore() {        return lbScore;    }    public SnakeJPanel getSnakePanel() {        return snakePanel;    }    public LinkedList<Point> getSnakeList() {        return snakeList;    }    public int getJPS_WIDTH() {        return JPS_WIDTH;    }    public int getJPS_HEIGHT() {        return JPS_HEIGHT;    }    //实现动画:采用线程,让线程sleep一定的时间,然后更新面板从而实现动画    private void begin() {        if (S_List==null||S_List.running==false) {            S_List = new SnakeList(this);            new Thread(S_List).start();        }    }    //画每一瞬间的蛇与食物    class SnakeJPanel extends JPanel {        @Override        public void paint(Graphics g) {            g.setColor(Color.WHITE);//设置画笔颜色            g.fillRect(0, 0, JPS_WIDTH, JPS_HEIGHT);//设置背景颜色            g.setColor(Color.GREEN);//设置画笔颜色            if (snakeList == null) {//卫条件                return;            }            Iterator<Point> it = snakeList.iterator();//通过迭代器获取链表中的每个节点            while (it.hasNext()) {                Point p = it.next();                g.fillRect(p.x*10, p.y*10, 9, 9);            }            if (food == null) {//卫条件                return;            }            g.setColor(Color.black);            g.fillRect(food.x*10, food.y*10, 9, 9);        }    }    @Override    public void keyTyped(KeyEvent e) {    }    //实现键盘监听    @Override    public void keyPressed(KeyEvent e) {        int keyCode = e.getKeyCode();         switch (keyCode) {         case KeyEvent.VK_UP:             S_List.changDirection(SnakeList.UP);             break;         case KeyEvent.VK_DOWN:             S_List.changDirection(SnakeList.DOWN);             break;         case KeyEvent.VK_LEFT:             S_List.changDirection(SnakeList.LEFT);             break;         case KeyEvent.VK_RIGHT:             S_List.changDirection(SnakeList.RIGHT);             break;         case KeyEvent.VK_SPACE:             S_List.changeCondition();             break;         default:            break;        }    }    @Override    public void keyReleased(KeyEvent e) {    }    //主函数    public static void main(String[] args) {        Snake snake = new Snake();    }}//实现Runnable接口,动态改变蛇的位置和食物的位置,以便实现动画class SnakeList implements Runnable {    private int w;// 活动区间的宽度    private int h;// 活动区间的高度    private boolean[][] matrix;// 活动区间 matrix[i][j]为真说明该点有东西(食物/蛇身节点)    private Snake snake;// 框架    private LinkedList<Point> list;// 蛇身    private int direction;// 方向    // UP和DOWN是偶数,RIGHT和LEFT是奇数,避免冲突,例如向上跑时按向上或者向下是无法改变蛇的跑动方向    public static final int UP = 2;    public static final int DOWN = 4;    public static final int LEFT = 1;    public static final int RIGHT = 3;    boolean running;//判断游戏是否结束    boolean isPause = true;//判断游戏是否暂停    private long time = 200;//sleep时间(单位是毫米)    private int score;//得分    public SnakeList(Snake snake) {        //构造传参        this.snake = snake;        list = snake.getSnakeList();        w = snake.getJPS_WIDTH();        h = snake.getJPS_HEIGHT();        matrix = new boolean[w / 10][h / 10];        direction = 1;// 初始方向向左        running = true;        isPause = false;        initlist();//初始化蛇    }    //初始化蛇的函数    public void initlist() {        int x = h / 20;        int y = w / 20;        for (int i = 0; i < 10; i++) {            Point point = new Point((x + i) , y );            matrix[x + i][y] = true;            list.add(point);        }        createFood();    }    //创建食物:通过调用Random是食物随机出现    public void createFood() {        int x = 0;        int y = 0;        do {            Random random = new Random();            x = random.nextInt(w / 10);//随机数的范围0 ~ w/10            y = random.nextInt(h / 10);        } while (matrix[x][y]);// 如果matrix[x][y]为真说明这是蛇身不能从新创建食物        snake.food = new Point(x, y);        matrix[x][y] = true;    }    //关键函数,控制蛇位置函数    public boolean move() {        Point first = list.getFirst();//通过头节点来控制蛇运动方向        int x = first.x;        int y = first.y;        switch (direction) {        case LEFT:            x--;            break;        case RIGHT:            x++;            break;        case UP:            y--;            break;        case DOWN:            y++;        }        if (0 <= x && x < w / 10 && 0 <= y && y < h / 10) {//如果在活动区间说明蛇不是撞墙死的            if(matrix[x][y]){//matrix[x][y]为真说明这个位置要么是食物,要么是自己身体的某个节点                if(x==snake.food.x&&y==snake.food.y){//能进来说明是食物                    score += (int)1000/time+list.size();//结算得分                    list.addFirst(snake.food);                    createFood();//创建新的食物                    return true;//蛇没死                }else{                    //System.out.println("撞自己");                    return false;                }             }            //能到这里说明matrix[x][y]值为假,此时该点应该是蛇头到达的点            Point point = new Point(x, y);            matrix[x][y] = true;            list.addFirst(point);            point=list.removeLast();//同时蛇尾应该变成它的上一个节点            matrix[point.x][point.y] = false;            return true;//蛇没死        }        //System.out.println("撞墙");        return false;    }    //不能180度转弯    public void changDirection(int dcn){        if(dcn%2!=direction%2){            direction = dcn;        }    }//  public void up(){//      direction = UP;//  }//  public void down(){//      direction = DOWN;//  }//  public void left(){//      direction = LEFT;//  }//  public void right(){//      direction = RIGHT;//  }    //改变蛇状态函数    public void changeCondition(){        isPause = !isPause;    }    //重写run方法    @Override    public void run() {        while(running){            try {//这里只是控制蛇的移动速度,当分数达到一定值时,蛇移速变快                if (score>222) {                    Thread.sleep(time/2);                }else if(score>666){                    Thread.sleep(time/3);                }else{                    Thread.sleep(time);                }            } catch (InterruptedException e) {                e.printStackTrace();            }            if(!isPause){                if (move()) {                    snake.getSnakePanel().updateUI();                    snake.getLbScore().setText("得分:"+score);                    continue;                }                JOptionPane.showMessageDialog(null, "游戏结束!");                running = false;            }        }    }}class Point {    int x;    int y;    public Point(int x, int y) {        this.x = x;        this.y = y;    }}