思路 :主要是模拟蛇的运动。只要对蛇头进行操作,然后让前一种状态先的蛇头位置复制给现在蛇身的第二个部分,让前一状态的蛇的第二个部分的位置复制给现在蛇身的第三个部分......以此类推,从而达到模拟蛇运动的状态。
GluttonousSnake.java
package xjj; //贪吃蛇图形界面 import java.awt.FlowLayout; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Observable; import java.util.Observer; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.border.TitledBorder; import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; public class GluttonousSnake extends JFrame implements ActionListener { private JButton upButton, downButton, leftButton, rightButton;//控制方向按钮 private JTextField score;//分数 private SnakeCanvas snake;//蛇的模型 public GluttonousSnake() { super("贪吃蛇");//设置标题 this.setSize(725, 515);//设置大小 this.setDefaultCloseOperation(EXIT_ON_CLOSE);//设置退出按键 this.setLocationRelativeTo(null);//设置窗口居中 JPanel p = new JPanel();//控制按钮和显示分数面板 p.setBorder(new TitledBorder("控制和显示区"));//设置这个面板的标题 this.getContentPane().add(p, "East");//设置此面板的位置 p.setLayout(new GridLayout(4, 1));//设置此面板的布局方式,为网格布局方式 JPanel p2 = new JPanel();//在此面板中添加显示分数的面板 p2.setLayout(new FlowLayout());//设置为流布局方式 p2.add(new JLabel("得分:")); score = new JTextField("0"); score.setEditable(false); p2.add(score); p.add(p2); //添加按钮,有图片的并添加事件监听 upButton = new JButton("", new ImageIcon("up.png")); upButton.setActionCommand("up"); upButton.addActionListener(this); downButton = new JButton("", new ImageIcon("down.png")); downButton.setActionCommand("down"); downButton.addActionListener(this); leftButton = new JButton("", new ImageIcon("left.png")); leftButton.setActionCommand("left"); leftButton.addActionListener(this); rightButton = new JButton("", new ImageIcon("right.png")); rightButton.setActionCommand("right"); rightButton.addActionListener(this); p.add(upButton); JPanel p1 = new JPanel(); p1.setLayout(new GridLayout(1, 2)); p1.add(leftButton); p1.add(rightButton); p.add(p1); p.add(downButton); addMenu();//添加菜单 start(); this.setResizable(false); this.setVisible(true); } private void start() { snake = new SnakeCanvas(); this.getContentPane().add(snake); } private void addMenu() { JMenuBar menubar = new JMenuBar(); this.setJMenuBar(menubar); JMenu game = new JMenu("游戏"); JMenu help = new JMenu("帮助"); JMenuItem jitemNew = new JMenuItem("新游戏"); jitemNew.setActionCommand("new"); jitemNew.addActionListener(this); JMenuItem jitemPause = new JMenuItem("暂停"); jitemPause.setActionCommand("pause"); jitemPause.addActionListener(this); JMenuItem jitemExit = new JMenuItem("退出"); jitemExit.setActionCommand("exit"); jitemExit.addActionListener(this); game.add(jitemNew); game.add(jitemPause); game.addSeparator();//菜单里设置分隔线 game.add(jitemExit); menubar.add(game); menubar.add(help); } public static void main(String[] args) { new GluttonousSnake(); } public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equalsIgnoreCase("exit")) { System.exit(EXIT_ON_CLOSE); } if (e.getSource() instanceof JButton) { if (e.getActionCommand().equalsIgnoreCase("up")) {//响应向上按钮按下事件 snake.setDirect(1);//设置蛇头运动方向 snake.repaint();//对蛇模型重新画 snake.timer.start();//定时器开始 return; } if (e.getActionCommand().equalsIgnoreCase("down")) { snake.setDirect(2); snake.repaint(); snake.timer.start(); return; } if (e.getActionCommand().equalsIgnoreCase("left")) { snake.setDirect(3); snake.repaint(); snake.timer.start(); return; } if (e.getActionCommand().equalsIgnoreCase("right")) { snake.setDirect(4); snake.repaint(); snake.timer.start(); return; } } if (e.getSource() instanceof JMenuItem) { if (e.getActionCommand().equalsIgnoreCase("new")) { //this.getContentPane().remove(snake); snake.init(); snake.repaint(); snake.timer.start(); } if (e.getActionCommand().equalsIgnoreCase("pause")) { snake.timer.stop(); JOptionPane.showMessageDialog(this, "继续请按“确定”"); snake.timer.start(); } } } }
MapCanvas.java
package xjj; import java.awt.Canvas; import java.awt.Color; import java.awt.Graphics; //蛇运行的地图 public class MapCanvas extends Canvas{ public void paint(Graphics g) { g.setColor(Color.red); for(int i=30;i<=450;i+=30){ g.drawLine(0,i,450,i); } for(int i=30;i<=450;i+=30){ g.drawLine(i,0,i,450); } g.drawLine(0, 0, 450, 0); g.drawLine(0, 450, 450, 450); g.drawLine(0, 0, 0, 450); g.drawLine(450, 0, 450, 450); } }
package xjj; import java.awt.Color; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Random; import javax.swing.JOptionPane; import javax.swing.Timer; //蛇模型 public class SnakeCanvas extends MapCanvas implements ActionListener { private int number = 5;//蛇身的初始长度 public Timer timer;//定时器 private Body[] bodys;//蛇身 private Body food;//食物 public int score=0;//分数 private int speed=250;//定时器延时事件 int direct = 3;//蛇头初始方向 private int[][] mapflag = new int[455][455];//地图标记 //初始化 public void init(){ this.direct=3; this.number=5; this.bodys = new Body[this.number]; mapflag = new int[455][455]; for (int i = 0, x = 300; i < this.number; i++, x += 30) { bodys[i] = new Body(x, 180); mapflag[bodys[i].x][bodys[i].y] = 2;//如果是蛇体 设标记为2 } food = creatFood();//创建食物 } public SnakeCanvas() { init(); timer = new Timer(speed, this);//创建定时器对象 timer.start();//定时器启动 } private class Body {//蛇头成员 int x, y; Body(int x, int y) { this.x = x; this.y = y; } } public void paint(Graphics g) {//画图 super.paint(g); if(bodys==null){ return ; } for (int i = 0; i < this.number; i++) { if(i==0){//设置蛇头颜色不同 g.setColor(Color.blue); }else{ g.setColor(Color.black); } mapflag[bodys[i].x][bodys[i].y] = 2; g.fillOval(bodys[i].x, bodys[i].y, 30, 30);//画蛇体 } g.setColor(Color.red); g.fillOval(food.x, food.y, 30, 30);//画食物 } public Body creatFood() { int x = 0, y = 0; do {//随机生成食物位置 Random r = new Random();//创建随机数对象 x = r.nextInt(450);//在0到所给的数中随机产生一个伪随机数 y = r.nextInt(450); //System.out.println(mapflag[x][y]+"!"); } while (x % 30 != 0 || y % 30 != 0 || mapflag[x][y]==2 );//保证位置不是蛇身的部分,并且确保整齐性 //System.out.println(x + " " + y); mapflag[x][y] = 1;//将食物标记为1,下面就可以通过标记的不同来判断是吃到食物还是自身,从而进行不同的操作 return new Body(x, y); } public void actionPerformed(ActionEvent e) { if(bodys==null){ return ; } if(!isMove()){//如果移动失败,游戏结束 JOptionPane.showMessageDialog(this, "Game Over !"); bodys=null; timer.stop(); return ; } repaint(); } //蛇移动,原理:仅需对蛇头进行操作,然后将蛇头之前的位置赋值给现在蛇身的第二个部分, //让之前的第二个部分复制个现在的第三个部分。。。。以此类推,模拟蛇运动(这是关键) public void move(int x,int y) { Body[] b=new Body[bodys.length]; for(int i=0;i<this.number;i++){ b[i]=new Body(bodys[i].x,bodys[i].y); } this.bodys[0].x=x; this.bodys[0].y=y; for(int i=1;i<this.number;i++){ this.bodys[i]=b[i-1]; } setMapflag(); } private void setMapflag() {//地图标记的刷新 mapflag=new int[455][455]; mapflag[food.x][food.y]=1; for(int i=0;i<this.number;i++){ mapflag[bodys[i].x][bodys[i].y]=2; } } //判断是否移动成功 public boolean isMove() { if(bodys==null){ return false; } int x=bodys[0].x; int y=bodys[0].y; switch (this.direct) {//通过方向的不同对蛇头进行不同的操作 case 1: y -= 30; break; case 2: y += 30; break; case 3: x -= 30; break; case 4: x += 30; break; } if(x<0||y>=450||x>=450||y<0){//越界问题 return false; } if(mapflag[x][y]==1){//标记为1 ,则为蛇头,要添加蛇身长度 this.number++; addSnake(x,y);//添加蛇长 // System.out.println("*"); return true; }else if(mapflag[x][y]==2){//标记为2,则为蛇体,即自己吃到自己,游戏结束 // System.out.println("^"); return false; }else{ // System.out.println("%"); move(x,y); return true; } } //添加蛇身,类似move(); private void addSnake(int x, int y) { this.score++; Body[] b=new Body[bodys.length]; for(int i=0;i<this.number-1;i++){ b[i]=new Body(bodys[i].x,bodys[i].y); } bodys=new Body[this.number]; this.bodys[0]=new Body(x,y); for(int i=1;i<this.number;i++){ this.bodys[i]=b[i-1]; } setMapflag(); food = creatFood(); } public void setDirect(int i) {//设置蛇头方向 this.direct = i; } }