This article is to use 51 single chip microcomputer and OLED screen to realize a simple Snake game
The 51 single chip microcomputer used is Puzhong 51 series. The OLED screen belongs to zhongjingyuan electronics. The specific objects can be purchased in a treasure
OLED module:
About OLED module correlation function and its application h and c files can use the process code provided by zhongjingyuan e-commerce, which is verified to be reliable
Here are some features of OLED screen:
1, The OLED screen is composed of 128 * 64 pixels, 128 columns long, and 64 wide pixels are divided into eight pages. When the MCU transmits data to it, it transmits eight bits of data at one time, that is, a column in a page. Therefore, I simply regard a total of 64 pixels of 8 * 8 as a snake body of a greedy snake
2, I believe anyone who has used OLED screen will find another feature, that is, when you light up an area, if you don't clear the area and display other images, the previously lit part will continue to be displayed without being covered by new data. The specific phenomenon can be verified by yourself
3, In the library functions provided by zhongjingyuan merchants, there are two functions, one is OLED_Set_Pos(x,y), another is OLED -_ WR_Byte() function. One of these two functions is used to locate the position in the OLED screen, where x is the number of columns and Y is the number of page s. After positioning, the second function is used to transmit image data to the location. Moreover, the OLED screen will automatically transmit data to the next column after transmitting one column, which is very convenient.
Code part:
About the code idea, we use an area of 8 * 8 as a snake body, so the whole screen is divided into 16 * 8. We will display our snake body in this area. Because the display area is small, we can consider no wall or wall. It depends on personal preferences. The most important thing is the movement and lengthening of the snake body.
First, define a structure that can represent the coordinates of the snake body, including the horizontal and vertical coordinates, and then define a structure array and a variable to store the positions of all snake bodies and food
struct snake //A structure for storing the position of the snake body; { unsigned char x; unsigned char y; } ; struct snake Pos[20]; //Snake body coordinate data; struct snake food ;//Coordinate position of food;
Because the storage space we can use in the single chip microcomputer is very limited, the maximum length defined is 20. The problem of 51 memory will also be mentioned in the next snake body lengthening and mobile module. After solving the problem of snake position storage, it is necessary to print the snake on the screen
Snake Print function:
Two practical functions have been mentioned in the OLED module. We can realize snake printing through these two functions
const unsigned char code body[8]={0xFF,0xFF,0xC3,0xC3,0xC3,0xC3,0xFF,0xFF}; void OLED_SnakeBody(unsigned char x, unsigned char y) { u8 i; OLED_Set_Pos(x*8,y); for(i=0;i<8;i++) { OLED_WR_Byte(body[i],OLED_DATA); } }
Where body[8] is used to print the data transmitted by the snake, and the display effect is shown in the following figure:
Food generation function:
Personally, the food generation function is a difficult part of the greedy Snake game, because the random generation of food means to generate a random number, but what kind of number is random? After learning from some big guys' codes, I decided to use the timer to generate two random numbers as the abscissa and ordinate positions of food. (in 51 single chip microcomputer, THX and TLX can be regarded as two constants, but these two constants are self adding and clearing over time.) the specific code is as follows:
void Food_Init() //Food initialization function; { u8 i; while(!foodflag) { food.x=count%16; food.y=count%8; for(i=0;i<head;i++) { if((food.x==Pos[i].x&&food.y==Pos[i].y)||food.x>=15||food.y>=7||food.x<=1||food.y<=1)//Once this condition is met, the food //Data should be discarded because it is on the snake or on the map; giveup=1; } if(giveup==1) { foodflag=0; giveup=0; } else { foodflag=1; OLED_SnakeBody(food.x,food.y); giveup=0; break; } } }
There is a flag bit: foodflag. This flag bit is used to mark the food state. When the food is eaten, that is, when the foodflag is 0, it will enter the cycle and always calculate the count, Until a suitable food coordinate is taken out (I put the count in the timer for self addition and reset it on time). However, there is a defect in this food generation method, that is, after you eat the food, there may be a short delay before generating the food, so other methods can be considered to generate random food. In short, different people have different opinions.
Press the independent key to change the moving direction of the snake:
In the process of snake moving, you need to constantly change the direction, and 51 single chip microcomputer has four independent keys. Although the position is not friendly for the game, it can reduce the code complexity, so it is recommended to use independent keys instead of matrix keyboard. Here, I introduce a variable mode to represent the movement direction of the snake, 1 left, 2 right, 3 up and 4 down, and change the value of mode through independent keys. When changing the direction, it should be noted that when the snake moves to the left, it can not directly right, nor can it move to the left, but can only go up or down, as is the case in other cases.
void leftkey() { if(left==0&&mode!=1&&mode!=2) delay(25); if(left==0&&mode!=1&&mode!=2) mode=1; while(!left); } void rightkey() { if(right==0&&mode!=1&&mode!=2) delay(25); if(right==0&&mode!=1&&mode!=2) mode=2; while(!right); } void upkey() { if(up==0&&mode!=3&&mode!=4) delay(25); if(up==0&&mode!=3&&mode!=4) mode=3; while(!up); } void downkey() { if(down==0&&mode!=3&&mode!=4) delay(25); if(down==0&&mode!=3&&mode!=4) mode=4; while(!down) ; } void keypros() { leftkey(); rightkey(); upkey(); downkey(); }
The following is the core part of the whole program. Of course, it is also a relatively simple part in my opinion:
Snake movement function:
In the above section, I have mentioned the feature of an OLED screen, that is, the parts that have not been cleared will continue to be displayed, that is, every time the snake moves, we just need to clear the snake tail and update the snake head, and the middle body node can remain unchanged. What should I do after eating the food? It's better. Even the snake tail doesn't have to be removed. Just update the head. However, when updating the position every time, you should pay attention to changing the coordinates of each snake in the array (pay attention to the moving direction of the data). After eating the food, you also need to lengthen the snake, that is, length + +.
As shown in the specific code, only the left shift status function is provided, others are similar:
void modeleft() { u8 i,j; if(mode==1) { //First, judge whether to hit the wall; if(Pos[head-1].x==0) page=2; //Then judge whether you bite your body; if(page==1) for(i=0;i<head-1;i++) //Here, we should pay attention not to squeeze the head coordinates, so as to save some time; { if((Pos[head-1].x-1)==Pos[i].x&&Pos[head-1].y==Pos[i].y) page=2; } //If you don't hit the wall or bite yourself, consider eating food and not eating; if(page==1) { //Eating food; if((Pos[head-1].x-1)==food.x&&Pos[head-1].y==food.y) { head++;//Length plus one; foodflag=0;//Refresh the next food; Pos[head-1].x=Pos[head-2].x-1; //After changing the head, assign the value after calculating the position of the previous head; Pos[head-1].y=Pos[head-2].y; OLED_SnakeBody(Pos[head-1].x,Pos[head-1].y);//Light up a new dog's head and it's over; } else //No food‘ { OLED_CLR_Body(Pos[tail].x,Pos[tail].y);//Cut off the old tail; for(j=0;j<head-1;j++) { Pos[j].x=Pos[j+1].x; Pos[j].y=Pos[j+1].y; } Pos[head-1].x--; OLED_SnakeBody(Pos[head-1].x,Pos[head-1].y);//Light up the new dog head; Don't light up the dog head first, otherwise it will cause data loss; } } } }
At this point, all the important function parts have been completed.
The rest are snake initialization function, game end function, restart function, etc., but it should be noted that if there is a restart function, it is not only necessary to reinitialize the snake, but also reset important variables and flag bits such as snake length, otherwise it is waiting to fix the bug.
Of course, there are many cumbersome steps and functions in this program, which is purely due to the limited level of bloggers. If there are typos in the article... Scold me (Chinese skills are not good).
For your reference, let's release all the codes:
#include"reg51.h" #include"oled.h" sbit left=P3^1; sbit right=P3^0; sbit up=P3^2; sbit down=P3^3;//Key definition; bit moveflag=0,foodflag=0,giveup=0,start=0,fun=0; //giveup is the food discarded flag bit, foodflag is the food refresh flag bit, and moveflag is the mobile flag bit; u8 yanshi=60,count=0,page=0,mode=0,time=150,point=1;//Page is used to display different pages, such as game end page, start page, etc; mode is used to identify the motion state of the snake; //time is divided into two grades, one is 150 and the other is 90; struct snake //A structure for storing the position of the snake body; { unsigned char x; unsigned char y; } ; u8 head=3,tail=0;//Snake length; struct snake Pos[20]; //Snake body coordinate data; struct snake food ;//Coordinate position of food; void TimerInit() { TMOD=0x01; TH0=0xec; TL0=0x77; //10 ms timing; // It is still necessary to change the timing initial value; Select 4 milliseconds; ET0=1; EA=1; TR0=0; //Open only when page2; } void delay(u8 i) { while(i--); } void ShowTen() { OLED_ShowChar(0,3,'T'); } void Game_Restart() { if(left==0) delay(25); if(left==0) { page=0; OLED_Clear(); } while(!left); } void Snake_Over()//The snake death function displays game over after death, and constantly detects whether the reset key is pressed; { if(page==2) { mode=0;//Must be cleared, otherwise it will cause the snake to move disorderly at the beginning of the next game; TR0=0;//Turn off the timer; OLED_Clear();//Clear the screen first; while(page==2) { OLED_ShowString(0,0,"game over"); OLED_ShowString(30,3,"press left to restart"); Game_Restart(); } } } void Food_Init() //Food initialization function; { u8 i; while(!foodflag) { food.x=count%16; food.y=count%8; for(i=0;i<head;i++) { if((food.x==Pos[i].x&&food.y==Pos[i].y)||food.x>=15||food.y>=7||food.x<=1||food.y<=1)//Once this condition is met, the food //Data should be discarded because it is on the snake or on the map; giveup=1; } if(giveup==1) { foodflag=0; giveup=0; } else { foodflag=1; OLED_SnakeBody(food.x,food.y); giveup=0; break; } } } void Snake_Init() //Snake initialization function; { u8 i; Pos[0].x=6; Pos[0].y=3; Pos[1].x=7; Pos[1].y=3; Pos[2].x=8; Pos[2].y=3; for(i=0;i<head;i++) { OLED_SnakeBody(Pos[i].x,Pos[i].y); } } void leftkey() { if(left==0&&mode!=1&&mode!=2) delay(25); if(left==0&&mode!=1&&mode!=2) mode=1; while(!left); } void rightkey() { if(right==0&&mode!=1&&mode!=2) delay(25); if(right==0&&mode!=1&&mode!=2) mode=2; while(!right); } void upkey() { if(up==0&&mode!=3&&mode!=4) delay(25); if(up==0&&mode!=3&&mode!=4) mode=3; while(!up); } void downkey() { if(down==0&&mode!=3&&mode!=4) delay(25); if(down==0&&mode!=3&&mode!=4) mode=4; while(!down) ; } void keypros() { leftkey(); rightkey(); upkey(); downkey(); } void modeleft() { u8 i,j; if(mode==1) { //First, judge whether to hit the wall; if(Pos[head-1].x==0) page=2; //Then judge whether you bite your body; if(page==1) for(i=0;i<head-1;i++) //Here, we should pay attention not to squeeze the head coordinates, so as to save some time; { if((Pos[head-1].x-1)==Pos[i].x&&Pos[head-1].y==Pos[i].y) page=2; } //If you don't hit the wall or bite yourself, consider eating food and not eating; if(page==1) { //Eating food; if((Pos[head-1].x-1)==food.x&&Pos[head-1].y==food.y) { head++;//Length plus one; foodflag=0;//Refresh the next food; Pos[head-1].x=Pos[head-2].x-1; //After changing the head, assign the value after calculating the position of the previous head; Pos[head-1].y=Pos[head-2].y; OLED_SnakeBody(Pos[head-1].x,Pos[head-1].y);//Light up a new dog's head and it's over; } else //No food‘ { OLED_CLR_Body(Pos[tail].x,Pos[tail].y);//Cut off the old tail; for(j=0;j<head-1;j++) { Pos[j].x=Pos[j+1].x; Pos[j].y=Pos[j+1].y; } Pos[head-1].x--; OLED_SnakeBody(Pos[head-1].x,Pos[head-1].y);//Light up the new dog head; Don't light up the dog head first, otherwise it will cause data loss; } } } } void moderight() { u8 i,j; if(mode==2) { //First, judge whether to hit the wall; if(Pos[head-1].x==15) page=2; //Then judge whether you bite your body; if(page==1) for(i=0;i<head-1;i++) //Here, we should pay attention not to squeeze the head coordinates, so as to save some time; { if((Pos[head-1].x+1)==Pos[i].x&&Pos[head-1].y==Pos[i].y) page=2; } //If you don't hit the wall or bite yourself, consider eating food and not eating; if(page==1) { //Eating food; if((Pos[head-1].x+1)==food.x&&Pos[head-1].y==food.y) { head++;//Length plus one; foodflag=0;//Refresh the next food; Pos[head-1].x=Pos[head-2].x+1; //After changing the head, assign the value after calculating the position of the previous head; Pos[head-1].y=Pos[head-2].y; OLED_SnakeBody(Pos[head-1].x,Pos[head-1].y);//Light up a new dog's head and it's over; } else //No food‘ { OLED_CLR_Body(Pos[tail].x,Pos[tail].y);//Cut off the old tail; for(j=0;j<head-1;j++) { Pos[j].x=Pos[j+1].x; Pos[j].y=Pos[j+1].y; } Pos[head-1].x++; OLED_SnakeBody(Pos[head-1].x,Pos[head-1].y);//Light up the new dog head; Don't light up the dog head first, otherwise it will cause data loss; } } } } void modeup() { u8 i,j; if(mode==3) { //First, judge whether to hit the wall; if(Pos[head-1].y==0) page=2; //Then judge whether you bite your body; if(page==1) for(i=0;i<head-1;i++) //Here, we should pay attention not to squeeze the head coordinates, so as to save some time; { if((Pos[head-1].x)==Pos[i].x&&Pos[head-1].y-1==Pos[i].y) page=2; } //If you don't hit the wall or bite yourself, consider eating food and not eating; if(page==1) { //Eating food; if(Pos[head-1].x==food.x&&(Pos[head-1].y-1)==food.y) { head++;//Length plus one; foodflag=0;//Refresh the next food; Pos[head-1].x=Pos[head-2].x; //After changing the head, assign the position of the previous head to the new head after operation; Pos[head-1].y=Pos[head-2].y-1; OLED_SnakeBody(Pos[head-1].x,Pos[head-1].y);//Light up a new dog's head and it's over; } else //No food‘ { OLED_CLR_Body(Pos[tail].x,Pos[tail].y);//Cut off the old tail; for(j=0;j<head-1;j++) { Pos[j].x=Pos[j+1].x; Pos[j].y=Pos[j+1].y; } Pos[head-1].y--; OLED_SnakeBody(Pos[head-1].x,Pos[head-1].y);//Light up the new dog head; Don't light up the dog head first, otherwise it will cause data loss; } } } } void modedown() { u8 i,j; if(mode==4) { //First, judge whether to hit the wall; if(Pos[head-1].y==7) page=2; //Then judge whether you bite your body; if(page==1) for(i=0;i<head-1;i++) //Here, we should pay attention not to squeeze the head coordinates, so as to save some time; { if((Pos[head-1].x)==Pos[i].x&&Pos[head-1].y+1==Pos[i].y) page=2; } //If you don't hit the wall or bite yourself, consider eating food and not eating; if(page==1) { //Eating food; if(Pos[head-1].x==food.x&&(Pos[head-1].y+1)==food.y) { head++;//Length plus one; foodflag=0;//Refresh the next food; Pos[head-1].x=Pos[head-2].x; //After changing the head, assign the position of the previous head to the new head after operation; Pos[head-1].y=Pos[head-2].y+1; OLED_SnakeBody(Pos[head-1].x,Pos[head-1].y);//Light up a new dog's head and it's over; } else //No food‘ { OLED_CLR_Body(Pos[tail].x,Pos[tail].y);//Cut off the old tail; for(j=0;j<head-1;j++) { Pos[j].x=Pos[j+1].x; Pos[j].y=Pos[j+1].y; } Pos[head-1].y++; OLED_SnakeBody(Pos[head-1].x,Pos[head-1].y);//Light up the new dog head; Don't light up the dog head first, otherwise it will cause data loss; } } } } void choice() { if(left==0) { delay(20); if(left==0) { point++; if(point>3) point=1; } while(!left); } if(right==0) { delay(20); if(right==0) { if(point==1) { time=90; page=1; start=1; foodflag=0; } if(point==2) { time=150; page=1; //OLED_ShowString(0,0,"150"); start=1; foodflag=0; } if(point==3) fun=1; } while(!right); } } void modepros() { modeup(); modeleft(); modedown(); moderight(); } void page0() { OLED_ShowString(10,0,"greedy snake"); OLED_ShowString(20,2,"difficult"); OLED_ShowString(20,4,"normal"); OLED_ShowString(20,6,"easy") ; if(point==1) { OLED_ShowChar(10,2,'>'); OLED_ShowChar(10,4,' '); OLED_ShowChar(10,6,' '); } if(point==2) { OLED_ShowChar(10,2,' '); OLED_ShowChar(10,4,'>'); OLED_ShowChar(10,6,' '); } if(point==3) { OLED_ShowChar(10,3,' '); OLED_ShowChar(10,4,' '); OLED_ShowChar(10,6,'>'); } if(fun==1) { OLED_Clear(); OLED_ShowString(0,4,"you really have face"); delay(100000); OLED_Clear(); fun=0; } choice(); } void main() { TimerInit(); OLED_Init(); OLED_Clear(); ShowTen(); while(yanshi) { delay(30000); yanshi--; } OLED_Clear(); while(1) { while(page==0) { page0(); } while(page==1) { if(start==1) { OLED_Clear(); TR0=1; head=3; Snake_Init(); start=0; } Food_Init(); keypros(); } while(page==2) { TR0=0; Snake_Over(); } } } void Timer0() interrupt 1 { TH0=0xec; TL0=0x77; //4ms count++; //OLED_ShowNum(0,0,count,3,16); if(count==time) { count=0; modepros(); } }
Due to the later addition of other functions. Some functions can be ignored directly and only refer to valuable code segments.
The level of bloggers is limited and they are still learning and charging. If there are some mistakes, mistakes, incomplete expressions and other shortcomings, please forgive and give advice.
Thank you for your support!!!