用java实现贪吃蛇小游戏
在本类中目的是做一个背景和画板,实现贪吃蛇游戏的一个动态效果
package Snake; import javax.swing.*; import java.awt.*; import java.awt.event.*; public class Background { //容器对象 private JFrame sanke_jf = new JFrame("贪吃蛇小游戏"); private JPanel jPanel = new JPanel(); private SnakeClass snake;//蛇 private GetFood.Food food;//食物 public Background( SnakeClass snake, GetFood.Food food) { //构造器 this.food = food; this.snake = snake; } //背景的大小 public static final int BACK_WIDTH = 1210; public static final int BACK_HEIGHT = 858; //游戏相关信息的画布和画笔 private class DrawArea extends Canvas { @Override public void paint(Graphics g) { if (Is.isOver) { g.setColor(Color.black); g.setFont(new Font("Times", Font.BOLD, 45)); g.drawString("游戏结束!", 600, 350); g.drawString("你的分数:"+food.count, 600, 400); g.drawString("历史最高分:"+SaveMaxCount.read(), 600, 450); } else { if (food.color==5||food.color==10||food.color==7){ g.setColor(Color.orange); g.fillOval(food.foodx, food.foody, Node.NODE_DIA, Node.NODE_DIA); }else if(food.color==4||food.color==8){ g.setColor(Color.black); g.fillOval(food.foodx, food.foody, Node.NODE_DIA, Node.NODE_DIA); }else { g.setColor(Color.GREEN); g.fillOval(food.foodx, food.foody, Node.NODE_DIA, Node.NODE_DIA); } //蛇头(黄色) g.setColor(Color.orange); g.fillOval(snake.head.nodex, snake.head.nodey, Node.NODE_DIA, Node.NODE_DIA); //蛇的身体(蓝色) g.setColor(Color.BLUE); Node temp = snake.head; while (true) { if (temp.next == null) { g.fillOval(temp.nodex, temp.nodey, Node.NODE_DIA, Node.NODE_DIA); break; } temp = temp.next; g.fillOval(temp.nodex, temp.nodey, Node.NODE_DIA, Node.NODE_DIA); } } } } private Timer timer;//多久自动执行一次相关代码的对象 //创建绘画区域对象 DrawArea drawArea = new DrawArea(); //组装显示界面的方法 public void draw_back() { drawArea.setPreferredSize(new Dimension(BACK_WIDTH, BACK_HEIGHT));//为绘画区域设置大小 //键盘监听器,用键盘控制蛇的移动 KeyListener listener = new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { int keyCode = e.getKeyCode(); snake.move(keyCode); } }; //为相关区域添加监听器 jPanel.addKeyListener(listener); sanke_jf.addKeyListener(listener); drawArea.addKeyListener(listener); //设置JFrame容器的大小 sanke_jf.setSize(BACK_WIDTH, BACK_HEIGHT); //每过指定时间自动执行的任务代码 ActionListener tack = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { drawArea.repaint();//重画,实现视觉效果 snake.autmove(); if (Is.isOver) timer.stop(); } }; timer = new Timer(230, tack); timer.start(); //将绘画区域添加到容器中 jPanel.add(drawArea); sanke_jf.add(jPanel); sanke_jf.setResizable(false);//设置JFrame的大小不可变 sanke_jf.setVisible(true);//设置窗口可见 //点击右上角“x”号结束程序 sanke_jf.addWindowListener(new WindowAdapter() { @Override public void windowClosed(WindowEvent e) { System.exit(0); } }); }
//该类是食物相关类,实现同一时间有且仅有一个食物存在,主要利用多线程的通信机制
package Snake; import java.util.Random; public class GetFood { //食物消失与生成的类 //每一个食物也是一个节点 Random rx = new Random();//随机生成食物的x坐标 Random ry = new Random();//随机生成食物的y坐标 Random rc = new Random(); //将背景按节点大小划分为一定的格子,食物只能产生在格子中以保证蛇一定可以吃到 int rowy = Background.BACK_WIDTH/Node.NODE_DIA; int rowx = Background.BACK_HEIGHT/Node.NODE_DIA; //食物类 class Food{ //坐标 public int foodx =-1; public int foody =-1; public int color = 1; //是否需要产生食物 public boolean isGetFood = true; //计算分数 int count=0; } //用多线程实现食物的消亡与产生,利用等待唤醒机制保证食物的同步 class CreateFood extends Thread { //提供锁对象 private Food food; public CreateFood(Food food){ this.food=food; } @Override public void run() { while(true){ synchronized (food){ if(!food.isGetFood){ try { food.wait();//如果有食物进入等待 } catch (InterruptedException e) { e.printStackTrace(); } }else{ //如果没有食物随机生成一个食物(坐标) if(Is.is_Strength){ food.color=(int)(Math.random()*10); } food.foodx=Node.NODE_DIA*(rx.nextInt(rowy-2)+1); food.foody=Node.NODE_DIA*(ry.nextInt(rowx-3));//设置一定范围,增强游戏体验 food.isGetFood=false;//产生食物以后修改食物的状态 food.notify();//唤醒吃食物的线程 } } } } } class EatFood extends Thread{ private Food food;//提供锁对象 private SnakeClass snake;//蛇(与食物发生碰撞) public EatFood(Food food,SnakeClass snake){ //构造器 this.food=food; this.snake=snake; } @Override public void run() { while(true){ synchronized (food){ if(food.isGetFood){ try { food.wait();//如果没有食物,进入等待 } catch (InterruptedException e) { e.printStackTrace(); } }else{ //如果有食物进 if (snake.head.nodex==food.foodx&&snake.head.nodey==food.foody){ //如果蛇头食物发生碰撞 if(food.color==5||food.color==10||food.color==7){ snake.add_Tail(new Node(snake.get_Tail().nodex,snake.get_Tail().nodey)); snake.add_Tail(new Node(snake.get_Tail().nodex,snake.get_Tail().nodey)); food.foodx=-1; food.foody=-1; food.count+=2; }else if(food.color==4||food.color==8){ snake.deleteTail(); food.foodx=-1; food.foody=-1; food.count--; try { Thread.sleep(3500); } catch (InterruptedException e) { e.printStackTrace(); }if(food.count>=SaveMaxCount.read()){ SaveMaxCount.save(food.count); } food.isGetFood=true;//修改食物状态 food.notify();//唤醒产生食物的线程 } else{ snake.add_Tail(new Node(snake.get_Tail().nodex,snake.get_Tail().nodey)); food.foodx=-1; food.foody=-1; food.count++; } // System.out.println(food.color);(在测试时查看随机数) //如果当前分数大于历史最大分数,则将当前分数存入文件 if(!food.isGetFood){ food.isGetFood=true;//修改食物状态 food.notify();//唤醒产生食物的线程 } } } } } } } }
//游戏入口
public class Inner { public static void main(String[] args) { new StartFrame().start_window(); } }
//游戏模式和游戏是否结束的开关
package Snake; public class Is { public static boolean isOver = false;//表示游戏是否结束 public static boolean is_nodie=false; public static boolean is_Strength=false; }
package Snake; //蛇的没一个节点的基本单位,因为在本游戏中不断地进行删除与增加,所以选择使用链表 public class Node { //表示蛇的每一个节点(链表结构) public Node next;//指向下一个 public int nodex;//节点的x坐标 public int nodey;//节点的y坐标 public static final int NODE_DIA = 22;//节点的大小 public Node(int nodex,int nodey){ //构造器 this.nodex=nodex; this.nodey=nodey; } }
package Snake; //游戏的初始化信息和模式设置 public class RunGameMethod { public static void method1(){//标准模式 Is.is_nodie=false; Is.is_Strength=false; original(); } public static void method2(){//无尽模式 Is.is_Strength=false; Is.is_nodie=true; original(); } public static void method3(){//无尽模式 Is.is_nodie=false; Is.is_Strength=true; original(); } private static void original(){ SaveMaxCount filecount = new SaveMaxCount();//创建保存最高分数的对象 //初始化一条小蛇 Node head = new Node(2 * Node.NODE_DIA, 0); Node body1 = new Node(Node.NODE_DIA, 0); Node body2 = new Node(0, 0); SnakeClass snake = new SnakeClass(head); snake.add_Tail(body1); snake.add_Tail(body2); //创建食物的对象 GetFood.Food food = new GetFood().new Food(); //显示游戏运行界面 Background back = new Background(snake, food); back.draw_back(); //开启产生食物和吃食物的两个线程,并将食物对象作为锁对象 new GetFood().new EatFood(food, snake).start(); new GetFood().new CreateFood(food).start(); } }
package Snake;
//利用文件保存游戏的最高分 import java.io.*; public class SaveMaxCount { //保存最高分数的文件位置 private static File file_normal = new File("C:\\Users\\lenovo\\IdeaProjects\\MyJava\\src\\Snake\\savecount.txt"); private static File file_Strength= new File("C:\\Users\\lenovo\\IdeaProjects\\MyJava\\src\\Snake\\save_strength.txt"); private static File file = file_normal; private static File getFile() { if(Is.is_Strength){ return file_Strength; } return file; } //存储分数的方法 public static void save(int count){ FileOutputStream fos = null; try{ fos = new FileOutputStream(SaveMaxCount.getFile()); fos.write(count); }catch(IOException e){ e.getMessage(); }finally { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } //得到历史最高分数的方法 public static int read(){ FileInputStream fis = null; int count = 0; try { fis=new FileInputStream(SaveMaxCount.getFile()); count=fis.read(); } catch (IOException e) { e.printStackTrace(); }finally{ try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } return count; } }
package Snake; //实现蛇的构造、移动、游戏结束逻辑的控制。 public class SnakeClass { public SnakeClass(){ } //蛇的构造器,重点是头节点 IS是为了在整个程序中保证判断结束语句的对象的一致性 public SnakeClass( Node head) { this.head = head; } public Node head;//头结点 public Node tail;//尾节点 //增加蛇的长度(向尾部添加一个节点) public void add_Tail(Node node) { Node temp = head; while (true) { if (temp.next == null) { //此时temp为当前的尾节点 break; } temp = temp.next; } temp.next = node; } //在头结点的后面和原头结点的下一个节点中间插入一个节点(移动的第一步) public void add_head(Node node) { Node temp = head.next; head.next = node; node.next = temp; } //得到当前的尾节点(在得分和移动时都需要用到) public Node get_Tail() { Node temp = head; while (true) { if (temp.next == null) { tail = temp; return tail; } temp = temp.next; } } //删除尾节点(移动的第二步) public void deleteTail() { Node temp = head; Node temp2 = temp; while (true) { if (temp.next == null) { temp2.next = null; return; } temp2 = temp; temp = temp.next; } } //蛇头撞到了自身,游戏结束逻辑的一方面 private boolean isOver_Self(){ boolean b = false; Node temp = head.next; while(true){ if(head.nodey==temp.nodey&&head.nodex==temp.nodex){ b=true; break; }else if(temp.next==null){ break; } temp=temp.next; } return b; } //游戏结束的逻辑 public boolean isOver(){ return head.nodex < Node.NODE_DIA || head.nodex > (Background.BACK_WIDTH - 2 * Node.NODE_DIA) || head.nodey < -0 || head.nodey > (Background.BACK_HEIGHT - 4 * Node.NODE_DIA)||isOver_Self(); } //移动的方法(改变方向)(将头结点按指定方法移动一个单位,在其后新增加一个单位,删除尾节点 //判断蛇是否可以向右移动,正在向左方向移动 //条件成立不能该方向移动 private boolean can_moveRight(){ return head.nodey==head.next.nodey&&head.nodex<head.next.nodex; } //判断蛇是否可以向左移动,正在向右方向移动 //条件成立不能该方向移动 private boolean can_moveLeft(){ return head.nodey==head.next.nodey&&head.nodex>head.next.nodex; } //判断蛇是否可以向上移动,正在向下方向移动 //条件成立不能该方向移动 private boolean can_moveUp(){ return head.nodex==head.next.nodex&&head.nodey<head.next.nodey; } //判断蛇是否可以向下移动,正在向上方向移动 //条件成立不能该方向移动 private boolean can_moveDown(){ return head.nodex==head.next.nodex&&head.nodey>head.next.nodey; } public void move(int ch) { boolean b=isOver(); //撞到边界的判断逻辑,满足条件游戏结束 int tempx = head.nodex; int tempy = head.nodey; //新增节点的x和y坐标 switch (ch) { case 37: if(can_moveLeft()) break; if(!Is.is_nodie){ if (b) Is.isOver = true; } head.nodex -= Node.NODE_DIA; add_head(new Node(tempx, tempy)); deleteTail(); break; case 40: if(can_moveUp()) break; if(!Is.is_nodie){ if (b) Is.isOver = true; } head.nodey += Node.NODE_DIA; add_head(new Node(tempx, tempy)); deleteTail(); break; case 39: if(can_moveRight()) break; if(!Is.is_nodie){ if (b) Is.isOver = true; } head.nodex += Node.NODE_DIA; add_head(new Node(tempx, tempy)); deleteTail(); break; case 38: if(can_moveDown()) break; if(!Is.is_nodie){ if (b) Is.isOver = true; } head.nodey -= Node.NODE_DIA; add_head(new Node(tempx, tempy)); deleteTail(); break; default: break; } } //实现蛇的自动移动(与第一种移动方式有相似的逻辑,但不会改变方向) public void autmove(){ int tempx = head.nodex; int tempy = head.nodey; boolean b=isOver(); if(can_moveRight()){ if(!Is.is_nodie){ if (b) Is.isOver = true; } head.nodex -= Node.NODE_DIA; add_head(new Node(tempx, tempy)); deleteTail(); }else if(can_moveLeft()){ if(!Is.is_nodie){ if (b) Is.isOver = true; } head.nodex += Node.NODE_DIA; add_head(new Node(tempx, tempy)); deleteTail(); }else if (can_moveUp()){ if(!Is.is_nodie){ if (b) Is.isOver = true; } head.nodey -= Node.NODE_DIA; add_head(new Node(tempx, tempy)); deleteTail(); }else{ if(!Is.is_nodie){ if (b) Is.isOver = true; } head.nodey += Node.NODE_DIA; add_head(new Node(tempx, tempy)); deleteTail(); } } }
package Snake; //游戏开始界面 import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class StartFrame implements ActionListener{ private JFrame start_trame = new JFrame("游戏模式选择"); private JPanel start_panle = new JPanel(); private JButton but1 = new JButton("标准模式"); private JButton but2 = new JButton("无尽模式"); private JButton but3 = new JButton("强化模式"); private JButton but4 = new JButton("游戏信息"); private String str; public void start_window(){ but1.addActionListener(this); but2.addActionListener(this); but3.addActionListener(this); but4.addActionListener(this); start_panle.add(but1); start_panle.add(but2); start_panle.add(but3); start_panle.add(but4); start_panle.setLayout(new GridLayout(4,1)); start_trame.add(start_panle); start_trame.setSize(400,500); start_trame.setVisible(true); start_trame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } @Override public void actionPerformed(ActionEvent e) { Is.isOver=false; if(e.getSource()==but1){ RunGameMethod.method1(); }else if(e.getSource()==but2){ RunGameMethod.method2(); }else if(e.getSource()==but3){ RunGameMethod.method3(); }else{ str="你可以通过“↑”“↓”“←”“→”控制一条小蛇去吃食物,注意不要撞到边界和自己的身体!\n" + "在无尽模式中你可以任意移动而不担心死亡,在强化模式中有三种不同的食物各有不同的效果!\n" + "快点开始你的游戏之旅吧!\n" + "\n\n" + JOptionPane.showMessageDialog(null,str); } } }
版权声明:本文为susexuexi011原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。