欢迎访问我的个人博客 https://jialaner.cn/
java是一种面向对象的语言,有着其中不用质疑的优点。学习java将近三个月了,一直在琢磨着“万物皆对象”的意义,却总是只知其表不知其意,做完这个java贪吃蛇后才有了那么一点的理解。直接上效果图。
游戏分为了六个类,分别为:
Node类:蛇身都是由一个一个节点组成,所以首先将节点抽象为一个类。
Snake类:此类是一条蛇抽象出来的一个类。一种包含了存储节点的 LinkedList 类型的集合,蛇的移动的方式是头部加一个节点,尾部减去一个节点。蛇的类有着吃蛋、越界的行为方法。
SnakeGameView类:这个类是一个游戏界面的类,用于布局游戏的界面。
GameControl类:此类用于监听键盘,从而实现对游戏的操作。
GameRunThread类:此类是一个线程类,用于不断重画界面。
DrawView类:此类继承了面板类,从而拥有了paint()方法的使用权,用于游戏面板的重画,像方格、蛇、蛋、的重画。
其中存在一个问题,就是游戏在不断的重画过程中,会出现游戏的闪烁问题,就引出了双缓冲的技术。
在编写程序的时候,总会出现我们意料之外的情况,像游戏变量权限问题、方法归属问题,都需要我们进行思考,如何让我们的程序执行、编写起来更合理、更加符合面向对象编程的思维维,护时改动最少的代码去实现最好的效果,这些都应该是我们去思考的问题。对于游戏的越界问题,一般有两种方式:中心检测法和边界检测法,在这里我用的是边界检测法,具体情况具体选择。
程序可能有着其他不太合理之处,还请大家原谅一个学java不到三个月的求学者。谢谢大家。下面开始上源码。
Node类
public class Node {private int nodeX;private int nodeY;public Node(int nodeX, int nodeY) {this.nodeX = nodeX;this.nodeY = nodeY;}public int getNodeX() {return nodeX; }public void setNodeX(int nodeX) {this.nodeX = nodeX;}public int getNodeY() {return nodeY;}public void setNodeY(int nodeY) {this.nodeY = nodeY;}}
Snake类
public class Snake {private LinkedList<Node> snakeBody;private Random random;//Snake的方向 0上, 1 右 , 2 下 , 3 左private int direction = 0; private SnakeGameView snakeGameView;public Snake(int snakeHeadX, int snakeHeadY, SnakeGameView snakeGameView) {random = new Random();snakeBody = new LinkedList<>();snakeBody.add(new Node(snakeHeadX, snakeHeadY));this.snakeGameView = snakeGameView;}//获取Snake的方向public int getDirection() {return direction;}//设置Snake方向public void setDirection(int direction) {this.direction = direction;}//获取Snake头部结点public Node getSnakeHead() {return snakeBody.getFirst();}//获取Snake尾部结点public Node getSnakeTail() {return snakeBody.getLast();}//获取Snake的Bodypublic LinkedList<Node> getSnakeBody(){return snakeBody;}//Snake移动public void snakeMove() {switch(direction) {case 0:snakeBody.addFirst(new Node(getSnakeHead().getNodeX() ,getSnakeHead().getNodeY() - DrawView.VIEW_NUMBER));break;case 1:snakeBody.addFirst(new Node(getSnakeHead().getNodeX() + DrawView.VIEW_NUMBER,getSnakeHead().getNodeY()));break;case 2:snakeBody.addFirst(new Node(getSnakeHead().getNodeX(),getSnakeHead().getNodeY()+ DrawView.VIEW_NUMBER));break;case 3:snakeBody.addFirst(new Node(getSnakeHead().getNodeX() - DrawView.VIEW_NUMBER,getSnakeHead().getNodeY()));break;}snakeBody.removeLast();}//Snake 吃Eggpublic void eatEgg(Node egg) {if(snakeBody.getFirst().getNodeX() == egg.getNodeX() && snakeBody.getFirst().getNodeY() == egg.getNodeY()) {snakeBody.add(egg);//产蛋snakeGameView.setEgg(random.nextInt(DrawView.VIEW_WIDTH - 1) * DrawView.VIEW_NUMBER,random.nextInt(DrawView.VIEW_WIDTH - 1) * DrawView.VIEW_NUMBER);SnakeGameView.gameScore = SnakeGameView.gameScore + 5;snakeGameView.getJTextArea().setText(SnakeGameView.gameScore + "");System.out.println("吃到蛋了");}}//Snake越界public void snakeRunInterface() {if(this.getSnakeHead().getNodeX() < 0 || this.getSnakeHead().getNodeY() < 0||this.getSnakeHead().getNodeX() > (DrawView.VIEW_WIDTH * DrawView.VIEW_NUMBER)||this.getSnakeHead().getNodeY() > (DrawView.VIEW_WIDTH * DrawView.VIEW_NUMBER)) {SnakeGameView.gameState = false;}}
}
SnakeGameView类
public class SnakeGameView extends Frame{private JLabel jl;private DrawView drawView;private Panel panel;private JLabel scoreTitle;private JLabel score;private Panel rule;private JTextArea hint;private Random random;public static boolean gameState = true;public static int gameScore = 0;private Snake snake;private Node egg;private GameRunThread grt;private static Thread thread;public SnakeGameView() {random = new Random();//初始化Snakesnake = new Snake(10 * DrawView.VIEW_NUMBER + random.nextInt(19) * DrawView.VIEW_NUMBER,10 * DrawView.VIEW_NUMBER + random.nextInt(19) * DrawView.VIEW_NUMBER,this);//初始化Eggegg = new Node(random.nextInt(DrawView.VIEW_WIDTH - 1) * DrawView.VIEW_NUMBER,random.nextInt(DrawView.VIEW_WIDTH - 1) * DrawView.VIEW_NUMBER);//初始化画板drawView = new DrawView(snake, egg);grt = new GameRunThread(drawView, snake);thread = new Thread(grt); jl = new JLabel("SnakeGame-3.0",JLabel.CENTER); panel = new Panel();scoreTitle = new JLabel("Score", JLabel.CENTER);score = new JLabel(gameScore + "", JLabel.CENTER);rule = new Panel();hint = new JTextArea(" 操作方式\n ↑\n ← →\n ↓\n\n【Esc】退出程序\n\n关于\n"); }//设置鸡蛋public void setEgg(int eggX, int eggY) {this.egg.setNodeX(eggX);this.egg.setNodeY(eggY);}//获得public JLabel getJTextArea() {return score;}//主方法public static void main(String[] args) {new SnakeGameView().showView();thread.start();}public void showView() {jl.setFont(new Font("宋体", 1, 18));//设置字体,0正常,1粗体jl.setForeground(Color.white);//设置颜色jl.setBounds(0, 0, DrawView.VIEW_HEIGHT * DrawView.VIEW_NUMBER + 70, 40);drawView.setBackground(new Color(51,51,51));drawView.setBounds(20, 50, DrawView.VIEW_WIDTH * DrawView.VIEW_NUMBER + 1, DrawView.VIEW_HEIGHT * DrawView.VIEW_NUMBER + 1);panel.setLayout(null); panel.setBackground(new Color(0,102,102));panel.setBounds(DrawView.VIEW_WIDTH * DrawView.VIEW_NUMBER + 40, 50, 150, DrawView.VIEW_HEIGHT * DrawView.VIEW_NUMBER); scoreTitle.setFont(new Font("宋体", 1, 18));//设置字体,0正常,1粗体scoreTitle.setForeground(Color.white);//设置颜色scoreTitle.setBounds(0, 0, 150, 50);score.setFont(new Font("宋体", 1, 20));//设置字体,0正常,1粗体score.setForeground(Color.white);//设置颜色score.setBounds(0, 50, 150, 40);rule.setBackground(new Color(0,128,128));rule.setBounds(0, 90, 150, 1);hint.setFont(new Font("宋体", 0, 16));//设置字体,0正常,1粗体hint.setBounds(10, 100, 130, 150);hint.setBackground(new Color(0,102,102));panel.add(scoreTitle);panel.add(score);panel.add(rule);panel.add(hint); this.add(jl);this.setTitle("SnakeGame-3.0");this.setSize(DrawView.VIEW_WIDTH * DrawView.VIEW_NUMBER + 210, DrawView.VIEW_HEIGHT * DrawView.VIEW_NUMBER + 70);this.setLocation(500, 200);this.setLayout(null);//this.setUndecorated(true);this.setBackground(new Color(0,128,128));this.add(drawView);this.add(panel);this.addKeyListener(new GameControl(snake));this.addWindowListener(new WindowAdapter() {public void windowClosing(WindowEvent we) {System.exit(0);}});this.setVisible(true);}}
DrawView类
public class DrawView extends Panel{//画板的宽度高度public static final int VIEW_WIDTH = 40;public static final int VIEW_HEIGHT = 40;public static final int VIEW_NUMBER = 15;private Image iBuffer; private Graphics gBuffer; private Snake snake;private Node egg;public DrawView(Snake snake, Node egg) {this.snake = snake;this.egg = egg;}public void paint(Graphics g) {snake.snakeMove();this.drawGridding(g);this.drawSnake(g);this.drawEgg(g);snake.eatEgg(egg);System.out.println("("+snake.getSnakeHead().getNodeX()+","+snake.getSnakeHead().getNodeY()+")");}public void update(Graphics g) {if(iBuffer==null) { iBuffer=createImage(DrawView.VIEW_WIDTH * DrawView.VIEW_NUMBER + 1, DrawView.VIEW_HEIGHT * DrawView.VIEW_NUMBER + 1); gBuffer=iBuffer.getGraphics(); } gBuffer.setColor(getBackground()); gBuffer.fillRect(0,0,DrawView.VIEW_WIDTH * DrawView.VIEW_NUMBER + 1, DrawView.VIEW_HEIGHT * DrawView.VIEW_NUMBER + 1); paint(gBuffer); g.drawImage(iBuffer,0,0,this); }//画网格public void drawGridding(Graphics g) {g.setColor(new Color(128,128,128));for(int i = 0; i < VIEW_WIDTH; i++) {g.drawLine(0, i * VIEW_NUMBER, VIEW_WIDTH * VIEW_NUMBER, i * VIEW_NUMBER);}for(int i = 0; i < VIEW_HEIGHT; i++) {g.drawLine(i * VIEW_NUMBER, 0, i * VIEW_NUMBER, VIEW_WIDTH * VIEW_NUMBER);}}//画Snakepublic void drawSnake(Graphics g) {for(int i = 0; i < snake.getSnakeBody().size(); i ++) {g.setColor(Color.white);if(i == 0)g.setColor(Color.lightGray);g.fillRect(snake.getSnakeBody().get(i).getNodeX(), snake.getSnakeBody().get(i).getNodeY(), VIEW_NUMBER, VIEW_NUMBER);}}//画Eggpublic void drawEgg(Graphics g) {g.setColor(Color.yellow);g.fillRect(egg.getNodeX(), egg.getNodeY(), VIEW_NUMBER, VIEW_NUMBER);}}
GameRunThread类
public class GameRunThread implements Runnable{private int runTime = 200;private DrawView drawView;private Snake snake;public GameRunThread(DrawView drawView, Snake snake) {this.drawView = drawView;this.snake = snake;}public void run() {while(true) {snake.snakeRunInterface();if(SnakeGameView.gameState) {drawView.repaint();try {Thread.sleep(runTime);}catch(InterruptedException e) {e.printStackTrace();}}}}}
GameControl类
public class GameControl implements KeyListener{private Snake snake;public GameControl(Snake snake) {this.snake = snake;}public void keyReleased(KeyEvent ke) {switch(ke.getKeyCode()) {case KeyEvent.VK_UP:snake.setDirection(0);break;case KeyEvent.VK_RIGHT:snake.setDirection(1);break;case KeyEvent.VK_DOWN:snake.setDirection(2);break;case KeyEvent.VK_LEFT:snake.setDirection(3);break;case KeyEvent.VK_ENTER:if(SnakeGameView.gameState == true) SnakeGameView.gameState = false;else SnakeGameView.gameState = true;break;case KeyEvent.VK_ESCAPE:System.exit(0);break;}}public void keyPressed(KeyEvent arg0) {}public void keyTyped(KeyEvent arg0) {}}