“This is the 16th day of my participation in the First Challenge 2022. For details: First Challenge 2022”

❤️ Author’s home page: Xiao Xu Zhu

❤️ About the author: Hello everyone, I am Xiao Xu Zhu. Java field quality creator 🏆, CSDN blog expert 🏆, Huawei cloud enjoy expert 🏆, Nuggets of the year popular author 🏆, Ali Cloud expert blogger 🏆

❤️ technology live, the appreciation

❤️ like 👍 collect ⭐ look again, form a habit

preface

Pushing boxes is an ancient game designed to train your logical thinking skills. In a narrow warehouse, it is required to put the wooden box in the designated position, and a little carelessness will appear that the box can not move or the channel is blocked, so it is necessary to skillfully use the limited space and channel, and reasonably arrange the order and position of movement in order to successfully complete the task.

The game is implemented in Java language, using Swing technology for interface processing, design ideas with object-oriented thought.

The main requirements

Control the porter to move up, down, left and right to push the box to the designated location

The main design

1. Game panel generation and display

2. Map generation algorithm

3. Character movement algorithm

4. Play background music

5. Box movement algorithm

6, all boxes moved to the designated position, only then the game pass

Screenshot function

The game start

Effect of mobile

The game pass

Code implementation

Core classes


public class GameFrame extends JFrame implements
        ActionListener.MouseListener.KeyListener {// Implement action event listener, mouse event listener, keyboard event listener

    // The current number of levels, the default is the first level, counting from 1
    private int grade = 1;
    Map [row][column] determines the position of the person
    private int row = 7, column = 7;
    // leftX, leftY records the position of the image in the upper left corner. Avoid starting the image from the coordinate (0,0)
    private int leftX = 50, leftY = 50;
    // Record the total number of rows and columns of the map
    private int mapRow = 0, mapColumn = 0;
    // Record the width and height of the screen window
    private int width = 0, height = 0;
    private boolean acceptKey = true;
    // The image used by the program
    private Image pics[] = null;// Image data
    private byte[][] map = null;// Map data
    private ArrayList list = new ArrayList();
    private SoundPlayerUtil soundPlayer;// Play the sound tool class

    /* Constants, resources in the game */
    private final static int WALL = 1;/ / wall
    private final static int BOX = 2;/ / box
    private final static int BOX_ON_END = 3;// Drop the box to the destination
    private final static int END = 4;/ / destination
    private final static int MAN_DOWN = 5;// Downward person
    private final static int MAN_LEFT = 6;// People on the left
    private final static int MAN_RIGHT = 7;// To the right
    private final static int MAN_UP = 8;// Upward people
    private final static int GRASS = 9;/ / channel
    private final static int MAN_DOWN_ON_END = 10;// The person standing down at the destination
    private final static int MAN_LEFT_ON_END = 11;// The person standing to the left at the destination
    private final static int MAN_RIGHT_ON_END = 12;// The person standing on the right of the destination
    private final static int MAN_UP_ON_END = 13;// The person standing on the upward direction of the destination
    private final static int MOVE_PIXEL = 30;// represents 30 pixels at a time

    /** * In the GameFrame0 constructor, call initMap() to initialize the gamemap of this level, empty the list of error messages, and play MIDI background music. * /
    public GameFrame(a) {
        // Some basic Settings of the game window
        setTitle("Push boxes.");
        setSize(600.600);
        setVisible(true);
        setLocation(300.20);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Container contentPane = getContentPane();
        contentPane.setLayout(null);
        contentPane.setBackground(Color.black);
        // Other Settings, initialize the window width and height assigned to width and height
        this.width = getWidth();
        this.height = getHeight();
        // Initialize the image resource
        getPics();
        // Initialize map data
        initMap();
        // Register event listeners
        setFocusable(true);
        addKeyListener(this);
        addMouseListener(this);
        // Play music
        initSound();
    }

    /** * initMap(); /** * initMap(); Call * getMapSizeAndPosition(method to get the size of the game area and display the top-left position of the game (leftX, leftY). * /
    public void initMap(a) {
        // Get map data for the current level
        map = MapFactory.getMap(grade);
        // Clear the rollback map data saved in the previous level, i.e. clear the contents of the list
        list.clear();
        // Initializes the number of lines and the starting position of the upper-left corner of the map
        getMapSizeAndPosition();
        // Get the coordinate position of the role
        getManPosition();
    }

    /** * the getManPosition() method retrieves the current position of the worker (row,column). * /
    public void getManPosition(a) {
        MANXXX (MAN_DOWN = down person; MAN_UP for upward person), which means that the position is where the person is standing, which is obtained by scanning map data
        for (int i = 0; i < map.length; i++) {
            for (int j = 0; j < map[0].length; j++) {
                if(map[i][j] == MAN_DOWN || map[i][j] == MAN_DOWN_ON_END || map[i][j] == MAN_UP || map[i][j] == MAN_UP_ON_END || map[i][j]  == MAN_LEFT || map[i][j] == MAN_LEFT_ON_END || map[i][j] == MAN_RIGHT || map[i][j] == MAN_RIGHT) {// The position of the saver, I for row, j for column, and starting from 0
                    this.row = i;
                    this.column = j;
                    break; }}}}/** * the getMapSizeAndPosition() method is used to get the size of the game area and display the top-left position of the game (lefX, leftY). * /
    private void getMapSizeAndPosition(a) {
        // Initialize mapRow and mapColumn, which represent the number of columns and columns of the map
        this.mapRow = map.length;
        this.mapColumn = map[0].length;
        // Initialize leftX and leftY, i.e. calculate the position of the upper left corner,
        this.leftX = (width - map[0].length * MOVE_PIXEL) / 2;
        this.leftY = (height - map.length * MOVE_PIXEL) / 2;
    }

    /** * the getPics() method is used to load the image to display */
    public void getPics(a) {
        // Create an array of 13 images
        pics = new Image[13];
        // Then loop each image read and save into the PICS array
        for (int i = 0; i < 13; i++) {
            pics[i] = Toolkit.getDefaultToolkit().getImage("src\\images\\pic_" + (i + 1) + ".png"); }}/** * Initializes the music */
    public void initSound(a) {
        // Call methods in the SoundPlayerUtil class to play music
        soundPlayer = new SoundPlayerUtil();
        soundPlayer.loadSound("src\\sounds\\music.wav");
        soundPlayer.playSound(true);// Loop
    }

    /** * grassOrEnd(); /** * grassOrEnd(@param man
     * @return* /
    public byte grassOrEnd(byte man) {
        if (man == MAN_DOWN_ON_END || man == MAN_LEFT_ON_END || man == MAN_RIGHT_ON_END || man == MAN_UP_ON_END) {
            return END;
        }
        return GRASS;
    }

    /** * Move up */
    private void moveUp(a) {
        // If the last digit is WALL, it cannot be moved
        if (map[row - 1][column] == WALL) {
            return;
        }
        // If the last bit is BOX or BOX_ON_END, what happens to the last bit
        if (map[row - 1][column] == BOX || map[row - 1][column] == BOX_ON_END) {
            // Then we need to consider the case of the last digit. If the previous digit is END or GRASS, we need to move up one step
            if (map[row - 2][column] == END || map[row - 2][column] == GRASS) {
                // Keep the current information to back up one step
                Map currMap = new Map(row, column, map);
                list.add(currMap);
                byte boxTemp = (byte) ((byte) map[row - 2][column] == END ? BOX_ON_END : BOX);
                byte manTemp = (byte) (map[row - 1][column] == BOX ? MAN_UP : MAN_UP_ON_END);
                // The box becomes temp, and the box moves forward
                map[row - 2][column] = boxTemp;
                // The person becomes MAN_UP and takes a step up
                map[row - 1][column] = manTemp;
                // Change the place where the person was standing to GRASS or END
                map[row][column] = grassOrEnd(map[row][column]);
                // Change the coordinates of the person after the person leavesrow--; }}else {
            // If the last bit is GRASS or END, you do not need to consider the previous step
            if (map[row - 1][column] == GRASS || map[row - 1][column] == END) {
                // Save information about the current step so that you can roll back the previous step
                Map currMap = new Map(row, column, map);
                list.add(currMap);
                byte temp = (byte) (map[row - 1][column] == END ? MAN_UP_ON_END : MAN_UP);
                // People become temp, people go up a step
                map[row - 1][column] = temp;
                // The place where the person was standing becomes GRASS or END
                map[row][column] = grassOrEnd(map[row][column]);
                // Change the coordinates of the person after the person leavesrow--; }}}/** * The character moves down, where (row,column) is the position where the current character is standing, and map[row,column] is the current character */
    private void moveDown(a) {
        // If the next digit is WALL, it cannot be moved
        // Map [row+1][column] represents the next level image block for the current role
        if (map[row + 1][column] == WALL) {
            return;
        }
        // If the next bit is BOX or BOX_ON_END placed at the destination (i.e., if the next bit is BOX, regardless of the type of BOX, you can push the BOX), what happens to the next bit
        if (map[row + 1][column] == BOX || map[row + 1][column] == BOX_ON_END) {
            // Then we need to consider the next bit of the box (what is the next bit of the box, which determines whether the box can move forward). If the next bit is destination END or channel GRASS, then the box can move down one step
            // map[row+2][column
            if (map[row + 2][column] == END || map[row + 2][column] == GRASS) {
                // The following code is the box moves one step forward, the person moves the original box position
                // Keep the current and map information to back up to the next step
                Map currMap = new Map(row, column, map);
                list.add(currMap);
                // Determine if the next step of the BOX is the destination. If it is the destination, then the next step of the BOX should become BOX_ON_END (the BOX placed at the destination). If it is not the destination then it should still be a normal BOX
                byte boxTemp = (byte) ((byte) map[row + 2][column] == END ? BOX_ON_END : BOX);
                // Check whether the next step is the box
                byte manTemp = (byte) (map[row + 1][column] == BOX ? MAN_DOWN : MAN_DOWN_ON_END);
                // The box becomes temp, and the box moves down one step
                map[row + 2][column] = boxTemp;
                // The person becomes MAN_UP and takes a step down
                map[row + 1][column] = manTemp;
                // Change the place where the person was standing to GRASS or END
                map[row][column] = grassOrEnd(map[row][column]);
                // Change the coordinates of the person after the person leavesrow++; }}else {
            // The next step is not a box, so it is either a channel or an endpoint
            // The next digit is GRASS or END, so there is no need to consider the next step
            if (map[row + 1][column] == GRASS || map[row + 1][column] == END) {
                // Save the information about the current step so that you can rollback to the next step
                Map currMap = new Map(row, column, map);
                list.add(currMap);
                byte temp = (byte) (map[row + 1][column] == END ? MAN_DOWN_ON_END : MAN_DOWN);
                // People become temp, people go down a step
                map[row + 1][column] = temp;
                // The place where the person was standing becomes GRASS or END
                map[row][column] = grassOrEnd(map[row][column]);
                // Change the coordinates of the person after the person leavesrow++; }}}/** * The character moves to the left */
    private void moveLeft(a) {
        // If the left bit is WALL, it cannot be moved
        if (map[row][column - 1] == WALL) {
            return;
        }
        // If the left bit is BOX or BOX_ON_END, what is the left bit of the left bit
        if (map[row][column - 1] == BOX || map[row][column - 1] == BOX_ON_END) {
            // If the left bit is END or GRASS, the left bit is left
            if (map[row][column - 2] == END || map[row][column - 2] == GRASS) {
                // Keep the current information for a step back to the left
                Map currMap = new Map(row, column, map);
                list.add(currMap);
                byte boxTemp = (byte) ((byte) map[row][column - 2] == END ? BOX_ON_END : BOX);
                byte manTemp = (byte) (map[row][column - 1] == BOX ? MAN_LEFT : MAN_LEFT_ON_END);
                // The box becomes temp, and the box moves forward
                map[row][column - 2] = boxTemp;
                // The person becomes MAN_UP and takes a step to the left
                map[row][column - 1] = manTemp;
                // Change the place where the person was standing to GRASS or END
                map[row][column] = grassOrEnd(map[row][column]);
                // Change the coordinates of the person after the person leavescolumn--; }}else {
            // The left bit is GRASS or END
            if (map[row][column - 1] == GRASS || map[row][column - 1] == END) {
                // Save the information about the current step so that you can go back one step to the left
                Map currMap = new Map(row, column, map);
                list.add(currMap);
                byte temp = (byte) (map[row][column - 1] == END ? MAN_LEFT_ON_END : MAN_LEFT);
                // When people become Temp, people take a step to the left
                map[row][column - 1] = temp;
                // The place where the person was standing becomes GRASS or END
                map[row][column] = grassOrEnd(map[row][column]);
                // Change the coordinates of the person after the person leavescolumn--; }}}/** * The character moves to the right */
    private void moveRight(a) {
        // If the right bit is WALL, it cannot be moved
        if (map[row][column + 1] == WALL) {
            return;
        }
        // If the right bit is BOX or BOX_ON_END, what about the right bit
        if (map[row][column + 1] == BOX || map[row][column + 1] == BOX_ON_END) {
            If the right and right bits are "END" or "GRASS", take a step to the right
            if (map[row][column + 2] == END || map[row][column + 2] == GRASS) {
                // Keep the current information for a step back to the right
                Map currMap = new Map(row, column, map);
                list.add(currMap);
                byte boxTemp = (byte) ((byte) map[row][column + 2] == END ? BOX_ON_END : BOX);
                byte manTemp = (byte) (map[row][column + 1] == BOX ? MAN_RIGHT : MAN_RIGHT_ON_END);
                // Box becomes temp, box moves one step to the right
                map[row][column + 2] = boxTemp;
                // The person becomes MAN_UP and takes a step to the right
                map[row][column + 1] = manTemp;
                // Change the place where the person was standing to GRASS or END
                map[row][column] = grassOrEnd(map[row][column]);
                // Change the coordinates of the person after the person leavescolumn++; }}else {
            // If the right bit is GRASS or END, there is no need to consider the right or right step
            if (map[row][column + 1] == GRASS || map[row][column + 1] == END) {
                // Save information about the current step so that you can rewind one step to the right
                Map currMap = new Map(row, column, map);
                list.add(currMap);
                byte temp = (byte) (map[row][column + 1] == END ? MAN_RIGHT_ON_END : MAN_RIGHT);
                // People become temp, people go one step to the right
                map[row][column + 1] = temp;
                // The place where the person was standing becomes GRASS or END
                map[row][column] = grassOrEnd(map[row][column]);
                // Change the coordinates of the person after the person leavescolumn++; }}}/** * Verify that the player has passed, if there is a destination END value or the person standing directly at the destination will not succeed **@returnReturn true if the level has already been cleared, false */ otherwise
    public boolean isFinished(a) {
        for (int i = 0; i < mapRow; i++) {
            for (int j = 0; j < mapColumn; j++) {
                if (map[i][j] == END || map[i][j] == MAN_DOWN_ON_END || map[i][j] == MAN_UP_ON_END || map[i][j] == MAN_LEFT_ON_END || map[i][j] == MAN_RIGHT_ON_END) {
                    return false; }}}return true;
    }

    // Use double buffering to solve animation flicker problem
    private Image iBuffer;
    private Graphics gBuffer;

    /** * Rewrite the drawing of the entire game area **@param g
     */
    @Override
    public void paint(Graphics g) {
        if (iBuffer == null) {
            iBuffer = createImage(width, height);
            gBuffer = iBuffer.getGraphics();
        }
        // Clear the screen of the original painting
        gBuffer.setColor(getBackground());
        gBuffer.fillRect(0.0, width, height);
        for (int i = 0; i < mapRow; i++) {
            for (int j = 0; j < mapColumn; j++) {
                // Draw a map where I represents the number of rows and j represents the number of columns
                if(map[i][j] ! =0) {
                    // The image should start from 0, but it starts from 1
                    gBuffer.drawImage(pics[map[i][j] - 1], leftX + j * MOVE_PIXEL, leftY + i * MOVE_PIXEL, 30.30.this);
                }
            }
        }
        gBuffer.setColor(Color.RED);
        gBuffer.setFont(new Font("Regular script _2312", Font.BOLD, 30));
        gBuffer.drawString("Now it's number one.".150.140);
        gBuffer.drawString(String.valueOf(grade), 310.140);
        gBuffer.drawString("Closed".360.140);
        g.drawImage(iBuffer, 0.0.this);
        /* The following code is code that does not use double buffering to cause the animation to flicker */
        /*g.clearRect(0, 0, width, height); for (int i = 0; i < mapRow; i++) { for (int j = 0; j < mapColumn; If (map[I][j]! = 0) {// We need to subtract 1 because the name of the image does not correspond to the serial number, should start from 0, (pics[map[I][j] -1], leftX + j * MOVE_PIXEL, leftY + I * MOVE_PIXEL, 30, 30, this); } } } g.setColor(Color.RED); G.setfont (new Font(" regular script _2312", Font.BOLD, 30)); G. rawString(" Now number ", 150, 140); g.drawString(String.valueOf(grade), 310, 140); G. rawString(" guan ", 360, 140); * /
    }

    @Override
    public void actionPerformed(ActionEvent e) {}@Override
    public void keyTyped(KeyEvent e) {}@Override
    public void keyPressed(KeyEvent e) {
        // The event that is triggered when a key on the keyboard is pressed
        switch (e.getKeyCode()) {
            case KeyEvent.VK_UP:// Up arrow key
                moveUp();// Move up
                break;
            case KeyEvent.VK_DOWN:// down arrow key
                moveDown();// Move down
                break;
            case KeyEvent.VK_LEFT:// Left arrow key
                moveLeft();// Move left
                break;
            case KeyEvent.VK_RIGHT:// Right arrow key
                moveRight();// Move to the right
                break;
        }
        // Then redraw the interface
        repaint();
        // After the move is completed, it may be cleared, so you need to determine whether to clear
        if (isFinished()) {
            // Disable the button
            acceptKey = false;
            // Check if it is the last level, if it is, ask whether to proceed to the next level
            if (grade == MapFactory.getCount()) {
                JOptionPane.showMessageDialog(this."Congratulations on passing the last stage!");
            } else {
                // Prompt to proceed to the next level
                String msg = "Congratulations on passing number one." + grade + "Off!! \n Whether to go to the next level?";
                int choice = JOptionPane.showConfirmDialog(null, msg, "Pass", JOptionPane.YES_NO_OPTION);
                if (choice == 1) {
                    System.exit(0);
                } else if (choice == 0) {
                    // Go to the next level
                    acceptKey = true; nextGrade(); }}}}@Override
    public void keyReleased(KeyEvent e) {}@Override
    public void mouseClicked(MouseEvent e) {
        // mouseEvent.button3 represents the right mouse button
        if(e.getButton() == MouseEvent.BUTTON3) { undo(); }}@Override
    public void mousePressed(MouseEvent e) {}@Override
    public void mouseReleased(MouseEvent e) {}@Override
    public void mouseEntered(MouseEvent e) {}@Override
    public void mouseExited(MouseEvent e) {}/** * Returns the current person's location using getManX() and getManY() methods **@return* /
    public int getManX(a) {
        return row;
    }

    public int getManY(a) {
        return column;
    }

    /** * returns the current level number **@return* /
    public int getGrade(a) {
        return grade;
    }

    /** * Returns map information for the current level **@return* /
    public byte[][] getMap() {
        return MapFactory.getMap(grade);
    }

    /** * Displays a prompt message dialog box **@param str
     */
    public void displayToast(String str) {
        JOptionPane.showMessageDialog(null, str, "Tip", JOptionPane.ERROR_MESSAGE);
    }

    /** * undoes the move operation */
    public void undo(a) {
        if (acceptKey) {
            if (list.size() > 0) {
                // If you want to undo, you must go through
                // Consider using a stack
                Map priorMap = (Map) list.get(list.size() - 1);
                this.map = priorMap.getMap();
                this.row = priorMap.getManX();
                this.column = priorMap.getManY();
                repaint();// Redraw the diagram
                list.remove(list.size() - 1);
            } else {
                displayToast("It can't be revoked!); }}else {
            displayToast("This level is complete and cannot be undone!"); }}/** * Initialize the next level and call repaint() to display the game interface */
    public void nextGrade(a) {
        // Initialize the data for the next level
        if (grade >= MapFactory.getCount()) {
            displayToast("Congratulations on completing all the levels!");
            acceptKey = false;
        } else {
            // Number of levels increased by 1
            grade++;
            // Initialize the map data for the next level
            initMap();
            // Redraw the screen
            repaint();
            acceptKey = true; }}/** * Initialize the previous level and call repaint() to display the game interface */
    public void priorGrade(a) {
        grade--;
        acceptKey = true;
        if (grade < 0) {
            grade = 0; } initMap(); repaint(); }}Copy the code

Sound player class


public class SoundPlayerUtil {
    public File file;
    public AudioInputStream stream;
    public AudioFormat format;
    DataLine.Info info;
    Clip clip;

    /** * Load sound files, support waV, MP3 and other sound files **@paramFilePath Path of the sound file */
    public void loadSound(String filePath) {
        file = new File(filePath);
        try {
            stream = AudioSystem.getAudioInputStream(file);
        } catch (UnsupportedAudioFileException | IOException e) {
            e.printStackTrace();
        }
        format = stream.getFormat();
    }

    /** * Play music **@paramIsLoop indicates whether to loop the music or, if true is passed in, loop the music */
    public void playSound(boolean isLoop) {
        info = new DataLine.Info(Clip.class, format);
        try {
            clip = (Clip) AudioSystem.getLine(info);
            clip.open(stream);
        } catch (LineUnavailableException | IOException e) {
            e.printStackTrace();
        }
        if (isLoop) {
            clip.loop(Clip.LOOP_CONTINUOUSLY);// Add this line of code to loop} clip.start(); }}Copy the code

conclusion

Through the “Push the box” game implementation, let me have a further understanding of swing related knowledge, the Language of Java has a deeper understanding than before.

Basic Java syntax, such as data types, operators, program flow control, and arrays, is better understood. Java is the core of the core object oriented thought, for this concept, finally realized some.

The source code for

After clicking “like” and following the blogger, the private blogger will get it for free