我们在上一篇已经可以通过按键控制蛇移动,我们在此基础上增加产生食物及撞墙及头身体相撞则触发gameover 的逻辑并显示gameove 画面。
之前过程贴连接如下:
Let'sDo第2期任务+《贪吃蛇》过程1+贪吃蛇按键采集、
Let'sDo第2期任务+《贪吃蛇》过程2+PIM580适配LVGL
Let'sDo第2期任务+《贪吃蛇》过程3+按键控制贪吃蛇移动
在此基础上添加控制算法:
#include <rtthread.h> #include <stdio.h> #include <stdlib.h> #include "snake.h" #include <hardware/structs/systick.h> static rt_list_t *spriteSnake; // 链表头(蛇头) snake_game_t game_snake; static uint32_t my_rand(void) { return systick_hw->cvr; } /* * 函数名: static link_snake* lv_100ask_stm32_game_snake_initLink(void) * 输入参数: 无 * 返回值: 初始化后的链表 * 函数作用: 初始化链表(蛇) */ static void game_snake_initLink(void) { /* init snake body list */ static rt_list_t snake_body = RT_LIST_OBJECT_INIT(snake_body); spriteSnake = &snake_body; for (int i = 0; i < GAME_SNAKE_INIT_LINE; i++) { link_snake *body = (link_snake*)malloc(sizeof(link_snake)); body->x = i; body->y = i; rt_list_insert_before(spriteSnake,&body->node); } } static void gen_food(void) { rt_list_t* head = NULL; rt_list_t* pos = NULL; link_snake * snake_body; head = spriteSnake; rt_list_t* frist = NULL; uint16_t i = 0,len = 0; len = rt_list_len(head); while((i != len)) { i = 0; game_snake.food_x = (my_rand()%(GAME_LV_HOR_RES)); game_snake.food_y = (my_rand()%(GAME_LV_VER_RES)); game_snake.food_x -= game_snake.food_x%GAME_SNAKE_SIZE; game_snake.food_y -= game_snake.food_y%GAME_SNAKE_SIZE; rt_list_for_each(pos,head) { i++; snake_body = rt_container_of(pos,link_snake,node); if((abs(game_snake.food_x - snake_body->x) <= GAME_SNAKE_SIZE)&&\ (abs(game_snake.food_y - snake_body->y) <= GAME_SNAKE_SIZE)) { break; } } } LCD_draw_block(game_snake.food_x,game_snake.food_y,GAME_SNAKE_SIZE,GAME_SNAKE_SIZE,GAME_FOOD_COLOR); } /* * 函数名: static void lv_100ask_stm32_game_snake_init(void) * 输入参数: 无 * 返回值: 无 * 函数作用: 应用界面初始化 */ void game_snake_init(void) { /* init game status */ game_snake.gesture = SNAKE_MOVE_UP; game_snake.status = GAME_STATUS_START; game_snake_initLink(); rt_list_t* head = NULL; rt_list_t* pos = NULL; link_snake * snake_body; head = spriteSnake; //将temp指针重新指向头结点 int i = 0; uint16_t init_x = (my_rand()%(GAME_LV_HOR_RES - (GAME_SNAKE_INIT_LINE * GAME_SNAKE_SIZE))); uint16_t init_y = (my_rand()%(GAME_LV_VER_RES - GAME_SNAKE_SIZE)); init_x -= init_x%GAME_SNAKE_SIZE; init_y -= init_y%GAME_SNAKE_SIZE; rt_list_for_each(pos,head) { snake_body = rt_container_of(pos,link_snake,node); if(i == 0) { /* draw head to lcd */ LCD_draw_block(init_x,init_y,GAME_SNAKE_SIZE,GAME_SNAKE_SIZE,GAME_SNAKE_HEAD_COLOR); } else { LCD_draw_block(init_x,init_y,GAME_SNAKE_SIZE,GAME_SNAKE_SIZE,GAME_BODY_COLOR); } snake_body->x = init_x; snake_body->y = init_y; init_x = init_x+GAME_SNAKE_SIZE; i++; } /* init food */ gen_food(); } void game_snake_move(void) { rt_list_t* head = NULL; rt_list_t* frist = NULL; rt_list_t* pos = NULL; rt_list_t* last = NULL; link_snake * snake_body_last; link_snake * snake_body_head; head = spriteSnake; /* update snake body */ frist = spriteSnake->next; last = spriteSnake->prev; /* clear last body cloor */ snake_body_last = rt_container_of(last,link_snake,node); LCD_draw_block(snake_body_last->x,snake_body_last->y,GAME_SNAKE_SIZE,GAME_SNAKE_SIZE,GLCD_COLOR_CYAN); snake_body_head = rt_container_of(frist,link_snake,node); /* move head postion to laster */ snake_body_last->x = snake_body_head->x; snake_body_last->y = snake_body_head->y; LCD_draw_block(snake_body_head->x,snake_body_head->y,GAME_SNAKE_SIZE,GAME_SNAKE_SIZE,GAME_BODY_COLOR); switch(game_snake.gesture) { case SNAKE_MOVE_UP: snake_body_head->y -= GAME_SNAKE_SIZE; break; case SNAKE_MOVE_LEFT: snake_body_head->x -= GAME_SNAKE_SIZE; break; case SNAKE_MOVE_DOWN: snake_body_head->y += GAME_SNAKE_SIZE; break; case SNAKE_MOVE_RIGHT: snake_body_head->x += GAME_SNAKE_SIZE; break; } LCD_draw_block(snake_body_head->x,snake_body_head->y,GAME_SNAKE_SIZE,GAME_SNAKE_SIZE,GAME_SNAKE_HEAD_COLOR); rt_list_remove(&snake_body_last->node); rt_list_insert_after(&snake_body_head->node,&snake_body_last->node); } uint8_t game_snake_eat_food(void) { /* get head pos */ rt_list_t* head = NULL; link_snake *body; head = spriteSnake; link_snake * snake_body_head = rt_container_of( head->next,link_snake,node); //rt_kprintf("food %d %d head %d %d\n",game_snake.food_x,game_snake.food_y,snake_body_head->x,snake_body_head->y); /* check head pos and food pos */ /* left */ if((game_snake.gesture == SNAKE_MOVE_LEFT) && (snake_body_head->y == game_snake.food_y) && (snake_body_head->x == game_snake.food_x + GAME_SNAKE_SIZE)) { goto eat_food; } /* right */ if((game_snake.gesture == SNAKE_MOVE_RIGHT) && (snake_body_head->y == game_snake.food_y) && (snake_body_head->x == game_snake.food_x - GAME_SNAKE_SIZE)) { goto eat_food; } /* up */ if((game_snake.gesture == SNAKE_MOVE_UP) && (snake_body_head->x== game_snake.food_x) && (snake_body_head->y == game_snake.food_y + GAME_SNAKE_SIZE)) { goto eat_food; } /* down */ if((game_snake.gesture == SNAKE_MOVE_DOWN) && (snake_body_head->x== game_snake.food_x) && (snake_body_head->y == game_snake.food_y - GAME_SNAKE_SIZE)) { goto eat_food; } return 0; eat_food: rt_kprintf("eate food \n"); /* create snake body list */ body = (link_snake*)malloc(sizeof(link_snake)); body->x = snake_body_head->x; body->y = snake_body_head->y; rt_list_insert_after(&snake_body_head->node,&body->node); /* update head pos */ snake_body_head->x = game_snake.food_x; snake_body_head->y = game_snake.food_y; /* draw snake */ LCD_draw_block(body->x,body->y,GAME_SNAKE_SIZE,GAME_SNAKE_SIZE,GAME_BODY_COLOR); LCD_draw_block(snake_body_head->x,snake_body_head->y,GAME_SNAKE_SIZE,GAME_SNAKE_SIZE,GAME_SNAKE_HEAD_COLOR); /* gen new food */ gen_food(); return 1; } void snake_gameover_check(void) { /* get head pos */ rt_list_t* head = NULL; rt_list_t* pos = NULL; link_snake *snake_body; head = spriteSnake; uint16_t x,y; uint16_t i = 0; link_snake * snake_body_head = rt_container_of( head->next,link_snake,node); if(game_snake.status != GAME_STATUS_START) return; /* touch wall check */ if((game_snake.gesture == SNAKE_MOVE_UP) && (snake_body_head->y == 0)) game_snake.status = GAME_STATUS_SHOW_OVER; if((game_snake.gesture == SNAKE_MOVE_DOWN) && (snake_body_head->y == GAME_LV_VER_RES - GAME_SNAKE_SIZE)) game_snake.status = GAME_STATUS_SHOW_OVER; if((game_snake.gesture == SNAKE_MOVE_LEFT) && (snake_body_head->x == 0)) game_snake.status = GAME_STATUS_SHOW_OVER; if((game_snake.gesture == SNAKE_MOVE_RIGHT) && (snake_body_head->x == GAME_LV_HOR_RES - GAME_SNAKE_SIZE)) game_snake.status = GAME_STATUS_SHOW_OVER; /* touch body check */ if(game_snake.gesture == SNAKE_MOVE_UP) { x = snake_body_head->x; y = snake_body_head->y - GAME_SNAKE_SIZE; } if(game_snake.gesture == SNAKE_MOVE_DOWN) { x = snake_body_head->x; y = snake_body_head->y + GAME_SNAKE_SIZE; } if(game_snake.gesture == SNAKE_MOVE_LEFT) { x = snake_body_head->x - GAME_SNAKE_SIZE; y = snake_body_head->y; } if(game_snake.gesture == SNAKE_MOVE_RIGHT) { x = snake_body_head->x + GAME_SNAKE_SIZE; y = snake_body_head->y; } rt_list_for_each(pos,head) { snake_body = rt_container_of(pos,link_snake,node); if((i > 0) && (x == snake_body->x) && (y == snake_body->y)) { game_snake.status = GAME_STATUS_SHOW_OVER; } i++; } } void game_snake_start(void) { if( 0 == game_snake_eat_food()) { game_snake_move(); } } extern const char gameover_320_240[153600]; void game_snake_showover(void) { rt_kprintf("snake game over \n"); LCD_DrawBitmap(0,0,320,240,gameover_320_240); game_snake.status = GAME_STATUS_IDLE; } void snake_game(void) { snake_gameover_check(); switch(game_snake.status) { case GAME_STATUS_START: game_snake_start(); break; case GAME_STATUS_SHOW_OVER: game_snake_showover(); break; case GAME_STATUS_IDLE: break; default: break; } } void set_move_dir(uint8_t dir) { if((game_snake.gesture == SNAKE_MOVE_UP) && (dir == SNAKE_MOVE_DOWN)) { return; } if((game_snake.gesture == SNAKE_MOVE_DOWN) && (dir == SNAKE_MOVE_UP)) { return; } if((game_snake.gesture == SNAKE_MOVE_RIGHT) && (dir == SNAKE_MOVE_LEFT)) { return; } if((game_snake.gesture == SNAKE_MOVE_LEFT) && (dir == SNAKE_MOVE_RIGHT)) { return; } game_snake.gesture = dir; } #ifdef RT_USING_FINSH #include <string.h> #include <stdlib.h> #include <ctype.h> #include <finsh.h> #include <msh_parse.h> static void __snake(int argc, char *argv[]) { rt_list_t* head = NULL; rt_list_t* pos = NULL; link_snake * snake_body; head = spriteSnake; //将temp指针重新指向头结点 rt_kprintf("food %d %d \n", game_snake.food_x, game_snake.food_y); rt_list_for_each(pos,head) { snake_body = rt_container_of(pos,link_snake,node); rt_kprintf("body %d %d \n", snake_body->x, snake_body->y); } } MSH_CMD_EXPORT_ALIAS(__snake, snake, snake [option]); #endif /* end of RT_USING_FINSH */
初版运行效果:
在基本功能的基础上更新显示素材,添加游戏开始结束画面,及产生多个和食物及添加障碍物的逻辑。
添加分数显示
添加暂停控制,并在底部显示游戏运行状态
添加flash 存储游戏记录显示
视频链接:
https://www.bilibili.com/video/BV1czWseoEdn/?vd_source=8c50c870cce93f7cea3a8fcc81260ac1