【问题标题】:Snake Game: how not to make it possible for the fruit to spawn in the same position as the body蛇游戏:如何不让水果在与身体相同的位置产卵
【发布时间】:2021-02-19 11:39:26
【问题描述】:

这是我的第一个问题,如果没有提前提出,我深表歉意。

所以,我正试图让水果(我在代码中也称它们为苹果)在与蛇体不同的位置产卵,就像标题所说的那样。问题是我不知道如何检查新水果的坐标是否不等于蛇的每个身体部位。

public void newApple() {
    tempX = random.nextInt(width/unitSize)*unitSize;
    tempY = random.nextInt(height/unitSize)*unitSize;

    for (int i = 0; i < bodyParts; i++) {
        if (tempX != x[i] && tempY != y[i]){    
            appleX = tempX;
            appleY = tempY;
        }
        else {
            newApple();
        }
    }
}

这就是我到目前为止所想的:我正在使用 2 个临时变量,它们将对应于帧中的随机位置,然后我想检查它们是否不等于蛇的每个位置。 for 循环中的 if 条件只是检查 x[0] 和 y[0],它们是我的蛇头的坐标。

这就是 paintComponent() 方法中绘制苹果的内容。

//drawing apple
if (appleX != null && appleY != null) {
    g.setColor(Color.red);
    g.fillOval(appleX, appleY, unitSize, unitSize);
}

如果您可能需要,下面是我的整个 2 类项目。

public class Main {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                createGame();
            }
        });
    }

    public static void createGame() {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);
        frame.add(new MyPanel());
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

public class MyPanel extends JPanel implements ActionListener {
    final int width = 600;
    final int height = 600;
    final int unitSize = 25;
    final int maxNumberOfUnits = (width*height)/(unitSize*unitSize);
    int[] x = new int[maxNumberOfUnits];
    int[] y = new int[maxNumberOfUnits];
    int bodyParts = 2;
    Integer appleX, appleY, tempX, tempY;
    int score = 0;
    char direction = 'R';
    boolean running;
    int delay = 175;
    Timer timer = new Timer(delay, this);
    Random random = new Random();
    Font font1 = new Font("Ink Free", Font.BOLD, 40);
    Font font2 = new Font("Ink Free", Font.BOLD, 75);

    public Dimension getPreferredSize() {
        return new Dimension(width, height);
    }

    MyPanel() {
        setBackground(new Color(16, 16, 16));
        setFocusable(true);
        timer.start();
        running = true;
        newApple();
        x[0] = 10*unitSize;
        y[0] = 10*unitSize;
        x[1] = 9*unitSize;
        y[1] = 10*unitSize;

        addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                switch (e.getKeyCode()) {
                    case 37:
                        if (direction !='R') {
                            direction = 'L';
                        }
                        break;
                    case 38:
                        if (direction !='D') {
                            direction = 'U';
                        }
                        break;
                    case 39:
                        if (direction !='L') {
                            direction = 'R';
                        }
                        break;
                    case 40:
                        if (direction !='U') {
                            direction = 'D';
                        }
                        break;
                }
            }
        });
    }

    public void moveSnake() {
        for (int i = bodyParts; i > 0; i--) {
            x[i] = x[i-1];
            y[i] = y[i-1];
        }
        switch (direction) {
            case 'U':
                y[0] -= unitSize;
                break;
            case 'D':
                y[0] += unitSize;
                break;
            case 'L':
                x[0] -= unitSize;
                break;
            case 'R':
                x[0] += unitSize;
                break;
        }
    }

    public void newApple() {
        tempX = random.nextInt(width/unitSize)*unitSize;
        tempY = random.nextInt(height/unitSize)*unitSize;

        for (int i = 0; i < bodyParts; i++) {
            if (tempX != x[i] && tempY != y[i]){
                appleX = tempX;
                appleY = tempY;
            }
            else {
                newApple();
            }
        }
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        if (running) {
            /*
                //drawing a matrix
                for (int i = 1; i < height/unitSize; i++) {
                    g.drawLine(0, i*unitSize, width, i*unitSize);
                }
                for (int i = 1; i < width/unitSize; i++) {
                    g.drawLine(i*unitSize, 0, i*unitSize, height);
                }
            */

            //drawing apple
            if (appleX != null && appleY != null) {
                g.setColor(Color.red);
                g.fillOval(appleX, appleY, unitSize, unitSize);
            }

            //drawing snake
            g.setColor(Color.green);
            for (int i = 0; i < bodyParts; i++) {
                g.fillRect(x[i], y[i], unitSize, unitSize);
            }

            //drawing score
            g.setColor(Color.red);
            g.setFont(font1);
            FontMetrics metrics1 = getFontMetrics(g.getFont());
            g.drawString("Score: " + score, (width-metrics1.stringWidth("Score: " + score))/2, g.getFont().getSize());
        }
        else {
            g.setColor(Color.red);
            g.setFont(font1);
            FontMetrics metrics1 = getFontMetrics(g.getFont());
            g.drawString("Score: " + score, (width-metrics1.stringWidth("Score: " + score))/2, g.getFont().getSize());

            g.setColor(Color.white);
            g.setFont(font2);
            FontMetrics metrics2 = getFontMetrics(g.getFont());
            g.drawString("Game Over", (width-metrics2.stringWidth("Game Over"))/2, height/2);
        }
    }

    public void checkApple() {
        if (x[0]==appleX && y[0]==appleY) {
            score++;
            bodyParts++;
            newApple();
        }
    }

    public void checkCollisions() {

        //body
        for (int i = bodyParts; i > 0; i--) {
            if (x[0] == x[i] && y[0] == y[i]) {
                running = false;
            }
        }
        if (x[0] < 0) {
            running = false;
        }
        if (x[0] > width) {
            running = false;
        }
        if (y[0] < 0) {
            running = false;
        }
        if (y[0] > height) {
            running = false;
        }
        if (!running) {
            timer.stop();
        }
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (running) {
            checkApple();
            moveSnake();
            checkCollisions();
        }
        repaint();
    }
}

【问题讨论】:

  • spawn 是什么意思?你的意思是要在屏幕上画一个新的苹果?
  • 是的,我的意思是在屏幕上画一个苹果,但正如问题所述,我不希望它位于蛇身所在的位置
  • 基本上我希望 newApple() 方法中的 if close 检查循环的所有 i (它们是蛇的身体部分),然后将临时变量设置为实际坐标苹果。该代码虽然只是检查第一个,但我想不出另一种方法,所以一切可能从一开始就错了。

标签: java arrays loops


【解决方案1】:

您不需要递归调用。方法newApple() 不应调用自身。你需要一个循环。

  1. tempXtempY 生成值。
  2. 对于蛇的每个部分,检查tempX 是否等于蛇的身体部分x[i] 以及tempY 是否等于y[i]
  3. 如果蛇身的至少一部分等于tempXtempY 的坐标,则返回上面的步骤1。
  4. 如果没有蛇的身体部位等于坐标,则将生成的坐标分配给“苹果”。

这是方法newApple()

public void newApple() {
    boolean ok = false;
    while (!ok) {
        ok = true;
        tempX = random.nextInt(width/unitSize)*unitSize;
        tempY = random.nextInt(height/unitSize)*unitSize;
        for (int i = 0; i < bodyParts; i++) {
            if (tempX == x[i] && tempY == y[i]) {
                ok = false;
                break;
            }
        }
        if (ok) {
            appleX = tempX;
            appleY = tempY;
        }
    }
}

【讨论】:

    【解决方案2】:

    我认为你应该有一个蛇身体部位所有位置的数组,因为任何身体部位都可能发生碰撞(好像蛇碰撞然后游戏结束)。

    您可以使用相同的数组进行水果重生,只要确保它不在数组中即可。

    【讨论】:

    • 是的,我的蛇的 x 和 y 坐标分别有 2 个数组
    【解决方案3】:

    您应该验证tempXtempY 是否与xy arrays 中的值匹配,如果匹配则调用newApple() 并跳出循环。

    这意味着,如果您到达循环的末尾,则找不到匹配项。然后就可以安全地设置苹果的xy 坐标了:

    public void newApple() {
        tempX = random.nextInt(width/unitSize)*unitSize;
        tempY = random.nextInt(height/unitSize)*unitSize;
    
        for (int i = 0; i < bodyParts; i++) {
            if (tempX == x[i] && tempY == y[i]){    
                newApple();
                break;
            }
            if ( i == bodyParts - 1 ) {
                appleX = tempX;
                appleY = tempY;    
            }
        }
    }
    

    【讨论】:

    • 方法newApple()在蛇吃完一个苹果后被调用。屏幕上一次只有一个苹果。蛇吃完唯一的苹果后,方法newApple() 必须appleXappleY 赋值,否则屏幕上将没有苹果,我认为这不是意图.蛇吃完苹果后,一定会出现另一个苹果。您的 newApple() 方法可能不会为 appleXappleY 分配新值。
    【解决方案4】:

    如果我站在你的立场上,我会尽量避免递归并创建一个单独的函数来计算坐标是否与蛇的身体发生碰撞。

    一个基本的想法是这样的:

    newApple()
    - randomize a new coordinate x,y
    - while overlapsWithSnake(x,y)
      - randomize a new coordinate x,y
    
    overlapsWithSnake(x,y)
    - iterate through all snake coordinates
      - if coordinate is the same, return true
    - return false
    

    现在,我需要指出,当屏幕中没有更多可用空间时(游戏结束),该算法不会考虑。

    此外,随着蛇体的增长,该算法需要越来越多的时间来找到可用的坐标。您可以做以下改进:

    newApple()
    - assign the result of getAvailableSpaces() to a list
    - if the list is empty, then is game over
    - else, randomize a number between 0 and list size - 1 as p
    - select the coordinate at position p of the list
    
    getAvailableSpaces()
    - create logic to return a list of all free coordinates
    

    【讨论】:

      【解决方案5】:

      问题是你只检查苹果是否在第一个身体部位索引中。 需要先完成for循环,再检查是否可以放苹果。

      boolean applePlacementOk = true;
      while(true){
          applePlacementOk = true;
          tempX = random.nextInt(width/unitSize)*unitSize;
          tempY = random.nextInt(height/unitSize)*unitSize;
          for (int i = 0; i < bodyParts; i++) {
              if (tempX == x[i] && tempY == y[i]){    
                  applePlacementOk = false;
                  break;
              }
          }
          if(applePlacementOk){
             break;
          }
      }
      appleX = tempX;
      appleY = tempY;
      

      编辑:对不起。这应该会更好

      Edit2:我注意到它现在的答案与 Abra 基本相同。

      【讨论】:

      • 起初对我来说真的很好,因为这正是我所需要的,但有时当蛇撞到苹果时,我会收到 StackOverflowError :(
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-06-30
      • 1970-01-01
      • 1970-01-01
      • 2020-07-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多