i am making a snake game in Raylib using C. I developed a function for handling collision for the snake and the apple (So that i could avoid 5 depth nesting which is quite unreadable). When the snake first picks up the apple, it does not grow, the other times it picks up the apple, it does grow. Why is this the case? Here is my code:
#include <math.h>
#include <raylib.h>
#include <stdint.h>
#include <stdlib.h>
#include <time.h>
#define MAX_LEN 32768
#define TILE_SIZE 32
#define TILE_SPACING 4
float Distance(Vector2 v1, Vector2 v2) {
float dx = v2.x - v1.x;
float dy = v2.y - v1.y;
return sqrt(dx * dx + dy * dy);
}
void CheckCollision(bool collision, Vector2* applePos, Vector2* snakePos,
int* snakeLength, Vector2 appleCenter) {
// Handle collision with apple
if (collision) {
applePos->x = rand() % 1024 + 1;
applePos->y = rand() % 1024 + 1;
if (*snakeLength < MAX_LEN) {
(*snakeLength)++;
if (*snakeLength > 1) snakePos[*snakeLength] = snakePos[*snakeLength - 1];
}
} else {
ClearBackground(BLACK);
}
for (int i = 0; i < *snakeLength - 1; i++) {
Vector2 centerTile = {snakePos[i].x + TILE_SIZE / 2, snakePos[i].y + TILE_SIZE / 2};
Vector2 snakeCenter = {snakePos[0].x + TILE_SIZE / 2, snakePos[0].y + TILE_SIZE / 2};
float dis1 = Distance(centerTile, appleCenter);
float dis2 = Distance(snakeCenter, centerTile);
bool collision1 = dis1 < TILE_SIZE + TILE_SPACING;
bool collision2 = dis2 < TILE_SIZE + TILE_SPACING;
if (collision1) {
applePos->x = rand() % 1024 + 1;
applePos->y = rand() % 1024 + 1;
}
}
}
int main(void) {
const uint16_t screenWidth = 1024;
const uint16_t screenHeight = 1024;
const Font font = GetFontDefault();
char snake[MAX_LEN];
int snakeLength = 1;
srand(time(NULL));
KeyboardKey keyPressed = KEY_W; // Start with initial direction
Vector2 snakePos[MAX_LEN];
snakePos[0].x = 512 - TILE_SIZE / 2;
snakePos[0].y = 512 - TILE_SIZE / 2;
Vector2 applePos = {rand() % 1024 + 1, rand() % 1024 + 1};
InitWindow(screenWidth, screenHeight, "Snake");
SetTargetFPS(15);
while (!WindowShouldClose()) {
BeginDrawing();
// Handle input and change direction
if (IsKeyPressed(KEY_W) && keyPressed != KEY_S) keyPressed = KEY_W;
if (IsKeyPressed(KEY_A) && keyPressed != KEY_D) keyPressed = KEY_A;
if (IsKeyPressed(KEY_S) && keyPressed != KEY_W) keyPressed = KEY_S;
if (IsKeyPressed(KEY_D) && keyPressed != KEY_A) keyPressed = KEY_D;
// Move snake head
if (keyPressed == KEY_W) snakePos[0].y -= TILE_SIZE + TILE_SPACING;
if (keyPressed == KEY_A) snakePos[0].x -= TILE_SIZE + TILE_SPACING;
if (keyPressed == KEY_S) snakePos[0].y += TILE_SIZE + TILE_SPACING;
if (keyPressed == KEY_D) snakePos[0].x += TILE_SIZE + TILE_SPACING;
// Update snake body positions
for (int i = snakeLength - 1; i > 0; i--) {
if (snakeLength > 1) snakePos[snakeLength] = snakePos[snakeLength - 1];
}
// Check collision with apple
Vector2 snakeCenter = {snakePos[0].x + TILE_SIZE / 2, snakePos[0].y + TILE_SIZE / 2};
Vector2 appleCenter = {applePos.x + TILE_SIZE / 2, applePos.y + TILE_SIZE / 2};
float distance = Distance(snakeCenter, appleCenter);
bool collision = distance <= TILE_SIZE + TILE_SPACING;
CheckCollision(collision, &applePos, snakePos, &snakeLength, appleCenter);
// Draw snake
for (int i = 0; i < snakeLength; i++) {
float tileX = snakePos[i].x;
float tileY = snakePos[i].y;
DrawRectangle(tileX, tileY, TILE_SIZE, TILE_SIZE, GREEN);
}
// Draw apple
DrawRectangle(applePos.x, applePos.y, TILE_SIZE, TILE_SIZE, RED);
EndDrawing();
}
CloseWindow();
return 0;
}
In these sections of the code:
// Update snake body positions
for (int i = snakeLength - 1; i > 0; i--) {
if (snakeLength > 1) snakePos[snakeLength] = snakePos[snakeLength - 1];
}
and
if (*snakeLength < MAX_LEN) {
(*snakeLength)++;
if (*snakeLength > 1) snakePos[*snakeLength] = snakePos[*snakeLength - 1];
}
instead of if (*snakeLength > 1) snakePos[*snakeLength] = snakePos[*snakeLength - 1];
it was,snakePos[*snakeLength] = snakePos[*snakeLength - 1];
previously, even more previously, it was if (*snakeLength > 1) snakePos[*snakeLength - 1] = snakePos[*snakeLength - 2];
, and at first, it was snakePos[*snakeLength - 1] = snakePos[*snakeLength - 2];
. I though this part of the program was the issue, it probably is, but i cant figure out whats the correct version of this code snippet. The very first version of this code snippet works the best by far, but the first time the snake picks up an apple, it still wont grow. The other version may display the second tile on the top-left corner, or do not make the snake grow at all when picking apples at any time.
EDIT: To make the task of fixing this issue easier, i provided a simpler code example, here it is:
All the libraries needed:
#include <math.h>
#include <raylib.h>
#include <stdint.h>
#define
Variables:
#define MAX_LEN 32768
#define TILE_SIZE 32
#define TILE_SPACING 4
The collision checker, briefly outputs text, informing us that the snake collided with the apple:
void CheckCollision(bool collision) {
if (collision) {
DrawText("Collided!", 512, 512, 64, WHITE);
} else {
ClearBackground(BLACK);
}
}
The main function:
int main(void) {
// Initialize variables
const uint16_t screenWidth = 1024;
const uint16_t screenHeight = 1024;
int snakeLength = 1;
Vector2 snakePos[MAX_LEN];
snakePos[0].x = 512 - TILE_SIZE / 2;
snakePos[0].y = 512 - TILE_SIZE / 2;
Vector2 applePos = {512, 600};
InitWindow(screenWidth, screenHeight, "Snake");
SetTargetFPS(15);
while (!WindowShouldClose()) {
BeginDrawing();
snakePos[0].y += 50.0f; // Make the snake go down
Vector2 snakeCenter = {snakePos[0].x + TILE_SIZE / 2, snakePos[0].y + TILE_SIZE / 2};
Vector2 appleCenter = {applePos.x + TILE_SIZE / 2, applePos.y + TILE_SIZE / 2};
float distance = sqrt(snakeCenter.x - appleCenter.x + snakeCenter.y - appleCenter.y);
bool collision = distance <= TILE_SIZE + TILE_SPACING;
CheckCollision(collision); // Check collision
DrawRectangle(snakePos[0].x, snakePos[0].y, TILE_SIZE, TILE_SIZE, GREEN);
DrawRectangle(applePos.x, applePos.y, TILE_SIZE, TILE_SIZE, RED);
EndDrawing();
}
CloseWindow();
return 0;
}
I also tested this out with input and output as well, and there seem to be random places where the text "Collided!" Appears? I do not know why this is the case, i tried figuring out why but i couldn't.