C language hand playing greedy snake version 1.0 (including interface, switchable music and three modes to play)

Posted by LordTyphon on Sun, 02 Jan 2022 15:56:40 +0100


C language is almost at the end of learning. With the basis of grammar, I want to make a small game to show what I have learned. Learning from the blogs of some bloggers, I finally chose to use C language to fight greedy snakes. My original version of greedy snake has interface, switchable music and three level modes. Next, let's briefly experience the greedy snake I wrote!!!

Because the gif motion picture I made has no sound, so we don't feel the beauty of music, but the program itself has some!!! Next, I will introduce my idea of beating greedy snakes with my hands in C language.

Overall framework

  1. Make the interface menu

    1. We can use the EasyX graphics library to draw an interface (add the header file #include < graphics. H >), and I draw a picture as the background of the interface.
    2. Add the buttons we want to click on the interface, such as three modes, switch music, exit, etc. Here we just draw the keys we want to have on the interface, but the implementation function is still behind.
  2. Realize the key of each function

    For each button on the interface, we can use the function of obtaining mouse information in EasyX graphics library. By clicking the button, we can judge the range of the mouse, and realize its function for different information. For example, click to exit the game. We will draw a new interface, and the background will show goodbye.

  3. First realize the two simplest functions

    1. Play and turn off music functions.
    2. Exit the game function.
  4. Realize three level modes

    In fact, the logic of the three level modes is similar, but we have added some restrictions or lifted some restrictions on the most basic common mode, so we have the other two modes

  5. Implementation of endless mode

    1. When our mouse moves in the range of normal mode keys, we start to realize our normal mode by clicking the left button
    2. First, we need to redraw the interface of a game, and the formation of the initial interface requires us to give all the parameters, For example, snake parameters (such as initial length, initial coordinates, radius of each section, initial direction, snake color), food parameters (such as randomly generated food coordinates, radius of food, color of food, and judgment signs whether food is eaten).
    3. After we initialize these parameters, we can draw these parameters through many functions of EasyX graphics library.
    4. The next step is to let the little snake move. You can first think about how the little snake moves forward bravely when you don't control it? And how to change after meeting the boundary? What happens when the snake head touches the body?
    5. Then we see a little snake moving in the figure. We need to control the movement of the little snake by pressing the key to make it close to the food.
    6. We then have to judge whether we have eaten food. If we have eaten food, the judgment mark of food will become also eaten, and new food will continue to be generated.
    7. But when we get to this step, we will find that there seems to be no next step, so is it over? Of course not. We will cycle back and forth (draw the parameters of the snake at this time - > the snake moves in the direction of the snake head - > control the snake to change the moving direction - > judge whether to eat food) until the snake head touches the snake and dies.
    8. After death, we draw a new interface. The background can be a picture of chicken, ha ha. Finally return to the start menu.
  6. Follow the normal mode to change the functions of other level modes

Menu interface drawing

The drawing of the menu interface is actually very simple. In fact, it uses the functions in the EasyX graphics library. But if you want to use it, you have to install it. This step is very simple. You can baidu. I'll go directly to my code!!!

void menuInit()
{
    //Create a width and height interface
	initgraph(width, heigth);
    //Fill this interface with figure 1
	loadimage(&img1, "Figure 1.png", width, heigth); 
    //The vertex in the upper left corner of Figure 1 is (0, 0)
	putimage(0, 0, &img1);
	//Set the filling background to transparent, so that the background is the color of the picture when filling text
    setbkmode(TRANSPARENT);	                    
    //Output text at the upper left coordinate (x, y)
	outtextxy(260, 50, "Snake Wars");			
	outtextxy(155, 250, "Normal mode");
	outtextxy(395, 250, "Obstacle mode");
	outtextxy(275, 250, "Endless mode");
	outtextxy(155, 300, "Continue the game");
	outtextxy(395, 300, "Exit the game");
	outtextxy(155, 350, "Turn on the music");
	outtextxy(395, 350, "Turn off music");
    //Draw a rectangle in the upper left corner (x1, y1) and lower right corner (x2, y2), which is the range set when the mouse clicks each button
	rectangle(150, 245, 225, 270);				
	rectangle(270, 245, 345, 270);
	rectangle(390, 245, 465, 270);
	rectangle(150, 295, 225, 320);
	rectangle(390, 295, 465, 320);
	rectangle(150, 345, 225, 370);
	rectangle(390, 345, 465, 370);
}

Control music playback / off

  1. First, we need to add the header file #include < mmstream h> And multimedia device interface #pragma comment (LIB, "winmm. Lib").
  2. Then use mciSendString(TEXT("open path \ \ music name. mp3 alias XXX"), NULL, 0, NULL); Our program can play this music, and the music is called XXX when used
  3. When playing music, we use mciSendString(TEXT("play XXX repeat"), NULL, 0, NULL);, Repeat can be deleted, and its meaning is to keep the song playing without ending.
  4. When closing music, we use mciSendString(TEXT("close XXX"), NULL, 0, NULL);

Realize the interface of exiting the game

This function is super simple! I just drew a new interface and used a lovely picture to say goodbye.

Code on!!!

void gameExit()
{
	initgraph(width, heigth);
	loadimage(&img2, "Figure 2.jpg", width, heigth);
	putimage(0, 0, &img2);
}

Normal mode parameter initialization

  1. First, we need to initialize a new interface

  2. We need to initialize the parameters of the snake. First, define various parameters of the snake through the structure. Take a look at my code!

    struct Snake
    {
    	int size;					//Snake length
    	int speed;					//Snake speed
    	int dir;					//Snake direction
    	POINT coor[SNAKE_MAX];		//Maximum length of snake
    }snake;
    

    POINT itself is a structure, which contains x and y coordinates

  3. We then initialize the parameters of the food.

    struct Food
    {
    	int x;
    	int y;
    	int r;
    	bool flag;					//1 didn't eat 0 ate it
    	DWORD color;
    }food;
    //typedef unsigned long DWORD;
    //DWORD represents a 32 bit unsigned integer
    
  4. Next, we will initialize the above parameters and add the code!

    void gameInit1()
    {
    	initgraph(width, heigth);
    	//Snake initialization
    	snake.size = 3;
    	snake.speed = 10;
    	snake.dir = LEFT;
    	for (int i = snake.size - 1; i >= 0; i--)
    	{
    		snake.coor[i].x = 10 * i + 400;
    		snake.coor[i].y = 30;
    	}
        //Food initialization
    	food.r = rand() % 16 + 5;		//The food radius shall be controlled within 5 ~ 15
    	food.x = rand() % width;		//Initialize x
    	food.y = rand() % heigth;		//Initialize y
    	food.color = RGB(rand() % 256, rand() % 256, rand() % 256);
    	food.flag = true;				
    }
    

Common mode game interface

This is also an easy part to implement. Now that we have parameters, we just need to draw the parameters through EasyX graphics library

void gameDraw1()
{
	loadimage(&img4, "Figure 4.png", width, heigth);
	putimage(0, 0, &img4);
    //Set drawing fill color
	setfillcolor(RGB(61, 89, 171));
	for (int i = 0; i < snake.size; i++)
	{
        //Draw each coordinate as a circle with a radius of 5
		solidcircle(snake.coor[i].x, snake.coor[i].y, 5);
	}
    //Change the drawing fill color to the color of the food
	setfillcolor(food.color);
    //To judge the existence of food, draw a circle with a radius of food at the coordinates
	if (food.flag)
		solidcircle(food.x, food.y, food.r);
    //End batch drawing
	EndBatchDraw();					
}

Normal mode Snake Movement

  1. How do we make the snake move? Are snakes actually following the snake head? To be more specific, each section moves with the previous section. So we just need to cycle from the last section to the second section, and the coordinates of each section are equal to the previous section, while the coordinates of the snake head depend on the direction of the snake at this time. You can use a switch statement to make changes.

  2. We need to think about what to do when the snake head meets the border.

  3. If the coordinates of the snake's head are equal to the coordinates of the snake's body, it will fail. Let's go straight to the code!

    bool snakeMove1()
    {
    	for (int i = snake.size - 1; i > 0; i--)
    	{
    		snake.coor[i] = snake.coor[i - 1];
    	}
    	switch (snake.dir)
    	{
    	case UP:
    		snake.coor[0].y -= snake.speed;
    		if (snake.coor[0].y <= 0)
    			return false;
    		break;
    	case DOWN:
    		snake.coor[0].y += snake.speed;
    		if (snake.coor[0].y >= heigth)
    			return false;
    		break;
    	case LEFT:
    		snake.coor[0].x -= snake.speed;
    		if (snake.coor[0].x <= 0)
    			return false;
    		break;
    	case RIGHT:
    		snake.coor[0].x += snake.speed;
    		if (snake.coor[0].x >= width)
    			return false;
    		break;
    	}
    	for (int i = snake.size - 1; i > 0; i--)
    	{
    		if (snake.coor[0].x == snake.coor[i].x && snake.coor[0].y == snake.coor[i].y)
    			return false;
    	}
    	return true;
    }
    

    Here, when the snake head encounters the boundary, I directly judge the boundary after the change. When the snake head touches the snake body, it is judged by a cycle, and it returns false. Because all my functions are modular, when using this function, just use a judgment statement. So as long as I return false, I will actually implement the failed function of the Bureau, and you will see this figure

The keyboard controls the snake to change direction

First, let's simply know the two functions

  1. kbhit() function

    • Function and return value: check whether there is keyboard input at present. If yes, a non-0 value will be returned; otherwise, 0 will be returned
    • Include header file: < conio h>
  2. getch() function

    • Function: read a character from the console, but it is not displayed on the screen
    • Include header file: < conio h>

Therefore, we must first judge whether there is input on our keyboard. If not, we will judge the characters read from the keyboard. We can use a switch statement. Code!

void keyControl()
{
	if (_kbhit())
	{
		switch (_getch())
		{
		case 'W':
		case 'w':
		case 72:
			if (snake.dir != DOWN)
				snake.dir = UP;
			break;
		case 'S':
		case 's':
		case 80:
			if (snake.dir != UP)
				snake.dir = DOWN;
			break;
		case 'A':
		case 'a':
		case 75:
			if (snake.dir != RIGHT)
				snake.dir = LEFT;
			break;
		case 'D':
		case 'd':
		case 77:
			if (snake.dir != LEFT)
				snake.dir = RIGHT;
			break;
		case ' ':
			while (_getch() != ' ');	//Give the read space an endless loop and block the program
			break;
		}
	}
}

Among them, we will find that an if conditional statement is added in each direction. The reason is very simple. The little snake originally moved to the left. Can we directly change the direction to make him move to the right? The program is OK, hehe, but not logically, so you need to add a judgment statement.

Judging food status

Here we can eat as long as the snake is next to the range of food. If you eat, regenerate the parameters of the new food. Just go straight to the code!

void Eat()
{
	if (food.flag && snake.coor[0].x >= food.x - food.r && snake.coor[0].x <= food.x + food.r && snake.coor[0].y >= food.y - food.r && snake.coor[0].y <= food.y + food.r)
	{
		food.flag = false;
        //Depending on the size of the food, the length of the snake will be different
		switch (food.r / 5)
		{
		case 1:
			snake.size += 1;
			break;
		case 2:
			snake.size += 2;
			break;
		case 3:
			snake.size += 3;
			break;
		default:
			break;
		}
	}
	if (!food.flag)
	{
		food.x = rand() % width;
		food.y = rand() % heigth;
		food.color = RGB(rand() % 256, rand() % 256, rand() % 256);
		food.r = rand() % 15 + 5;
		food.flag = true;
	}
}

The Bureau failed to interface

This can be directly on the code!

void gameOver()
{
	initgraph(width, heigth);
	loadimage(&img3, "Figure 3.jpg", width, heigth);
	putimage(0, 0, &img3);
}

summary

  1. The above is the general framework and the logic and code of the common mode. In fact, there are still small problems in many places, such as a flash point when eating food, boundary problems are not done well, archives cannot be read, etc. If there is time to optimize later, I will continue to optimize and share better and better versions.

  2. Then, although snake is a simple game, when I write the code I typed for the first time, I still feel full of achievement. In fact, the understanding of C language knowledge is also a good consolidation.

  3. Because the above code is not complete, friends in need can go My github View the complete code on. Hope you like it!!!

Topics: C Game Development