移动贪吃蛇身体穿过游戏板会导致身体紧凑。

3

我正在尝试将蛇移动到JPanel中,但无法正确移动。这不是JPanel的问题或错误,而是我不能像应该的那样移动蛇。我知道问题出在哪里,但是我无法找到正确的解决方法。

问题:蛇应该相距约10像素,但它们聚集在一起,相距仅约1像素。

这是预期效果。

This is how it is supposed to look.

这是聚集在一起时的效果。

This is how it looks when it clumps up

我已经尝试了多种方式,也已经努力了数日。

蛇应该以1像素的速度持续移动,以实现平滑动画。由于头部本来就会移动,因此不需要对头部进行处理。但是一旦循环追上头部,它会找到头部的位置,并将位置传递给身体。

蛇的移动。

int playerVel = 1;

 public void MovePlayer(){
    //Move the snake tail
    if(IsMoving()){
        //Move the head by its velocity
        if(left){
            X[0] -= playerVel;
        }
        else if(right){
            X[0] += playerVel;
        }
        else if(up){
            Y[0] -= playerVel;
        }
        else if(down){
            Y[0] += playerVel;
        }

        for(int i = getSizeWithArmor(); i > 0; i--){
            //Issue is when the for loop finishes counting down it located the Head's position that is 1 px away, and the rest of the body clumps up to it.
            int newLocX = X[i] - X[i-1];
            int newLocY = Y[i] - Y[i-1];

            if(newLocX != 0){
                if(newLocX == 10 || newLocX == -10){
                    X[i] = X[i-1];
                }
                else if(newLocX > 0){
                    X[i] = X[i-1] + 10;
                }
                else if(newLocX < 0){
                    X[i] = X[i-1] - 10;
                }
                else{
                    X[i] = X[i-1];
                }
            }

            if(newLocY != 0){
                if(newLocY == 10 || newLocY == -10){
                    Y[i] = Y[i-1];
                }
                else if(newLocY > 0){
                    Y[i] = Y[i-1] + 10;
                }
                else if(newLocY < 0){
                    Y[i] = Y[i-1] - 10;
                }
                else{
                    Y[i] = Y[i-1];
                }
            }
        }
    }
}

编辑1:

非常感谢您的帮助!如果我需要添加更多细节,请随时让我知道!

编辑2:

    Move    X[0]    X[1]    X[2]    X[3]    X[4]    X[5]
    -----------------------------------------------------

    1       60      50      40      30      20      10
    2       61      51      41      31      21      11  
    3       62      52      42      32      22      12 
    4       63      53      43      33      23      13 
    5       64      54      44      34      24      14 
    6       65      55      45      35      25      15 
    7       66      56      46      36      26      16 
    8       67      57      47      37      27      17

我正在尝试的是IT技术相关内容。但是随着贪吃蛇越来越长,它必须适应。

编辑3:

这里是整个玩家类。玩家类负责所有移动和对齐自身的工作。

类玩家:

public class Player implements Runnable{
//Info about the Player
private int[] X;
private int[] Y;
private int[] size;
private int blockPickup;
private int points;
private int armor;
private int blockSize = 10;

//Speeds
private int playerVel = 1;
private int speedDelay = 100;

//Direction the player is moving
private boolean up = false;
private boolean down = false;
private boolean right = false;
private boolean left = false;
private boolean inGame = false;
private boolean gamePaused = false;

private int boardWidth;
private int boardHeight;
private int allSpaces;

public void AddBlock(){
    size = new int[getSizeWithArmor() + 1];

    points++;
    blockPickup++;
}
private void AddBlock(int increase){
    size = new int[getSizeWithArmor() + increase];
    points++;
}

public void AddArmor(){
    armor++;
    AddBlock(1);
}

public void RemoveArmor(){
    armor--;
    size = new int[getSizeWithArmor() - 1];
}

public int getArmor(){
    return armor;
}
public int getSizeWithArmor(){
    return size.length;
}

public int getSizeWithoutArmor(){
    return size.length - armor;
}

public void ResetSize(){
    size = new int[1 + getArmor()];
}

public int getPoints(){
    return points;
}
public void IsGamePlaying(boolean playing){
    inGame = playing;
}
public void IsGamePaused(boolean pause){
    gamePaused = pause;
}

public void Reset(){
    points = 0;
    armor = 0;
    blockPickup = 0;

    ResetSize();
    InitPlayer();
}

public Image Head(){
    if(up){
        return headUp;
    }
    else if(down){
        return headDown;
    }
    else if(right){
        return headRight;
    }
    else if(left){
        return headLeft;
    }
    else{
        return headRight;
    }
}

public void InitPlayer(){
    for(int i = 0; i <= getSizeWithArmor(); i++){
        X[i] = (boardWidth / 2) - (i * blockSize);
        Y[i] = boardHeight / 2;
    }
}

public void SetDirection(int key){
    if(key == KeyEvent.VK_UP && !down){
        up = true;
        left = false;
        down = false;
        right = false;
    }
    else if(key == KeyEvent.VK_DOWN && !up){
        up = false;
        left = false;
        down = true;
        right = false;
    }
    else if(key == KeyEvent.VK_LEFT && !right){
        up = false;
        left = true;
        down = false;
        right = false;
    }
    else if(key == KeyEvent.VK_RIGHT && !left){
        up = false;
        left = false;
        down = false;
        right = true;
    }
}

public void MovePlayer(){
    //Move the snake tail
    if(IsMoving()){
        //Move the head by its velocity

        for(int i = getSizeWithArmor(); i > 0; i--){

            int newLocX = X[i] - X[i-1];
            int newLocY = Y[i] - Y[i-1];

            //Block going left
            if(newLocX > 0){
                if(newLocX == -1){

                }
                else if(newLocX < -1){
                    X[i] = X[i-1] + 10;
                }
            }
            //Block going right
            else if(newLocX < 0){
                X[i] = X[i-1] - 10;
            }

            //Block going up
            if(newLocY > 0){
                Y[i] = Y[i-1] + 10;
            }
            //Block going down
            else if(newLocY < 0){
                Y[i] = Y[i-1] - 10;
            }
        }

        if(left){
            X[0] -= playerVel;
        }
        else if(right){
            X[0] += playerVel;
        }
        else if(up){
            Y[0] -= playerVel;
        }
        else if(down){
            Y[0] += playerVel;
        }
    }
}

public boolean IsMoving(){
    if(up || down || left || right){
        return true;
    }
    else{
        return false;
    }
}

public int getX(int i){
    return X[i];
}

public int getY(int i){
    return Y[i];
}

public void paint(Graphics2D g){
    for(int i = 0; i < getSizeWithArmor(); i++){
        if(i == 0){
            g.drawImage(Head(), X[0], Y[0], null);
        }
        else if(getArmor() >= i){
            g.drawImage(armorImage, X[i], Y[i], null);
        }
        else{
            g.setColor(Color.YELLOW);
            g.fillRect(X[i], Y[i], blockSize, blockSize);
        }
    }
}

@Override
public void run() {
    try{
        while(inGame){
            if(!gamePaused){
                MovePlayer();
                Thread.sleep(20);
            }
        }
    }
    catch(Exception e){
        System.err.println("Player has encoundered an error: "+e.getMessage());
    }
}
}

我的Board类调用了修改该类所需的所有数据。如果这个类非常杂乱无章,我感到抱歉。我还没有回去重新组织它,并减少其中的许多垃圾。此外,我知道很多代码是冗余且不需要的。我正在努力解决所有这些问题。现在我只是想修复贪吃蛇身体的问题。

编辑4:

我已经解决了其中的一部分。我已经让它平滑地向右和向下移动。但是一旦你往左或往上走,它将会崩溃,并再次引起问题。就像我需要检查它以前是怎么移动的,朝哪个方向移动。

public void MovePlayer(){
    //Move the snake tail
    if(IsMoving()){
        //Move the head by its velocity

        for(int i = getSizeWithArmor(); i > 0; i--){
            int newLocX = X[i] - X[i-1];
            int newLocY = Y[i] - Y[i-1];

            //Figure out the last direction it was going in
            if(newLocX != 0 && newLocY != 0){

            }

            //Block going left
            if(newLocX > 0){
                if(newLocX > 0 && newLocX <= 10){
                    X[i] -= 1;
                }
                else if(newLocX > 10){
                    X[i] = X[i-1] - 10;
                }
            }
            //Block going right
            else if(newLocX < 0){
                if(newLocX < 0 && newLocX >= -10){
                    X[i] += 1;
                }
                else if(newLocX < -10){
                    X[i] = X[i-1] + 10;
                }
            }
            //Block going up
            else if(newLocY > 0){
                if(newLocY == 1){
                    Y[i] -= 1;
                }
                else if(newLocY > 1){
                    Y[i] = Y[i-1] - 10;
                }
            }
            //Block going down
            else if(newLocY < 0){
                if(newLocY < 0 && newLocY >= -10){
                    Y[i] += 1;
                }
                else if(newLocY < -10){
                    Y[i] = Y[i-1] + 10;
                }
            }

        if(left){
            X[0] -= playerVel;
        }
        else if(right){
            X[0] += playerVel;
        }
        else if(up){
            Y[0] -= playerVel;
        }
        else if(down){
            Y[0] += playerVel;
        }
    }
}

@TomSmilack 即使我将循环改为类似的形式,头部也不需要受到影响,因为它已经被移动了。但是一旦循环追上头部,它会找到头部的位置,并将位置提供给头部之前的身体,然后设置。 - Twister1002
你说得对,我没有仔细阅读剩下的部分。 - Tom Smilack
添加一个SSCCE,您将更快地获得更好的帮助。您的循环应该像这样for(int i = getSizeWithArmor(); i >= 0; i--)而不是for(int i = getSizeWithArmor(); i > 0; i--),因为这将永远不会检查数组的第一个值即array[0] - David Kroukamp
在循环内部,它不需要到达0,而是需要停在1。原因是 X[i] = X[i-1]; 我会有一个 IndexOutOfBoundsException。数组中的0不需要被检查,因为它正在移动。唯一需要检查的是随着头部移动的身体。至少这是我理解的。 - Twister1002
1个回答

2
这个JSFiddle(生成下面的表格)显示你的循环不像它应该的那样工作。从假设的x位置[50, 40, 30, 20, 10, 0]开始,经过10次迭代后,它看起来是这样的:
moves   X[0]    X[1]    X[2]    X[3]    X[4]    X[5]
----------------------------------------------------
0       50      40      30      20      10      0
1       51      41      40      30      20      10
2       52      42      31      40      30      20
3       53      43      32      41      40      30
4       54      44      33      42      31      40
5       55      45      34      43      32      41
6       56      46      35      44      33      42
7       57      47      36      45      34      43
8       58      48      37      46      35      44
9       59      49      38      47      36      45
10      60      50      39      48      37      46

我一直在尝试着解决这个代码的问题,但无法想到一个好的方法来移动棋子。我认为这比你只用数学或单个for循环做得要复杂一些。一种可能的方法是存储每个块移动的方向。
你也可以找出每个块的目的地(即它将在10步内到达哪里?),然后插值使其平滑移动。这种方法的优点是,在处理动画之前,您可以确保块已到达正确的位置-确保游戏逻辑正常工作,然后再考虑外观。
我认为这更接近你想做的事情(没有移动次数的行是目标数组):

点击此处

moves  X[0]    X[1]    X[2]    X[3]    X[4]    X[5]    X[6]    X[7]    X[8]    X[9]    X[10]
--------------------------------------------------------------------------------------------
0      50      40      30      30      30      20      20      20      30      40      50
       50      40      30      30      30      20      20      20      30      40      50
1      51      41      31      30      30      21      20      20      29      39      49
2      52      42      32      30      30      22      20      20      28      38      48
3      53      43      33      30      30      23      20      20      27      37      47
4      54      44      34      30      30      24      20      20      26      36      46
5      55      45      35      30      30      25      20      20      25      35      45
6      56      46      36      30      30      26      20      20      24      34      44
7      57      47      37      30      30      27      20      20      23      33      43
8      58      48      38      30      30      28      20      20      22      32      42
9      59      49      39      30      30      29      20      20      21      31      41
10     60      50      40      30      30      30      20      20      20      30      40
       60      50      40      30      30      30      20      20      20      30      40
11     61      51      41      31      30      30      21      20      20      29      39
12     62      52      42      32      30      30      22      20      20      28      38
13     63      53      43      33      30      30      23      20      20      27      37
14     64      54      44      34      30      30      24      20      20      26      36
15     65      55      45      35      30      30      25      20      20      25      35
16     66      56      46      36      30      30      26      20      20      24      34
17     67      57      47      37      30      30      27      20      20      23      33
18     68      58      48      38      30      30      28      20      20      22      32
19     69      59      49      39      30      30      29      20      20      21      31
20     70      60      50      40      30      30      30      20      20      20      30
       70      60      50      40      30      30      30      20      20      20      30
21     71      61      51      41      31      30      30      21      20      20      29

或者用图形方式表示:

enter image description here

我会尝试将它翻译成Java,但我的Java有点生疏,我无法将其插入您的代码中,所以不能保证它会完美无缺。重要的是逻辑部分。还要注意它仅适用于x值,但应该很容易添加y值:
您需要一个目标数组,它是X的副本: int[] dests = (int[])X.clone(); 然后,在MovePlayer内,仅根据移动方向设置它,只有在块已到达其目的地(因此需要新的目的地)时才这样做:
if(IsMoving() && X.equals(dests)) {
    for(int i = dests.length - 1; i > 0; i--) {
        if(X[i] != X[i-1])
            dests[i] = X[i-1];
    }

    if(left) {
        dests[0] = X[0] - 10;
    } else if(right) {
        dests[0] = X[0] + 10;
    }
}

最后,将方块向它们的目标位置移动:
for(var i = 0; i < X.length; i++) {
    X[i] += Integer.signum(dests[i] - X[i]);
}

"signum" 函数根据参数的符号返回 [-1, 0, 1] 中的一个值。

这基本上就是它的功能。我只是还没有想到如何绕过这个循环。 - Twister1002
更新了。如果这不是你要找的,那么你需要发布一张图片或一个带有数字的表格之类的东西。 - Tom Smilack
在“编辑2”字段中更新了预期结果。 - Twister1002
我添加了完整的Player类。看看这样是否更有意义。 - Twister1002
我的代码将根据表格执行您想要的操作。我在测试用例中放置了重复的值,以查看循环回来的蛇是否会正确移动。请参见我添加的图像。 - Tom Smilack
谢谢!谢谢!它只完成了一半的工作!现在主要问题是当X和Y都没有返回0时,它将会移动。所以现在我正在尝试进行一些检查并解决那部分问题。但这是一个重大的帮助。绝对比我的方法更短,哈哈。 - Twister1002

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接