Java 中的贪吃蛇游戏 [英] Snake Game in Java

查看:71
本文介绍了Java 中的贪吃蛇游戏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为蛇开发一个程序.到目前为止,我有两个类,一个用于游戏的所有逻辑,另一个作为运行游戏的主类.这是我所拥有的

I'm working on a program for snake. So far I have two classes, one for all the logic of the game and the other as a main class to run the game. Here's what I have

蛇类:

这是所有的常量

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;

public class Snake {
// GUI components
private JPanel board;
private JButton[] snakeBodyPart;
private JButton bonusfood;
private JTextArea scoreViewer;

// Constants
private final int SNAKE_RUNNING_SPEED_FASTEST = 25;
private final int SNAKE_RUNNING_SPEED_FASTER = 50;
private final int SNAKE_RUNNING_SPEED_FAST = 100;
private final int BOARD_WIDTH = 500;
private final int BOARD_HEIGHT = 250;
private final int SCORE_BOARD_HEIGHT = 20;
private final int SNAKE_LENGTH_DEFAULT = 4;
private final int SNAKE_BODY_PART_SQURE = 10;
private final int BONUS_FOOD_SQURE = 15;
private final Point INIT_POINT = new Point(100, 150);

// Others values
private enum GAME_TYPE {NO_MAZE, BORDER, TUNNEL};
private int selectedSpeed = SNAKE_RUNNING_SPEED_FASTER;
private GAME_TYPE selectedGameType = GAME_TYPE.NO_MAZE;
private int totalBodyPart;
private int directionX;
private int directionY;
private int score;
private Point pointOfBonusFood = new Point();
private boolean isRunningLeft;
private boolean isRunningRight;
private boolean isRunningUp;
private boolean isRunningDown;
private boolean isBonusFoodAvailable;
private boolean isRunning;
private Random random = new Random();

这是游戏开始的地方

Snake() {
    //initialize all variables.
    resetDefaultValues();
    // initialize GUI.
    init();
    // Create Initial body of a snake.
    createInitSnake();
    // Initialize Thread.
    isRunning = true;
    createThread();
}

这是设置董事会的地方

public void init() {
    JFrame frame = new JFrame("Snake");
    frame.setSize(500, 330);

    //Create Menue bar with functions
    setJMenueBar(frame);
    // Start of UI design
    JPanel scorePanel = new JPanel();
    scoreViewer = new JTextArea("Score ==>" + score);
    scoreViewer.setEnabled(false);
    scoreViewer.setBackground(Color.BLACK);

    board = new JPanel();
    board.setLayout(null);
    board.setBounds(0, 0, BOARD_WIDTH, BOARD_HEIGHT);
    board.setBackground(Color.WHITE);
    scorePanel.setLayout(new GridLayout(0, 1));
    scorePanel.setBounds(0, BOARD_HEIGHT, BOARD_WIDTH, SCORE_BOARD_HEIGHT);
    scorePanel.setBackground(Color.RED);
    scorePanel.add(scoreViewer); // will contain score board

    frame.getContentPane().setLayout(null);
    frame.getContentPane().add(board);
    frame.getContentPane().add(scorePanel);
    frame.setVisible(true);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.addKeyListener(new KeyAdapter() {
        @Override
        public void keyPressed(KeyEvent e) {
            snakeKeyPressed(e);
        }
    });
    frame.setResizable(false);
}

这是菜单

public void setJMenueBar(JFrame frame) {

    JMenuBar mymbar = new JMenuBar();

    JMenu game = new JMenu("Game");
    JMenuItem newgame = new JMenuItem("New Game");
    JMenuItem exit = new JMenuItem("Exit");
    newgame.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            startNewGame();
        }
    });
    exit.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            System.exit(0);
        }
    });
    game.add(newgame);
    game.addSeparator();
    game.add(exit);
    mymbar.add(game);

    JMenu type = new JMenu("Type");
    JMenuItem noMaze = new JMenuItem("No Maze");
    noMaze.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            selectedGameType = GAME_TYPE.NO_MAZE;
            startNewGame();
        }
    });
    JMenuItem border = new JMenuItem("Border Maze");
    border.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            selectedGameType = GAME_TYPE.BORDER;
            startNewGame();
        }
    });
    type.add(noMaze);
    type.add(border);
    mymbar.add(type);

    JMenu level = new JMenu("Level");
    JMenuItem level1 = new JMenuItem("Level 1");
    level1.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            selectedSpeed = SNAKE_RUNNING_SPEED_FAST;
            startNewGame();
        }
    });
    JMenuItem level2 = new JMenuItem("Level 2");
    level2.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            selectedSpeed = SNAKE_RUNNING_SPEED_FASTER;
            startNewGame();
        }
    });
    JMenuItem level3 = new JMenuItem("Level 3");
    level3.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            selectedSpeed = SNAKE_RUNNING_SPEED_FASTEST;
            startNewGame();
        }
    });
    level.add(level1);
    level.add(level2);
    level.add(level3);
    mymbar.add(level);

    JMenu help = new JMenu("Help");
    JMenuItem creator = new JMenuItem("Creator");
    JMenuItem instruction = new JMenuItem("Instraction");
    creator.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {                
            JOptionPane.showMessageDialog(null, "Author: Jack Moffe");
        }
    });

    help.add(creator);
    help.add(instruction);
    mymbar.add(help);

    frame.setJMenuBar(mymbar);
}

这会重置所有值

public void resetDefaultValues() {
    snakeBodyPart = new JButton[2000];
    totalBodyPart = SNAKE_LENGTH_DEFAULT;
    directionX = SNAKE_BODY_PART_SQURE;
    directionY = 0;
    score = 0;
    isRunningLeft = false;
    isRunningRight = true;
    isRunningUp = true;
    isRunningDown = true;
    isBonusFoodAvailable = false;
}

void startNewGame() {
    resetDefaultValues();
    board.removeAll();
    createInitSnake();
    scoreViewer.setText("Score==>" + score);
    isRunning = true;
}

这个方法负责用四个身体部位初始化蛇.

This method is responsible to initialize the snake with four body part.

public void createInitSnake() {
    // Location of the snake's head.
    int x = (int) INIT_POINT.getX();
    int y = (int) INIT_POINT.getY();

    // Initially the snake has three body part.
    for (int i = 0; i < totalBodyPart; i++) {
        snakeBodyPart[i] = new JButton();            
        snakeBodyPart[i].setBounds(x, y, SNAKE_BODY_PART_SQURE, SNAKE_BODY_PART_SQURE);
        snakeBodyPart[i].setBackground(Color.GRAY);
        board.add(snakeBodyPart[i]);
        // Set location of the next body part of the snake.
        x = x - SNAKE_BODY_PART_SQURE;
    }

    // Create food.
    createFood();
}

此方法负责创建蛇的食物.这条蛇的最后一部分被当作食物,还没有成为蛇的身体部位.当且仅当蛇头碰到它时,这种食物才是身体的一部分.

This method is responsible to create food of a snake. The most last part of this snake is treated as a food, which has not become a body part of the snake yet. This food will be the body part if and only if when snake head will touch it.

void createFood() {
    int randomX = SNAKE_BODY_PART_SQURE + (SNAKE_BODY_PART_SQURE * random.nextInt(48));
    int randomY = SNAKE_BODY_PART_SQURE + (SNAKE_BODY_PART_SQURE * random.nextInt(23));

    snakeBodyPart[totalBodyPart] = new JButton();
    snakeBodyPart[totalBodyPart].setEnabled(false);
    snakeBodyPart[totalBodyPart].setBounds(randomX, randomY, SNAKE_BODY_PART_SQURE, SNAKE_BODY_PART_SQURE);
    board.add(snakeBodyPart[totalBodyPart]);

    totalBodyPart++;
}

private void createBonusFood() {
    bonusfood = new JButton();
    bonusfood.setEnabled(false);
    //Set location of the bonus food.
    int bonusFoodLocX = SNAKE_BODY_PART_SQURE * random.nextInt(50);
    int bonusFoodLocY = SNAKE_BODY_PART_SQURE * random.nextInt(25);

    bonusfood.setBounds(bonusFoodLocX, bonusFoodLocY, BONUS_FOOD_SQURE, BONUS_FOOD_SQURE);
    pointOfBonusFood = bonusfood.getLocation();
    board.add(bonusfood);
    isBonusFoodAvailable = true;
}

处理蛇的下一步.并决定应该做什么.即蛇在做什么以及如何回应

Process next step of the snake. And decide what should be done. ie what the snake is doing and how to respond

void processNextStep() {
    boolean isBorderTouched = false;
    // Generate new location of snake head.
    int newHeadLocX = (int) snakeBodyPart[0].getLocation().getX() + directionX;
    int newHeadLocY = (int) snakeBodyPart[0].getLocation().getY() + directionY;

    // Most last part of the snake is food.
    int foodLocX = (int) snakeBodyPart[totalBodyPart - 1].getLocation().getX();
    int foodLocY = (int) snakeBodyPart[totalBodyPart - 1].getLocation().getY();

    // Check does snake cross the border of the board?
    if (newHeadLocX >= BOARD_WIDTH - SNAKE_BODY_PART_SQURE) {
        newHeadLocX = 0;
        isBorderTouched = true;
    } else if (newHeadLocX <= 0) {
        newHeadLocX = BOARD_WIDTH - SNAKE_BODY_PART_SQURE;
        isBorderTouched = true;
    } else if (newHeadLocY >= BOARD_HEIGHT - SNAKE_BODY_PART_SQURE) {
        newHeadLocY = 0;
        isBorderTouched = true;
    } else if (newHeadLocY <= 0) {
        newHeadLocY = BOARD_HEIGHT - SNAKE_BODY_PART_SQURE;
        isBorderTouched = true;
    }

    // Check has snake touched the food?
    if (newHeadLocX == foodLocX && newHeadLocY == foodLocY) {
        // Set score.
        score += 5;
        scoreViewer.setText("Score==>" + score);

        // Check bonus food should be given or not?
        if (score % 50 == 0 && !isBonusFoodAvailable) {
            createBonusFood();
        }
        // Create new food.
        createFood();
    }

    // Check has snake touched the bonus food?
    if (isBonusFoodAvailable &&
            pointOfBonusFood.x <= newHeadLocX &&
            pointOfBonusFood.y <= newHeadLocY &&
            (pointOfBonusFood.x + SNAKE_BODY_PART_SQURE) >= newHeadLocX &&
            (pointOfBonusFood.y + SNAKE_BODY_PART_SQURE) >= newHeadLocY) {
        board.remove(bonusfood);
        score += 100;
        scoreViewer.setText("Score ==>" + score);
        isBonusFoodAvailable = false;
    }

    // Check is game over?
    if(isGameOver(isBorderTouched, newHeadLocX, newHeadLocY)) {
       scoreViewer.setText("GAME OVER   " + score);
       isRunning = false;
       return;
    } else {
        // Move the whole snake body to forword.
        moveSnakeForword(newHeadLocX, newHeadLocY);
    }

    board.repaint();
}

这个方法负责检测游戏是否结束?当蛇被任何迷宫或它自己接触时,游戏应该结束.

This method is responsible to detect is game over or not? Game should be over while snake is touched by any maze or by itself.

private boolean isGameOver(boolean isBorderTouched, int headLocX, int headLocY) {
    switch(selectedGameType) {
        case BORDER:
            if(isBorderTouched) {
                return true;
            }
            break;
        case TUNNEL:
            // TODO put logic here...
            throw new UnsupportedOperationException();
        default:
            break;
    }

    for (int i = SNAKE_LENGTH_DEFAULT; i < totalBodyPart - 2; i++) {
        Point partLoc = snakeBodyPart[i].getLocation();
        System.out.println("("+partLoc.x +", "+partLoc.y+")  ("+headLocX+", "+headLocY+")");
        if (partLoc.equals(new Point(headLocX, headLocY))) {
            return true;
        }
    }

    return false;
}

/**
 * Every body part should be placed to location of the front part.
 * For example if part:0(100,150) , part: 1(90, 150), part:2(80,150) and new head location (110,150) then,
   Location of part:2 should be (80,150) to (90,150), part:1 will be (90,150) to (100,150) and part:3 will be (100,150) to (110,150)
 * This movement process should be start from the last part to first part.
 * We must avoid the food that means most last body part of the snake.
 * Notice that we write (totalBodyPart - 2) instead of (totalBodyPart - 1).
 * (totalBodyPart - 1) means food and (totalBodyPart - 2) means tail.
 * @param headLocX
 * @param headLocY
 */
public void moveSnakeForword(int headLocX, int headLocY) {
    for (int i = totalBodyPart - 2; i > 0; i--) {
        Point frontBodyPartPoint = snakeBodyPart[i - 1].getLocation();
        snakeBodyPart[i].setLocation(frontBodyPartPoint);
    }
    snakeBodyPart[0].setBounds(headLocX, headLocY, SNAKE_BODY_PART_SQURE, SNAKE_BODY_PART_SQURE);
}

public void snakeKeyPressed(KeyEvent e) {
    // snake should move to left when player pressed left arrow
    if (isRunningLeft == true && e.getKeyCode() == 37) {
        directionX = -SNAKE_BODY_PART_SQURE; // means snake move right to left by 10 pixel
        directionY = 0;
        isRunningRight = false;     // means snake cant move from left to right
        isRunningUp = true;         // means snake can move from down to up
        isRunningDown = true;       // means snake can move from up to down
    }
    // snake should move to up when player pressed up arrow
    if (isRunningUp == true && e.getKeyCode() == 38) {
        directionX = 0;
        directionY = -SNAKE_BODY_PART_SQURE; // means snake move from down to up by 10 pixel
        isRunningDown = false;     // means snake can move from up to down
        isRunningRight = true;     // means snake can move from left to right
        isRunningLeft = true;      // means snake can move from right to left
    }
    // snake should move to right when player pressed right arrow
    if (isRunningRight == true && e.getKeyCode() == 39) {
        directionX = +SNAKE_BODY_PART_SQURE; // means snake move from left to right by 10 pixel
        directionY = 0;
        isRunningLeft = false;
        isRunningUp = true;
        isRunningDown = true;
    }
    // snake should move to down when player pressed down arrow
    if (isRunningDown == true && e.getKeyCode() == 40) {
        directionX = 0;
        directionY = +SNAKE_BODY_PART_SQURE; // means snake move from left to right by 10 pixel
        isRunningUp = false;
        isRunningRight = true;
        isRunningLeft = true;
    }
}

private void createThread() {
    // start thread
    Thread thread = new Thread(new Runnable() {

        public void run() {
            runIt();
        }
    });
    thread.start(); // go to runIt() method
}

public void runIt() {
    while (true) {
        if(isRunning) {
            // Process what should be next step of the snake.
            processNextStep();
            try {
                Thread.sleep(selectedSpeed);
            } catch (InterruptedException ie) {
                ie.printStackTrace();
            }
        }
    }
}
}

主类:

public class Main {
/**
 * @param args the command line arguments
 */
public static void main(String[] args) {
    new Snake();
}
}

我的问题是,当我运行游戏时,方向不起作用.蛇只是自动向一个方向移动,其他什么都没有.

My problem is that when I run the game the directions won't work. The snake just moves in one direction automatically and nothing else at all.

你怎么看?

推荐答案

不要使用 KeyListener,尤其是在像 JFrame 这样的顶级容器上.

Don't use KeyListener, especially on a top level container like JFrame.

KeyListener 只会在它注册的组件是可聚焦的并且有焦点时才会引发关键事件.JFrame 有一个 JRootPane、内容和可能的玻璃面板,它们都可以在每次接收键盘焦点时挡住框架(这是在您将任何其他组件添加到它).

KeyListener will only raise key events when the component it is registered is focusable and has focus. A JFrame has a JRootPane, content and possibly a glass pane which can all get in the way of the frame every receiving keyboard focus (and that's before you add any other components to it).

相反,请使用 键绑定 API将允许您定义将引发关键事件的 foucs 级别

Instead, make use of the key bindings API which will allow you to define the foucs level at which key events will be raised

不要使用 null 布局.像素完美布局是现代 UI 设计中的一种错觉,您无法控制字体、DPI、渲染管道或其他会改变组件在屏幕上渲染方式的因素.

Don't use null layouts. Pixel perfect layouts are an illusion in modern UI design, you have no control over fonts, DPI, rendering pipelines or other factors that will change the way that you components will be rendered on the screen.

Swing 旨在与布局管理器一起解决这些问题.如果你坚持忽略这些特性并违背 API 设计,那么就要准备好迎接很多令人头疼的事情并且永无止境的努力......

Swing was designed to work with layout managers to overcome these issues. If you insist on ignoring these features and work against the API design, be prepared for a lot of headaches and never ending hard work...

这篇关于Java 中的贪吃蛇游戏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆