Reputation: 557
So I am having some issues with moving my Snake across the board in a JPanel. Now it's not the JPanel's issue or fault, I'm just not able to move the Snake like it should. I know where the issue is, but I just can't figure out a way to make it work correctly without issues.
Issue: The Snake is supposed to be about 10 px away from each other, however it is clumping up to be about 1 px away from each other.
This is how it is supposed to look.
This is how it looks when it clumps up
I have tried several different ways that I can think of, and I have worked on it for days.
The Snake is supposed to keep moving at 1px for its velocity, so it is smooth animation. The head doesn't need to be affected since it is being moved anyways. But once the loop catches up to the head, it finds the position of the head, and gives the location to the body.
Moving of the Snake.
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];
}
}
}
}
}
Edit 1:
Any help would be greatly appreciated! If I need to add more detail, please feel free to let me know!
Edit 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
is what I'm looking at trying to do. But as the Snake grows longer, it must adapt.
Edit 3:
Here is the whole class of Player. The player class takes care of all the moving, and aligning its self.
Class Player:
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());
}
}
}
My Board class calls all of the data needed to make changes to this class. I apologize if this class is very unorganized. I haven't went back to reorganize it and reduce a lot of the crap in it. Also I know a lot of the code is redundant and not needed. I am working on fixing all of that. Right now I'm just trying to fix the snake's body issue.
Edit 4:
I've figured out part of it. I've got it to smoothly animate to the right and down. However once you go left or up, it will collpase, and start cauing issues again. Its like I need to check to see if it was moving before and in which direction.
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;
}
}
}
Upvotes: 3
Views: 386
Reputation: 2075
This JSFiddle (which generates the table below) shows that your loop doesn't behave how it's supposed to. Starting with hypothetical x positions of [50, 40, 30, 20, 10, 0]
, this is what it looks like after 10 iterations:
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
I've been playing around with it but can't think of a good way to fix this code to move the pieces the way they should. I think it's a little bit more complicated than you can do with just math or just a single for loop. One possibility would be to store which direction each block is moving.
You could also figure out each block's destination (i.e., where will it be in 10 steps?) then interpolate to make it move smoothly. This method has the advantage that you can ensure the blocks are getting to the right place before you work on the animation - making sure the game logic works before worrying about how nice it looks.
I think this is closer to what you're trying to do (rows without a number of moves are the destination array):
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
Or graphically:
I'll try to translate it into Java, but my Java's rusty and I can't plug it into your code, so I can't guarantee it will be perfect. The important part is the logic. Also note that it is just for x values, but it should be trivial to add y values:
You need a destination array that is a copy of X
:
int[] dests = (int[])X.clone();
Then, inside MovePlayer
, set it based on the direction of movement only if the blocks have reached their destinations (and thus need new ones):
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;
}
}
Finally, inch the blocks towards their destinations:
for(var i = 0; i < X.length; i++) {
X[i] += Integer.signum(dests[i] - X[i]);
}
signum
returns one of [-1, 0, 1]
depending on the sign of the argument.
Upvotes: 2