小游戏---贪吃蛇

71 篇文章 0 订阅
5 篇文章 0 订阅

这几天一直在写贪吃蛇小游戏,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;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值