潘多拉stm32L475实现简易贪吃蛇游戏

 一.前言

1.使用到的屏幕是TFT屏,如果是OLED屏需要改一下显示的接口

2.上下左右四个按键按照自己的实际硬件配置就行

3.我是在rt-thread例程上改的,部分延时函数等需要另外实现

二.完整代码

#include <rtdevice.h>

#include <board.h>

#include <drv_lcd.h>

#include <rttlogo.h>

#include "stm32l4xx_hal.h"

#include <stdlib.h>

#include <time.h>

#include <rtdbg.h>


#ifndef bool

    #define bool _Bool

    #define true 1

    #define false 0

#endif

// 假设的Direction枚举定义

typedef enum {

    Direction_Up,

    Direction_Down,

    Direction_Left,

    Direction_Right,

} Direction;


Direction dir = Direction_Right;


// 屏幕尺寸

#define SCREEN_WIDTH  240

#define SCREEN_HEIGHT 240

// 蛇的尺寸

#define SNAKE_SIZE 10

// 更新间隔时间

#define DELAY_MS 100

#define MAX_SNAKE_LENGTH 100 // 假设蛇的最大长度

typedef struct {

    int x;

    int y;

} Point;

// 初始化蛇体数组

Point snakeBody[MAX_SNAKE_LENGTH];

int snakeLength = 0; // 记录蛇的实际长度


Point generateRandomFoodPosition(const Point snakeBody[], int length);

bool isPositionInSnake(const Point *position, const Point snakeBody[], int length);

void initSnake(Point snakeBody[], int* snakeLength);

void resetSnake(Point snakeBody[], int* snakeLength, Direction* dir);

int updateSnake(Point snakeBody[], int* snakeLength, Direction dir, Point* food, bool* hasEaten);


//3x3像素合并的绘制函数

void lcd_draw_super_pixel(int x, int y, uint16_t color) {

    // 计算超像素左上角的实际像素坐标

    int superPixelX = x * 3;

    int superPixelY = y * 3;


    // 填充3x3像素块

    for (int i = 0; i < 3; i++) {

        for (int j = 0; j < 3; j++) {

            lcd_draw_point(superPixelX + i, superPixelY + j, color);

        }

    }

}

// 生成随机食物位置函数需要修改为接受最大长度和当前蛇体数组

// 生成不在蛇身上的随机食物坐标

Point generateRandomFoodPosition(const Point snakeBody[], int length){


    srand((unsigned int)rt_tick_get());  

    Point foodPosition;

    do {

        foodPosition.x = rand() % SCREEN_WIDTH;

        foodPosition.y = rand() % SCREEN_HEIGHT;

    } while (isPositionInSnake(&foodPosition, snakeBody, snakeLength));

    return foodPosition;

}


// 检查给定坐标是否在蛇身中

bool isPositionInSnake(const Point *position, const Point snakeBody[], int length) {

    for (int i = 0; i < length; i++) {

        if (snakeBody[i].x == position->x && snakeBody[i].y == position->y) {

            return true;

        }

    }

    return false;

}


// 初始化蛇

void initSnake(Point snakeBody[], int* snakeLength)

{

    *snakeLength = SNAKE_SIZE;

    for(int i = 0; i < SNAKE_SIZE; ++i)

    {

        snakeBody[i].x = 120+i;

        snakeBody[i].y = 120;

    }

}


// 重置蛇状态

void resetSnake(Point snakeBody[], int* snakeLength, Direction* dir) {

    *snakeLength = SNAKE_SIZE; // 重置蛇长度

    *dir = Direction_Right; // 或其他默认方向

    // 无需free,因为snakeBody是静态分配的

    // 重新调用initSnake初始化蛇的位置

    initSnake(snakeBody, snakeLength);

}


// 更新蛇的位置,并处理增长逻辑

int updateSnake(Point snakeBody[], int* snakeLength, Direction dir, Point* food, bool* hasEaten)

{

    Point lodHead = snakeBody[*snakeLength - 1];

    // 移动蛇的每一个部分,从尾巴开始向前移动

    for(int i = *snakeLength - 1; i > 0; --i)

    {

        snakeBody[i] = snakeBody[i - 1]; // 每个节点变为前一个节点的位置

    }

    // 移动蛇头

    switch (dir)

    {

        case Direction_Up: snakeBody[0].y--; break;

        case Direction_Down: snakeBody[0].y++; break;

        case Direction_Left: snakeBody[0].x--; break;

        case Direction_Right: snakeBody[0].x++; break;

    }


    // 检查蛇头是否撞墙

    if (snakeBody[0].x < 0 || snakeBody[0].x >= SCREEN_WIDTH || snakeBody[0].y < 0 || snakeBody[0].y >= SCREEN_HEIGHT) {

        return -1; // 表示游戏结束

    }


    // 检查蛇是否撞到自己(除了头部)

    for(int i = 1; i < *snakeLength; ++i) {

        if (snakeBody[i].x == snakeBody[0].x && snakeBody[i].y == snakeBody[0].y) {

            return -1; // 表示游戏结束

        }

    }


    // 检查是否吃到食物

    if (snakeBody[0].x == food->x && snakeBody[0].y == food->y)

    {

        snakeBody[*snakeLength] = lodHead;

        *snakeLength++;

        *hasEaten = true; // 设置吃到食物标志

        return 1; // 返回特殊状态码表示吃到食物

    }

    return 0; // 更新成功

}

// 使用超像素渲染蛇到LCD

void renderSnake(const Point* snakeBody, int snakeLength) {

    // 清除屏幕

    lcd_clear(BLACK);

    // 绘制蛇

    for(int i = 0; i < snakeLength; ++i) {

        lcd_draw_super_pixel(snakeBody[i].x, snakeBody[i].y, GREEN);

    }


    // 绘制蛇头,可以考虑使用不同的颜色或样式以突出

    lcd_draw_super_pixel(snakeBody[0].x, snakeBody[0].y, RED);

}

// 食物的绘制也需要更新为使用超像素

void renderFood(const Point* food) {

    lcd_draw_super_pixel(food->x, food->y, YELLOW);

}


// 检查按键并改变方向

void checkInput(Direction* dir, int key)

{

    // 防止蛇立即反向移动的逻辑

    switch(key) {

        case 2: // 左

            if(*dir != Direction_Right) *dir = Direction_Left;

            break;

        case 1: // 下

            if(*dir != Direction_Up) *dir = Direction_Down;

            break;

        case 0: // 右

            if(*dir != Direction_Left) *dir = Direction_Right;

            break;

        case 3: // 上

            if(*dir != Direction_Down) *dir = Direction_Up;

            break;

        default:

            break;

    }

}


//按键初始化函数

void key_init(void) {

    rt_pin_mode(PIN_KEY0, PIN_MODE_INPUT);

    rt_pin_mode(PIN_KEY1, PIN_MODE_INPUT);

    rt_pin_mode(PIN_KEY2, PIN_MODE_INPUT);

    rt_pin_mode(PIN_WK_UP, PIN_MODE_INPUT);

}

//按键扫描函数

rt_int16_t key_scan(void) {

    if (rt_pin_read(PIN_KEY0) == PIN_LOW) {

        rt_thread_mdelay(20);

        if (rt_pin_read(PIN_KEY0) == PIN_LOW) {

            return 0;

        }

    } else if (rt_pin_read(PIN_KEY1) == PIN_LOW) {

        rt_thread_mdelay(20);

        if (rt_pin_read(PIN_KEY1) == PIN_LOW) {

            return 1;

        }

    } else if (rt_pin_read(PIN_KEY2) == PIN_LOW) {

        rt_thread_mdelay(20);

        if(rt_pin_read(PIN_KEY2) == PIN_LOW) {

            return 2;

        }

    } else if (rt_pin_read(PIN_WK_UP) == PIN_HIGH) {

        rt_thread_mdelay(20);

        if (rt_pin_read(PIN_WK_UP) == PIN_HIGH) {

            return 3;

        }

    }

    return -RT_ERROR;

}


// 主函数

int main(void)

{

    rt_int16_t key;

    Point food;

    int i;

    int updateResult;

      bool hasEaten = false;

    /* 清屏 */

    lcd_clear(WHITE);

    lcd_show_image(0, 0, 240, 69, image_rttlogo);

    lcd_set_color(WHITE, BLACK);

    key_init();

    initSnake(snakeBody, &snakeLength);  

    food = generateRandomFoodPosition(snakeBody, snakeLength);

    rt_thread_mdelay(3000);

    while (1)

    {

        key = key_scan();

        if(key!=-1)

        {

            checkInput(&dir, key);

            LOG_E("key %d", key);

        }

        updateResult = updateSnake(snakeBody, &snakeLength, dir, &food, &hasEaten);

        if(updateResult != 0 && updateResult != -1)

        { // 判断是否吃到食物

            //确保新食物位置不在蛇身上  

            do{

                food = generateRandomFoodPosition(snakeBody, snakeLength);

            }while(isPositionInSnake(&food, snakeBody, snakeLength));

        } else if (updateResult == -1) {

            // 处理游戏结束逻辑

            resetSnake(snakeBody, &snakeLength, &dir);

            food = generateRandomFoodPosition(snakeBody, snakeLength); // 重置游戏时也生成新食物

            hasEaten = false;

        }

        // 渲染蛇到LCD

        renderSnake(snakeBody, snakeLength);

        //生成食物

        renderFood(food);        

        rt_thread_mdelay(DELAY_MS);

     }

}

三.显示

单个像素点太小,所以用了3*3的方案

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值