【51单片机】【protues仿真】基于51单片机贪吃蛇游戏系统
目录
一、主要功能
二、使用步骤
三、硬件资源
四、软件设计
五、实验现象
一、主要功能
1、LCD12864液晶显示
2、按键控制移动方向,开始暂停
3、小蛇撞墙、吃到自己游戏结束,自动跳转到等待状态
4、显示得分,LED灯显示对应方向
二、使用步骤
本项目采用STC89C52RC作为主控芯片,搭配LCD12864显示屏,通过独立键盘实现方向控制。使用LCD12864可显示更复杂的游戏界面,包括得分统计和暂停功能,需定义显示缓冲区数组管理像素数据。使用定时器中断(如10ms周期)替代延时函数,避免主循环阻塞导致的屏幕闪烁。方向键检测需配合消抖处理,通常采用定时中断扫描按键状态。同时需判断蛇头是否触碰墙壁(坐标边界)或自身身体坐标。
三、硬件资源
1、51单片机核心模块
2、按键模块
3、蜂鸣器模块
4、LED等模块
5、LCD12864显示模块
四、软件设计
#include <LCD12864.h>
#include <reg52.h>
#include <stdlib.h>
#include <intrins.h>
#include <zifu.h>
uint8 code image1[]=
{0x00,0x00,0x80,0xC0,0xE0,0xE0,0xE0,0xE0,0xE6,0xEE,0xFE,0xFC,0xF8,0xE0,0xE0,0xE0,
0xE0,0xE0,0xF8,0xFC,0xFE,0xEE,0xE6,0xE0,0xE0,0xE0,0xE0,0xC0,0x80,0x00,0x00,0x00,
0x00,0xFF,0xFF,0xFF,0xFF,0x01,0xC1,0xC1,0xE1,0xE1,0xE1,0xE1,0xE1,0x01,0x01,0x01,
0x01,0x01,0xE1,0xE1,0xE1,0xE1,0xE1,0xC1,0xC1,0x01,0xFF,0xFF,0xFF,0xFE,0x00,0x00,
0x00,0xFF,0xFF,0xFF,0xFF,0x00,0x01,0x01,0x01,0x01,0x00,0x10,0x70,0x60,0x70,0x38,
0x60,0x60,0x30,0x00,0x00,0x01,0x01,0x01,0x01,0x00,0xFF,0xFF,0xFF,0xFF,0x00,0x00,
0x00,0x03,0x0F,0x1F,0x1F,0x3E,0x3E,0x7C,0x7C,0x7C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,
0x3C,0x3C,0x3C,0x3C,0x3C,0x7C,0x7C,0x7C,0x3C,0x3E,0x1F,0x1F,0x0F,0x03,0x00,0x00};
uint8 dat;uint16 i,j;
uint8 snake_head[2], snake_tail[2], snake_len; //蛇头蛇尾坐标,蛇身长度
uint8 head_step, tail_step; //蛇头蛇尾步数
uint8 tailtrun_step[20], tailtrun_direction[20]; //蛇尾转向步数&方向循环列表
uint8 rear, front; //蛇尾转向循坏列表指针
uint8 direction, direction_tail; //蛇头蛇尾方向
uint8 rand_x, rand_y; //随机食物坐标
uint8 pause_flag; //暂停标志位
uint8 victory_line; //游戏胜利随机行
uint8 start_line, start_column; //2×2点起始坐标
int bitTo16(uint8);
void start();
void over();
void delay();
void creat_food();
void Key_Scan();
void show_p(uint8,uint8,uint8);
void show_bp(uint8,uint8,uint8);
void out_bp(uint8,uint8,uint8);
void Display();
void snake_forward(uint8);
void snake_lose(uint8);
void InitGame();
void hit_block(uint8,uint8);
void bord();
void defen();
void show_number(uint8,uint8);
void score(uint8);
void pause();
void victory();
void judge_victory();
void hit_wall();
/**************************** 主函数 ****************************/
void main()
{
InitLCD();
ClearScreen(0);
Set_line(0);
InitGame();
while(1)
{
Display();
Key_Scan();
}
}
/**************************** 自定义函数 ****************************/
void InitGame(){
start(); //游戏开始
bord(); //画页面
pause_flag = 0; //暂停标志
victory_line = 3 + rand() % 27; //游戏胜利随机行
direction = 4;direction_tail = 4; //初始蛇头蛇尾方向向右
head_step = 3; tail_step = 1; //初始蛇头步数3,蛇尾步数1
for (i = 0;i < 20;i++) { //循环队列步数&方向初始化
tailtrun_step[i] = 0;
tailtrun_direction[i] = 0;
}
rear=front=0; //循环队列指针初始化
snake_len=3; //初始蛇身长度3
snake_head[0]=5;snake_head[1]=3; //蛇头第一个点
snake_tail[0]=5;snake_tail[1]=1; //蛇尾第一个点
creat_food(); //创建食物
score(3); //初始得分03->长度
}
void delay(){
i = 10000;
while (i--);
}
// 创造食物
void creat_food(){
while(1){
i = rand();
srand(i);
rand_x = 3 + rand() % 27; // 3-29
rand_y = 3 + rand() % 27;
dat = read_LCD_data(rand_x * 2 / 8, rand_y * 2);
if ((dat & bitTo16(rand_x * 2 % 8)) == 0x00) {
show_bp(2, rand_x, rand_y);
break;
}
}
}
// 按键扫描
void Key_Scan(void){
if(KEY1 == 1)
REDL1 = 1;
if(KEY2 == 1)
REDL2 = 1;
if(KEY3 == 1)
REDL3 = 1;
if(KEY4 == 1)
REDL4 = 1;
if (KEY0 == 0)
pause();
else if (KEY1 == 0) {
REDL1 = 0;
if (direction != 2) { //蛇不能直接掉头
direction = 1;
tailtrun_step[rear] = head_step;
tailtrun_direction[rear] = 1;
rear = (rear + 1) % 20;
}
}
else if (KEY2 == 0) {
REDL2 = 0;
if (direction != 1) {
direction = 2;
tailtrun_step[rear] = head_step;
tailtrun_direction[rear] = 2;
rear = (rear + 1) % 20;
}
}
else if (KEY3 == 0) {
REDL3 = 0;
if (direction != 4) {
direction = 3;
tailtrun_step[rear] = head_step;
tailtrun_direction[rear] = 3;
rear = (rear + 1) % 20;
}
}
else if (KEY4 == 0) {
REDL4 = 0;
if (direction != 3) {
direction = 4;
tailtrun_step[rear] = head_step;
tailtrun_direction[rear] = 4;
rear = (rear + 1) % 20;
}
}
}
// 2×2合并为一个点
void show_bp(uint8 screen,uint8 line,uint8 column){
SelectScreen(screen);
start_line = line * 2;start_column = column * 2;
for(j=0;j<2;j++){
dat = read_LCD_data(start_line / 8, start_column + j);
write_LCD_data(dat | bitTo16(start_line % 8));
dat = read_LCD_data(start_line / 8, start_column + j);
write_LCD_data(dat | bitTo16(start_line % 8 + 1));
}
Set_page(start_line / 8); //前面设置完会自动+1,需重新设置回来
Set_column(start_column + 1);
}
void out_bp(uint8 screen,uint8 line,uint8 column){
SelectScreen(screen);
start_line = line * 2;start_column = column * 2;
for(j=0;j<2;j++){
dat = read_LCD_data(start_line / 8, start_column + j);
write_LCD_data(dat & ~( bitTo16(start_line % 8)));
dat = read_LCD_data(start_line / 8, start_column + j);
write_LCD_data(dat & ~( bitTo16(start_line % 8 + 1)));
}
Set_page(start_line / 8); //前面设置完会自动+1,需重新设置回来
Set_column(start_column + 1);
}
void show_p(uint8 screen, uint8 line, uint8 column) {
SelectScreen(screen);
dat = read_LCD_data(line / 8, column);
Set_page(line/8);
Set_column(column);
write_LCD_data(dat | bitTo16(line % 8));
}
// 熄灭一个点,行列
void out_p(uint8 screen, uint8 line, uint8 column) {
SelectScreen(screen);
dat = read_LCD_data(line / 8, column);
Set_page(line/8);
Set_column(column);
write_LCD_data(dat & ~(bitTo16(line % 8)));
}
// 画框
void bord(){
for (i = 0;i < 32;i++) {
show_bp(0, 0, i);
show_bp(0, i, 31);
show_bp(0, 31, 31 - i);
show_bp(0, 31 - i, 0);
}
defen();
}
void hit_block(uint8 line, uint8 column) {
dat = read_LCD_data(line * 2 / 8, column * 2);
Set_page(line * 2 /8);
Set_column(column * 2);
if ((dat & bitTo16(line * 2 % 8)) != 0)
over();
}
// 撞墙
void hit_wall(){
if(snake_head[0] <2)
over();
if(snake_head[0] >29)
over();
if(snake_head[1] <2)
over();
if(snake_head[1] >29)
if(snake_head[0] != victory_line)
over();
}
// 显示
void Display(){
snake_forward(direction);
// 判断是否吃到食物
if ((snake_head[0] == rand_x) && (snake_head[1] == rand_y)) {
score(snake_len + 1);
snake_len++;
creat_food();
}
else {
snake_lose(direction_tail);
}
// 判断蛇尾是否转向
if (tail_step == tailtrun_step[front]) {
direction_tail = tailtrun_direction[front];
tailtrun_direction[front] = 0;
front = (front + 1) % 20;
}
judge_victory();
hit_wall();
}
// 蛇向前一格
void snake_forward(uint8 direction){
switch(direction){
case 1:{
if (((snake_head[0] - 1) != rand_x) && (snake_head[1] != rand_y)) // 如果不是食物再判断是否碰到蛇身
hit_block(snake_head[0] - 1, snake_head[1]);
show_bp(2, --snake_head[0], snake_head[1]); // 点亮前一个点
break;
}
case 2:{
if (((snake_head[0] + 1) != rand_x) && (snake_head[1] != rand_y)) // 如果不是食物再判断是否碰到蛇身
hit_block(snake_head[0] + 1, snake_head[1]);
show_bp(2, ++snake_head[0], snake_head[1]);
break;
}
case 3:{
if ((snake_head[0] != rand_x) && ((snake_head[1] - 1) != rand_y)) // 如果不是食物再判断是否碰到蛇身
hit_block(snake_head[0], snake_head[1] - 1);
show_bp(2, snake_head[0], --snake_head[1]);
break;
}
case 4:{
if ((snake_head[0] != rand_x) && ((snake_head[1] + 1) != rand_y)) // 如果不是食物再判断是否碰到蛇身
hit_block(snake_head[0], snake_head[1] + 1);
show_bp(2, snake_head[0], ++snake_head[1]);
break;
}
}
head_step++;
}
// 蛇消失一格
void snake_lose(uint8 direction_tail){
switch(direction_tail){
case 1:{
out_bp(2, snake_tail[0]--, snake_tail[1]);
break;
}
case 2:{
out_bp(2, snake_tail[0]++, snake_tail[1]);
break;
}
case 3:{
out_bp(2, snake_tail[0], snake_tail[1]--);
break;
}
case 4:{
out_bp(2, snake_tail[0], snake_tail[1]++);
break;
}
}
delay();
tail_step++;
}
// 游戏胜利
void judge_victory() {
if (snake_len >= 17) {
out_bp(2, victory_line, 31);
if (snake_head[1] >= 31)
victory();
}
}
// 暂停函数
void pause() {
pause_flag = 1;
delay();delay();
while (pause_flag == 1) {
if (KEY0 == 0) {
pause_flag = 0;
delay();delay();
}
}
}
// 位转16进制
int bitTo16(uint8 wei){
switch (wei){
case 0:return 0x01;break;
case 1:return 0x02;break;
case 2:return 0x04;break;
case 3:return 0x08;break;
case 4:return 0x10;break;
case 5:return 0x20;break;
case 6:return 0x40;break;
case 7:return 0x80;break;
//default:return 0x00;break;
}
}
// 游戏开始界面
void start(){
BEEP = 1;
show_im(1, 2, 0 * 16, image1);
show_ch(1, 4, 2 * 16, ch + 32 * 0);
show_ch(1, 4, 3 * 16, ch + 32 * 1);
show_ch(2, 4, 0 * 16, ch + 32 * 2);
show_ch(2, 4, 1 * 16, ch + 32 * 3);
show_ch(2, 4, 2 * 16, ch + 32 * 6);
show_ch(2, 4, 3 * 16, ch + 32 * 6);
pause();
ClearScreen(0);
}
// 游戏结束界面
void over(){
BEEP = 0;
ClearScreen(0);
show_im(1, 2, 0 * 16, image1);
show_ch(1, 4, 2 * 16, ch + 32 * 0);
show_ch(1, 4, 3 * 16, ch + 32 * 1);
show_ch(2, 4, 0 * 16, ch + 32 * 4);
show_ch(2, 4, 1 * 16, ch + 32 * 5);
show_ch(2, 4, 2 * 16, ch + 32 * 6);
show_ch(2, 4, 3 * 16, ch + 32 * 6);
pause();InitGame();
}
// 游戏胜利界面
void victory() {
BEEP = 0;
ClearScreen(0);
show_im(1, 2, 0 * 16, image1);
show_ch(1, 4, 2 * 16, ch + 32 * 0);
show_ch(1, 4, 3 * 16, ch + 32 * 1);
show_ch(2, 4, 0 * 16, ch + 32 * 9);
show_ch(2, 4, 1 * 16, ch + 32 * 10);
show_ch(2, 4, 2 * 16, ch + 32 * 6);
show_ch(2, 4, 3 * 16, ch + 32 * 6);
pause();InitGame();
}
// 得分
void defen(){
show_ch(1, 2, 1 * 16, ch + 32 * 7);
show_ch(1, 2, 2 * 16, ch + 32 * 8);
}
// 分数
void show_number(uint8 num, uint8 colume) {
switch(num){
case 0:{
show_num(1, 4, colume, number + 16 * 0);
break;
}
case 1:{
show_num(1, 4, colume, number + 16 * 1);
break;
}
case 2:{
show_num(1, 4, colume, number + 16 * 2);
break;
}
case 3:{
show_num(1, 4, colume, number + 16 * 3);
break;
}
case 4:{
show_num(1, 4, colume, number + 16 * 4);
break;
}
case 5:{
show_num(1, 4, colume, number + 16 * 5);
break;
}
case 6:{
show_num(1, 4, colume, number + 16 * 6);
break;
}
case 7:{
show_num(1, 4, colume, number + 16 * 7);
break;
}
case 8:{
show_num(1, 4, colume, number + 16 * 8);
break;
}
case 9:{
show_num(1, 4, colume, number + 16 * 9);
break;
}
}
}
void score(uint8 score){
uint8 shi_wei, ge_wei;
shi_wei = score / 10;
ge_wei = score % 10;
show_number(shi_wei, 20);
show_number(ge_wei, 32);
}
五、实验现象
演示视频:
【51单片机-B054】【protues仿真】基于51单片机贪吃蛇游戏设计仿真