ArrayList stuff for Snake game

I’m pretty new to coding with Processing (I know some basics) so I wanted to make the game Snake. I’ve been able to make the square move in a grid and eat food then spawn new food, but adding a new square is giving me trouble. In the Snake class and void update() I’m trying to create a vector for the new part of the snake by increasing total by 1 in boolean eat() then having inside the update an array that adds 1 to the length. Right now I have the total set as 0 so I get the ArrayIndex… error but if I change total to 1 to fix that I get NullPointerException when trying to draw the new rectangle in the void show() function. I’ve tried using 2 floats instead of the Pvector and get no errors but then the new tail block doesnt stay connected with the snake. I know my code is probably messy and bad but I’ve been trying to find a fix and I can’t so I hope someone can help. If you need me to explain something else in the code that wasn’t already mentioned just ask.

PVector food;
float scl=20;
Snake s;

void setup()
{
  size(800,800);
  background(255);
  s= new Snake();
  frameRate(10);
  pickLocation();
}

void draw()
{
  background(200);
  s.update();
  s.show();
  if(s.eat(food))
  {
    pickLocation();
  }
  
  fill(255,0,0);
  rect(food.x,food.y,scl,scl);
}

void pickLocation()
{
  float cols=floor(width/scl);
  float rows=floor(height/scl);
  food= new PVector(floor(random(cols)),floor(random(rows)));
  food.mult(scl);
}

class Snake
{
  float x=0;
  float y=0;
  float xspeed=1;
  float yspeed=0;
  float total=0;
  PVector[] tail= new PVector[6400];
  
  void dir(float x, float y)
  {
    xspeed=x;
    yspeed=y;
  }
  
  boolean eat(PVector pos)
  {
    float d=dist(x,y,pos.x,pos.y);
    if(d<1)
    {
      total++;
      return true;
    }
    else return false;
  }
  
  void update()
  {
    if(total==tail.length)
    {
      for(int i=(int)total; i<tail.length-1; i++)
      {
        tail[i]=tail[i+1];
      }
    }
    tail[(int)total-1]= new PVector(x,y);
    
    x=x + xspeed*scl;
    y=y + yspeed*scl;
    x=constrain(x,0,width-scl);
    y=constrain(y,0,height-scl);
  }
  
  void show()
  {
    fill(0);
    for(int i=0; i<tail.length; i++)
      {
        rect(tail[i].x,tail[i].y,scl,scl);
      }
    
    stroke(255);
    fill(0);
    rect(x,y,20,20);
  }
}

void keyPressed()
{
  if(keyCode==UP)
  {
    s.dir(0,-1);
  }
  else if(keyCode==DOWN)
  {
    s.dir(0,1);
  }
  else if(keyCode==RIGHT)
  {
    s.dir(1,0);
  }
  else if(keyCode==LEFT)
  {
    s.dir(-1,0);
  }
}
1 Like

It would be helpful to include your code for your Snake class (and any other related classes) too.

Basically, the error you are getting is because you are trying to access a variable which is a null value. This could be because you used an object before you defined it, or you tried to access an array value that doesn’t exist.

Here are some examples that could cause NullPointException:
float a; float b = a; a was not initialized
float[] a = new float[10]; println(a[-1]) [-1] doesn’t exist
Object o; o.doSomething(); o was not initialized

1 Like

Thanks for the reply! I was able to get rid of the null error by changing in the for loop in void show() the i<tail.length to i>tail.length but I can’t wrap my head around why that would remove the error, I also tried running the program with this change and it still doesn’t add the new snake block.
Here is the Snake class btw (it was in the original post, you must’ve just missed it)

class Snake
{
  float x=0;
  float y=0;
  float xspeed=1;
  float yspeed=0;
  float total=0;
  PVector[] tail= new PVector[6400];
  
  void dir(float x, float y)
  {
    xspeed=x;
    yspeed=y;
  }
  
  boolean eat(PVector pos)
  {
    float d=dist(x,y,pos.x,pos.y);
    if(d<1)
    {
      total++;
      return true;
    }
    else return false;
  }
  
  void update()
  {
    if(total==tail.length)
    {
      for(int i=(int)total; i>tail.length-1; i++)
      {
        tail[i]=tail[i+1];
      }
    }
    tail[(int)total-1]= new PVector(x,y);
    
    x=x + xspeed*scl;
    y=y + yspeed*scl;
    x=constrain(x,0,width-scl);
    y=constrain(y,0,height-scl);
  }
  
  void show()
  {
    fill(0);
    for(int i=0; i<tail.length; i++)
      {
        rect(tail[i].x,tail[i].y,scl,scl);
      }
    
    stroke(255);
    fill(0);
    rect(x,y,20,20);
  }
}

On line 71 tail[(int)total-1]= new PVector(x,y);, the value of (int)total-1 is -1, and tail[-1] doesn’t exist.

ive tried changing total to 1 so i dont get that array error but i still get the nullpointerror which i cant figure out how to fix

On line 66 for(int i=(int)total; i<tail.length-1; i++, why did you put tail.length-1? (instead of tail.length)

Figured out why it brings the error: In the constructor you put PVector[] tail= new PVector[6400];, which sets the length of tail to 6400, but the array is empty, so when the for loop tries to loop through the 6400 elements it is finds no values, returning NullPointException.

In the case, you shouldn’t use an array, but an ArrayList. An ArrayList is like a “flexible” array, there is no set size, it starts at 0, and you can add as many values as you want to it.

For more infomation on ArrayList see these links:
https://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html
https://processing.org/reference/ArrayList.html

Thanks! I’m not to sure how I would implement that vs what I have currently because I’ve never used an ArrayList before so I would appreciate some help with that. I’ll try to see what I can do though.

It’s very similar, you create a new ArrayList for your tail positions ArrayList<PVector> tail = new ArrayList<PVector>();. Then you can add elements with tail.add() remove with tail.remove and run a for loop through it you can use an normal loop for(int i = 0; i < tail.size(); i++) or use an enhanced loop for(PVector position : tail). If you have any other specific questions, feel free to ask.

I understand tail.size() and how I would change my code with that but I’m not sure how to implement tail.add() or tail.remove(). For example, how would I change tail[i]=tail[i+1]; to work with an ArrayList?

Hi Dinator14,

You need to read about the get() and set() function here.

It would be something like this:

tail.set(i, tail.get(i+1));
1 Like

I was able to write this code which I think should give me the intended result but now I can’t draw the rectangle in the void show() function because I’m getting an error saying rect requires (float,float,float,float) which I understand but I don’t know how to change the ArrayList to cooperate with this.

void update()
  {
    if(total==tail.size())
    {
      for(int i=(int)total; i<tail.size()-1; i++)
      {
        tail.set(i, tail.get(i+1));
      }
    }
    tail.set((int)total-1, new PVector(x,y));
   
    x=x + xspeed*scl;
    y=y + yspeed*scl;
    x=constrain(x,0,width-scl);
    y=constrain(y,0,height-scl);
  }
  
  void show()
  {
    fill(0);
    for(int i=0; i<tail.size(); i++)
      {
        rect(tail.get((int)x),tail.get((int)y),scl,scl);
      }

.get only gets the PVector, so you would have to use. (int)tail.get(i).x. You can also use an enhanced loop with it:

for(PVector position : tail) {
    rect((int)position.x,(int)position.y,scl,scl);
}
1 Like

So I’ve tried using both of the options you gave me which they seem to work for the rectangle but on line 71: tail.set((int)total-1, new PVector(x,y)); I get the error IndexOutOfBoundsException: Index 0, Size 0. Any idea what’s causing this?

  • It means that List got its size() = 0 currently!
  • Its method set() doesn’t change its size()!
  • In order to increase a List’s size(), we can use its method add().
  1. ArrayList / Reference / Processing.org
  2. List (Java SE 10 & JDK 10 )

Thanks, I was able to get rid of all errors and run the program, but the tail.size() is always increasing but I only want it to increase when a piece of food is eaten hence total++ in the boolean eat() function and the if statement at the start of void update(). I tried moving tail.add((int)total, new PVector(x,y)); into the if statement but that causes the new tail blocks to be left behind and not stay connected with the snake. Why does the if(total==tail.size()) prevent this?

class Snake
{
  float x=0;
  float y=0;
  float xspeed=1;
  float yspeed=0;
  float total=0;
  ArrayList<PVector> tail = new ArrayList<PVector>();
  
  void dir(float x, float y)
  {
    xspeed=x;
    yspeed=y;
  }
  
  boolean eat(PVector pos)
  {
    float d=dist(x,y,pos.x,pos.y);
    if(d<1)
    {
     total++;
      return true;
    }
    else return false;
  }
  
  void update()
  {
    if(total==tail.size())
    {
      for(int i=0; i<tail.size()-1; i++)
      {
        tail.set(i, tail.get(i+1));
      }
    }
    tail.add((int)total, new PVector(x,y));
   println(tail.size());
    x=x + xspeed*scl;
    y=y + yspeed*scl;
    x=constrain(x,0,width-scl);
    y=constrain(y,0,height-scl);
  }
  
  void show()
  {
    fill(0);
    for(int i=0; i<tail.size(); i++)
      {
       rect((int)tail.get(i).x,(int)tail.get(i).y,scl,scl);
      }
    stroke(255);
    fill(0);
    rect(x,y,20,20);
  }    
}

also when putting tail.add((int)total, new PVector(x,y)); into the if statement the tail.size() only increases when food is eaten which is good but the snake doesn’t stay connected. Also, I have learned that the for loop in void update() doesn’t affect anything because i is not affecting the tail.add((int)total, new PVector(x,y)); So how would I correlate the two? Also I might be thinking about my own code wrong so feel free to correct me there too.

What does total represent in your program?

total is just a float value that represents the total length of the snake. The tail.size() should only update when total goes up by 1 which happens when food is eaten. In other words if tail.size() and total are equal the snake should add 1 tail block onto it and total will stay the same(1 under tail.size()) until another piece of food is eaten

Hi Dinator14,

You should be able to add a new element simply like this:

tail.add(new PVector(x,y))

Also, you don’t need to cast all your variable, instead define them with the proper type. For example total can be an integer and this way no casting is needed.

Now for your question I don’t have the time to look into it right now but I think you are over-complicating everything. Put the concept down on a piece of paper, and try to simplify your code (don’t be afraid do delete whole bunch of code, it happens a lot :slight_smile:)

For example you should not have the need to keep track of a total variable, it is given by tail.size().

And can you please put the whole code, not just your class to see how evey thing is working together?

2 Likes