Wednesday, December 4, 2024

Part 2: Snake Game in C | Snake Collision Detection, Calculate next position and Changing direction

Previous page 

In this section, we will discuss the following items.

  • Techniques to change direction of the snake based on user input.
  • Techniques to calculate the snake's next position.
  • Techniques to check for snake collision with window and its body.

Snake direction and food

We are using the gotoxy(int x, int y) function to move the snake on the screen. The top left corner of the PC window starts at (0,0). The right side of the screen is positive x-axis, and the bottom of the screen is positive y-axis. However, we can represent the direction of snake in all four directions by positive and negative axes (-x, 0), (x, 0), (0, -y), and (0, y). For the left arrow key, set direction as negative x-axis (-x,0), for the right arrow key set direction as positive x-axis (x,0), for the up arrow key set direction as negative y-axis (-y,0), and for the down arrow key set direction as positive y-axis (y,0). When the direction is changed, the snake will move perpendicular to current position, so we don't have to use both x, and y coordinates values. The coordinates system of the PC window is shown in the image below.
Simply, we draw the "0" character as food in a random position on the window. To simplify this process, we create an array of Point2D struct variables (struct Point2D food[sSize]) and initialize it by storing all possible coordinates of the window. In this game, there are 800 possible coordinates, 40 towards x-axis and 20 towards y-axis. Now, we generate a random number between 0 and 800. The random number is used as an index of the food array, and we draw the "0" character at the coordinate of that index.

struct Point2D dir[4]; //array for snake direction vector
struct Point2D food[sSize];

void initDir(){

    //Up direction
    dir[UP].x = 0;
    dir[UP].y = -1;

    //Down direction
    dir[DW].x = 0;
    dir[DW].y = 1;

    //Right Direction
    dir[RT].x = 1;
    dir[RT].y = 0;

    //Left Direction
    dir[LT].x = -1;
    dir[LT].y = 0;
}

void initFood(){

    int i=0;
    for(int j=0;j<winYLen;j++){
        for(int k=0;k<winXLen;k++){
            food[i].x = k + startPosX;
            food[i].y = j + startPosY;

            i++;
        }
    }
}

Calculate Snake's next position

At first, we have to initialize snake variable which initial values such as length, body, head, tail and direction. We set snake length and calculate initial snake body coordinates from initial starting position, set snake head and tail coordinates and set snake initial direction. In this game, I have used initial length of 3, snake starting position (11,10), and initial moving direction is right (1, 0). The body of snake will be (11,10), (12,10), (13,10), snake head will be (13,10) and tail will be (11,10).

void initSnake(){

    for(int i=0;i<startLen;i++){
        s.body[i].x = startPosX + i;
        s.body[i].y = 10;
    }

    s.head.x = s.body[startLen-1].x;
    s.head.y = s.body[startLen-1].y;

    //In first frame of the game tail position
    //need to be out of window or blank
    s.tail.x = 0;
    s.tail.y = 0;

    s.dir.x = dir[RT].x; s.dir.y = dir[RT].y;

    s.length = startLen;
}
Now, using initial snake position and direction we will calculate next position. Next position will be always snake's head position. As you know, snake always move in straight line and change direction ±90 degree from its current head position. In each given frame only one coordinate axis (either x or y) will change. So, we need to calculate new head position only. Snake body is an array of 2D coordinates. We know the maximum possible length of snake is 800 in this example. We had already created snake body array of size 800. We will add new head position in body array and swap the body coordinates so that previous tail coordinates of snake will no longer be the part of snake body will get removed. If snake collide with food, then we will not swap the body, but we will increase the length of the snake and increase score. On screen we will print blank on previous tail coordinates and print "*" on new head coordinate. Even we don't have to print whole snake body in each frame. Also, we don't have to print window in each frame, it is always fixed. Here, moving parts will be always head and tail of the snake other parts are not changes.  

For example, if the snake head is (13,10) and the direction is right (1,0), then add 1 unit to the x-coordinate. If direction is up (0, -1), then subtract 1 unit from the y-coordinate. If the direction is down (0,1), then add 1 unit to the y-coordinate. If direction is left (-1,0), then subtract 1 unit from the x-coordinate. However, if previous direct is right, then user should not be allowed to move left. Snake should move only by 90 degrees either positive or negative. 

int buildSnake(struct Point2D f,struct Point2D d){

    if(s.dir.x != -d.x && s.dir.y != -d.y){
        s.dir.x = d.x; s.dir.y = d.y;
    }

    s.head.x = s.body[s.length-1].x + s.dir.x;
    s.head.y = s.body[s.length-1].y + s.dir.y;

    s.body[s.length].x = s.head.x;
    s.body[s.length].y = s.head.y;
    if(s.head.x == f.x && s.head.y == f.y){
        s.length++;
        return 1;
    } else {
        s.tail.x = s.body[0].x;
        s.tail.y = s.body[0].y;
        //Swap snake body back starting from initial index
        for (int i = 0;i<s.length;i++){
            s.body[i].x = s.body[i+1].x;
            s.body[i].y = s.body[i+1].y;
        }
    }

    return 0;
}

Collision detection

We have very few objects in this game, so the collision detection is very simple. We have to detect the collision of snake's head with four walls of window and its body. We already have coordinates of all these objects. After calculating the next snake's head position, we will compare the coordinates of snake's head position with four walls. Also, we will check weather new head position falls within its body. If a collision with walls and its body found, we will terminate game and ask user to restart again or exit. 

int checkCollision(){
    if(s.length == sSize){
        return 2;
    }

    if(s.head.x == wX1 || s.head.x == wX2){
        return 1;
    }

    if(s.head.y == wY1 ||s.head.y == wY3){
        return 1;
    }

    for (int i=0;i<s.length-2;i++){

        if(s.head.x == s.body[i].x && s.head.y == s.body[i].y){
            return 1;
        }
    }

    return 0;
}
 
Please find video of this article in YouTube.



No comments:

Post a Comment