Automatic movement for snake

edited August 2017 in Questions about Code

I'm recreating the old game snake but I have no clue how to have it so that the snake will move on its own (except when you change it's direction obviously). I only need help with the movement, other than that i only have the basic code.

Tagged:

Answers

  • edited August 2017

    sorry, forgot to upload what i have. here's the code

    int headX = 250;
    int headY = 250;
    int foodX = int( random(50));
    int foodY = int( random(50));
    int foodXX = round(foodX);
    int foodYY = round(foodY);
    int foodXXX = foodXX * 10;
    int foodYYY = foodYY * 10;
    int movup = 0;
    int movdown = 0;
    int movleft = 0;
    int movright = 0;
    
    //setup
    void setup() {
      size(500, 500);
      frameRate(60);
    }
    
    //drawing
    void draw() {
      background(255);
      rect(headX, headY, 10, 10);
      rect(foodXXX, foodYYY, 10, 10);
    }
    
    //controls
    void keyPressed() {
      if (key == CODED) {
        if (keyCode == UP) {
          headY = headY - 10;
          movright = 0;
          movleft = 0;
          movdown = 0;
          movup = 1;
        }
        else if (keyCode == DOWN) {
          headY = headY + 10;
          movright = 0;
          movleft = 0;
          movup = 0;
          movdown = 1;
        }
        else if (keyCode == LEFT) {
          headX = headX - 10;
          movright = 0;
          movup = 0;
          movdown = 0;
          movleft = 1;
        }
        else if (keyCode == RIGHT) {
          headX = headX + 10;
          movleft = 0;
          movup = 0;
          movdown = 0;
          movright = 1;
        }
      }
    
    if(headX == foodXXX) {
      if(headY == foodYYY) {
        foodX = int( random(50));
        foodY = int( random(50));
        foodXX = round(foodX);
        foodYY = round(foodY);
        foodXXX = foodXX * 10;
        foodYYY = foodYY * 10;
      }
    }
    }
    
  • Please edit your post (gear icon in the top right corner of your post), select your code and hit ctrl+o to format it. Make sure there is an empty line above and below your code.

    Kf

  • so that the snake will move on its own

    Do you mean an AI -- so that the computer can play snake and try to live as long as possible?

  • no, I'm fairly new to this coding language so my code may seem a bit crude. But i want it so that the snake will keep moving in the same direction until you press any of the arrow keys to make it move directions

  • It's just a case of remembering the last key that was pressed and applying that until it changes.

    Add a direction variable, change that in your keyPressed method. Move the rest of the moving code, modified to use direction rather than key, to draw so that it's executed every frame. (This will possibly be way too fast).

    foodXXX = foodXX * 10;
    foodYYY = foodYY * 10;
    

    Think of better names.

  • Oh hey! I made a snake game a while back and ran into all the obstacles you did. I'll answer any questions you have :) Here's all my code if you need help. Just note I did mine in an OOP sense, meaning I made 'objects' which are their own little identities that I can spawn up whenever I want instead of typing everything in the draw and setup method.

    I don't mean to sound too harsh, but from what I'm seeing from your current methods, I believe snake might need to wait until you learn a few more concepts. Not to fret! The Processing tutorial section has many well written pages to get you on your way to the god of all snakes ... inside your program of course. I'll help introduce you to these concepts after the initial example.

    Because the way I structured it is wayyy different than how you've done it, I'll just make a separate example that is more of what you're doing :

    PVector head; // A variable that stores an x and a y in one. Think of it like headX and headY in one
    int headSize = 20; // How long the head will be
    int snakeSpeed = 5; // How fast the snake moves each loop
    color snakeColor = color(50, 200, 50); // Stores the color of the snake in (R, G, B)
    char direction; // A letter to represent a direction. w = Up, a = Left, s = Down, d = Right
    
    void setup() {
      size(800, 800);
      noStroke(); // I prefer no outline on snakes :)
      head = new PVector(width / 2, height / 2); // How to make the X and Y values. (X, Y) is the numbers
                                                 // This will make the snake's head in the middle of the screen
      direction = 'w'; // Just set the direction it will first move                                            
    }
    
    void draw() {
      background(255);
      if (keyPressed) // If we press a key
       direction = key; // Set the direction equal to whatever key we press (like 'd')
      move();
      fill(snakeColor); // Fill the snake's color
      rect(head.x, head.y, headSize, headSize); // Draw the snake
    }
    
    void move() {
      switch(direction) { 
       case 'w' : // If direction is == w
        head = new PVector(head.x, head.y - snakeSpeed); // Make the head's new location up snakeSpeed
        break; // This makes it so we won't do any of the other scenarios below w
       case 'a' : // If direction is == a 
        head = new PVector(head.x - snakeSpeed, head.y); // Make the head's new location left snakespeed
        break;
       case 's' : // If direction is == s
        head = new PVector(head.x, head.y + snakeSpeed); // Make the head's new location down snakeSpeed
        break;
       case 'd' : // If direction == d
        head = new PVector(head.x + snakeSpeed, head.y); // Make the head's new location right snakeSpeed
        break;
      }
    }
    

    Hopefully the comments can explain most of it. The move(); line inside the draw will do all of the code inside of the move() { } method. It's a bit buggy, not too interested to figure out why, but hopefully it will lead you on the right path :)

    The main problem with your code is putting all of that code within the keyPressed method. When you do that, that code will only activate whenever you press the key down. I basically did the whole move thing inside the draw method, meaning every loop it's going to move whatever direction we currently have it set to.

    So you may be asking from my very mean comment earlier (sorry :( I tried to word it nicely since I hope everyone learns to become programming wizards, I hope one day to be also) what else do you need to learn, and how do I know you need to learn it?

    Well here's a few things : Objects - This will teach you how to make a body square. This way you don't have to keep writing out XX1 and XX2 and make a new variable for every snake and every food position yourself. It would become incredibly taxing to write after a while. Arrays - These store a bunch of variables into one! (Not quite like the PVector I showed earlier, since that stored one x and y while arrays can store a near limitless amount of the same variable) This way you can add a new body when you eat a food sprite, and is basically essential to make your snake bodies move to where each body takes the previous position of the one in front of it. I'm not sure if this game would be possible without these useful things!

    Daniel Shiffman made some awesome videos over arrays and objects! Very fun guy to watch and could be better if you like the video format instead.

  • edited August 2017

    thanks so much dino! but i've got down what i didn't have but my all it does is make my snake just move really fast in the direction that you chose, it will stop when you let go of one of the keys here's what i have now:

    PVector head;
    int headSize = 10;
    int foodX = int( random(50));
    int foodY = int( random(50));
    int foodXX = round(foodX);
    int foodYY = round(foodY);
    int foodXXX = foodXX * 10;
    int foodYYY = foodYY * 10;
    int snakeSpeed = 10;
    char direction;
    
    //setup
    void setup() {
      size(500, 500);
      frameRate(60);
      head = new PVector(width / 2, height / 2);
      direction = 'w';
    }
    
    //drawing
    void draw() {
      background(255);
      rect(head.x, head.y, 10, 10);
      rect(foodXXX, foodYYY, 10, 10);
      if (keyPressed) {
        direction = key;
       move();
       if(head.x == foodXXX) {
      if(head.y == foodYYY) {
        foodX = int( random(50));
        foodY = int( random(50));
        foodXX = round(foodX);
        foodYY = round(foodY);
        foodXXX = foodXX * 10;
        foodYYY = foodYY * 10;
      }
    }
     }
    }
    
    void move() {
      switch(direction) {
        case 'w' :
          head = new PVector(head.x, head.y - snakeSpeed);
          break;
        case 'a' :
          head = new PVector(head.x - snakeSpeed, head.y);
          break;
        case 's' :
          head = new PVector(head.x, head.y + snakeSpeed);
          break;
        case 'd' :
          head = new PVector(head.x + snakeSpeed, head.y);
          break;
      }
    }
    
  • edited August 2017

    Ah :) It's because you put the {}'s around the if (keyPressed) around all the lines below it

    If there is only one statement below an if statement you don't have to put the curly braces. People bash me for it because it can cause problems when bug fixing but I've never had trouble with it.

    So you can either do

      if (keyPressed) {
        direction = key;
    }
    

    or

    if (keyPressed)
     direction = key;
    

    So the final draw method will look like this :

    void draw() {
      background(255);
      rect(head.x, head.y, 10, 10);
      rect(foodXXX, foodYYY, 10, 10);
      if (keyPressed) 
        direction = key;
       move();
       if(head.x == foodXXX) {
      if(head.y == foodYYY) {
        foodX = int( random(50));
        foodY = int( random(50));
        foodXX = round(foodX);
        foodYY = round(foodY);
        foodXXX = foodXX * 10;
        foodYYY = foodYY * 10;
      }
     }
    }
    

    And that should fix it! Before I move onto the snake's speed I should note that if you select all your code and press Ctrl + T it will automatically indent all the code which will get rid of the nasty

      }
    }
     }
    }
    

    line in your draw method :) Just a tip I just learned! Thought it might be useful. Anyways ... onto speed!

    I also noticed that it goes really fast. There's two solutions I can think of. The first would be to change the framerate to frameRate(30); This will make it choppier, but will make the snake a lot slower (Also in your sketch you set frameRate to 60. Processing sets it to 60 by default c;)

    The other solution would be to set the snakeSpeed to 5 (half of what it is now) and keep the frameRate and 60. (Method 1 in one second would move forward 10 pixels 30 times, 30 * 10 = 300 pixels/sec | Method 2 in one second would move forward 5 pixels 60 times, 60 * 5 = 300 pixels/sec) Both of these methods will move the same speed per second, and this method will feel a lot lot smoother since the second method is 60 FPS instead of 30. The problem of course is that the snake doesn't move in the same grid format of its head, since now every move it's going only half of its head instead of a full length. A solution to this could be to only accept a change in direction every other frame (this way it will go two lengths every time in a given directon no matter if you press your key down to go somewhere else) Because of how fast the program is refreshing, the user can't really feel the input lag. So how could we make it so it only changes direction every half frame? My solution is to change the if (keyPressed) line to:

      if (keyPressed && frameCount % 2 != 0) 
        direction = key;
    

    (And also get rid of the frameRate line since we're going to run this bad boy at 60 fps :d)

    Just in case you don't know, the % (modulus operator) is like division except the number it outputs is the remainder from the division. So like 7 % 3 is 1 because 7 / 3 is 2 with 1 left over. Also, frameCount counts how many times the draw loop has looped basically, so after 1 second it should be around 60 (it's really hard to get exactly 60) and after 2 seconds it will be around 120, meaning the draw loop will have executed all the code inside of it 120 times. So we take this frameCount and divide it by 2, and if the remainder doesn't equal 0 (aka if it's 1 aka if frameCount is odd) only then will we update the direction with the key we press down. Why != instead of ==? Well honestly I just played around with it and saw if it was set to == then the snake is unaligned with the food and stuff and when I set it to != I saw that it was aligned with the food.

    I actually have no idea why != works. I would think the first frameCount would move forward 5, making it unalligned with the grid we kind of set up, so only on the second frame would it be alligned, thus acceptable to move, so the 4th, 6th, 8th ... would also be like this which means on even numbers which means % 2 == 0 but it didn't seem to work out like that :d If someone could explain why I'd be interested)

    Now honestly ... we kinda got lucky with the numbers we chose to make this all work. If you wanted to make the speed like 2, then you could change the line to frameCount % 5 == 0 Because then every 5th will accept input but then this could be noticable by the user if their key doesn't register at the right time so then it all gets messy.

  • Once again thanks so much Dino. the new problem i have is that the heads y axis is a little off which causes the food to be a little higher than the head, this means that the the only way to collect the food is to get the x axis right and then move up or down on it. here's the code now

    PVector head;
    int headSize = 10;
    int foodX = int( random(50));
    int foodY = int( random(50));
    int foodXX = round(foodX);
    int foodYY = round(foodY);
    int foodXXX = foodXX * 10;
    int foodYYY = foodYY * 10;
    int snakeSpeed = 2;
    char direction;
    
    //setup
    void setup() {
      size(500, 500);
      frameRate(60);
      head = new PVector(width / 2, height / 2);
      direction = 'w';
    }
    
    //drawing
    void draw() {
      background(255);
      rect(head.x, head.y, 10, 10);
      rect(foodXXX, foodYYY, 10, 10);
      if (keyPressed && frameCount % 5 == 0) 
        direction = key;
      move();
      if (head.x == foodXXX) {
        if (head.y == foodYYY) {
          foodX = int( random(50));
          foodY = int( random(50));
          foodXX = round(foodX);
          foodYY = round(foodY);
          foodXXX = foodXX * 10;
          foodYYY = foodYY * 10;
        }
      }
    }
    
    void move() {
      switch(direction) {
      case 'w' :
        head = new PVector(head.x, head.y - snakeSpeed);
        break;
      case 'a' :
        head = new PVector(head.x - snakeSpeed, head.y);
        break;
      case 's' :
        head = new PVector(head.x, head.y + snakeSpeed);
        break;
      case 'd' :
        head = new PVector(head.x + snakeSpeed, head.y);
        break;
      }
    }
    
Sign In or Register to comment.