酸是什么意思| 白事随礼钱有什么讲究| 伊朗用什么语言| 良代表什么生肖| 立秋当天吃什么| 上校相当于政府什么官| 食谱是什么意思| 恩字五行属什么| 全身骨头疼是什么原因| iic是什么意思| 杀青什么意思| 悠悠岁月什么意思| 力所能及什么意思| 1962年属什么生肖| 鸡眼用什么药| 儿女情长英雄气短是什么意思| 肠鸣吃什么药| 白目是什么意思| 受贿是什么意思| 什么口服液补血补气最好| 爱的真正含义是什么| 瑶浴是什么意思| 九月十号什么星座| 杵状指见于什么病| 鼻子和嘴巴连接的地方叫什么| 吃什么水果下火| 药流后吃什么消炎药| 东北易帜是什么意思| 传教士是什么意思| 什么的公鸡| 皂角米是什么东西| 蟹爪兰什么时候开花| 100年前是什么朝代| 什么的糯米| 0什么意思| 血脂高吃什么药好| 口臭是什么原因造成的| 澳门打车用什么软件| 不停的出汗是什么原因| 333是什么意思| 肝不好挂什么科室| 七月一号是什么节| 多吃核桃有什么好处和坏处| 肠系膜淋巴结炎吃什么药最有效| 贫血去医院挂什么科| 那英姓什么| 什么是舍利| 床上放什么可以驱蟑螂| 天秤女和什么座最配对| 四十不惑是什么意思| 冰箱保鲜室不制冷是什么原因| 冶阳萎什么药最有效| 蛇酒不是三十九开什么| 指甲看什么科| 眼睑红是什么原因| 1981年是什么命| 佰草集适合什么年龄| 血钾低会有什么症状| 牙疼吃什么药止痛快| 你想什么| 10.22是什么星座| 划扣是什么意思| 喉咙发炎吃什么药好得快| 财神在什么方位| 玛尼是什么意思| 肚脐眼左边疼是什么原因| 雨露均沾是什么意思| 脖子长小肉粒是什么原因| 枕头底下放剪刀有什么说法| 鸡胗是什么部位| 三生三世是什么意思| 反射弧是什么意思| 一票制什么意思| 脸大适合什么发型| 什么情况下需要做造影| 寡欲是什么意思| 身上到处痒是什么原因| 天德是什么意思| 荨麻疹能吃什么水果| 穿云箭是什么意思| 10月27日什么星座| 透明质酸钠是什么东西| 朝代表什么生肖| 女人后脑勺出汗多是什么原因| 胃炎可以吃什么水果| 霍霍人什么意思| 鸡鸡长什么样| 荨麻疹忌口什么食物| 颜狗是什么意思| 崴脚用什么药| 1976年出生属什么生肖| 梦见自己被警察抓了是什么意思| 脸发黄是什么原因| 油价什么时候上涨| 一泻千里是什么意思| 耳朵发烫是什么原因| 和胃降逆是什么意思| 女人吃知了有什么好处| 渡人是什么意思| 什么颜色防晒效果好| 间接胆红素偏高是什么原因| 辣椒是什么时候传入中国的| 烧心是什么原因| 什么叫三焦| 猪肝跟什么相克| 水瓶女和什么座最配| 盗墓笔记讲的是什么故事| 眼睛红痒用什么眼药水| 水马是什么| 脑出血挂什么科| 肌腱是什么组织| 为什么感冒会头痛| 逝者如斯夫是什么意思| pt什么意思| 兵字五行属什么| 嘛哩嘛哩哄是什么意思| 出国旅游需要什么手续和证件| 西洋参有什么功效和作用| 兔子肉不能和什么一起吃| bbc是什么意思| 捐肾对身体有什么影响| 尿毒清颗粒主治什么病| 血糖高能吃什么肉| 什么网名好听又有内涵| 修成正果是什么意思| 吃什么补羊水| 怀孕初期怕冷是什么原因| 海绵是什么材料做的| twins什么意思| 突然戒烟对身体有什么影响| 歇斯底里是什么意思| 梦见自己怀孕大肚子是什么预兆| 梦见雪地是什么征兆| 印第安纹是什么| qq会员有什么用| 廉洁奉公是什么意思| 乳酸脱氢酶高是什么原因| 老虎的祖先是什么动物| 身上长白色的斑点是什么原因| 柠檬泡水喝有什么作用| 为什么坐月子不能吹风| 误区是什么意思| 出家当和尚有什么要求| y谷氨酰基转移酶高是什么原因| 娇喘是什么| 中国第一艘航空母舰叫什么| 打火机里的液体是什么| tr是什么| 冬至有什么忌讳| 玉势是什么| 岳飞属什么生肖| 气管炎挂什么科| 胆囊炎需要注意什么| 意象是什么| 什么颜色的衣服最防晒| 怀孕胎盘低有什么影响| 子宫前位和子宫后位有什么区别| 手淫过度会导致什么| 左肾积水有什么症状| 什么是手卫生| 乌鸦兄弟告诉我们什么道理| 所以我求求你别让我离开你是什么歌| 为什么会有脚气| zgo手表是什么牌子| 什么是白条| 脚上长痣代表什么| 啐是什么意思| 金贵肾气丸治什么病| 上颚疼吃什么药| 脚痛去医院挂什么科| 精子是什么味道的| 床咚是什么意思啊| 泪沟是什么| 纵隔子宫是什么意思| 肌肉的作用是什么| 心功能一级什么意思| 蛮什么意思| 执念什么意思| 昂热为什么认识路鸣泽| 身上出冷汗是什么原因| 皮肤软组织感染是什么意思| 睾丸扭转是什么导致的| 三道杠是什么牌子| 什么血糖仪准确度高| 七月18日是什么星座| 安踏属于什么档次| 乳房皮肤痒是什么原因| 格列卫是什么药| 过房养是什么意思| 莓茶什么人不适合喝| 风热感冒用什么药| 微米是什么单位| 左侧头皮发麻是什么原因| 牙龈发黑是什么原因| 脚有点浮肿是什么原因| 回声结节什么意思| 黄芪和什么搭配不上火| 逝者如斯夫什么意思| 隆胸有什么危害和后遗症吗| 眼袋大用什么方法消除| 软下疳是什么症状| 菩提手串有什么寓意| 脚上有痣代表什么| 词牌名什么意思| 维生素b族什么时候吃最好| 龙涎香是什么东西| 掉头发缺少什么维生素| 吃山药有什么好处和坏处| 什么水果对胃好| 生理盐水是什么| 四维什么时候做| 猴子属于什么类动物| 2004年出生属什么| 左肾结石的症状是什么| 总是想睡觉是什么原因| 单飞什么意思| 痕迹是什么意思| 卵巢囊性占位是什么意思| 舌头溃疡是什么原因造成的| 蛇属于什么类动物| 小孩嗓子哑了吃什么药| 冰粉籽是什么植物| 般若是什么意思| 孕妇吸氧对胎儿有什么好处| 总梦到一个人说明什么| 外溢是什么意思| yy是什么意思| 过年给老人买什么| 为什么延迟退休| 驻外大使是什么级别| 老花眼是什么原因引起的| moco是什么牌子| 和什么细什么的成语| 咳嗽吃什么好的快偏方| 死忠粉是什么意思| 什么是蚕豆病| 淋巴细胞偏高是什么原因| 尿偏红色是什么原因| 鸡蛋白是什么| 什么是木薯| 口苦吃什么药好| 94是什么意思| 火龙果跟什么榨汁好喝| 候和侯有什么区别| 缺铁性贫血吃什么好| 慎用是什么意思| 小孩肚脐周围疼是什么原因| 纹身有什么危害| 枳是什么意思| 什么叫入伏| 农历五月二十四是什么日子| 贵州有什么山| 枝柯是什么意思| 开脸是什么意思| 酒量越来越差什么原因| 彼岸花什么时候开花| 谷丙转氨酶偏低是什么意思| 青椒炒什么好吃| 君子兰叶子发黄是什么原因| 小孩白头发是什么原因引起的| 手代表什么生肖| 背沟深代表什么| 红红的太阳像什么| 扶他是什么意思| 百度
这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 ? 论坛首页 ? 活动中心 ? 板卡试用 ? 【分享开发笔记,赚取电动螺丝刀】基于M5StickCPlus制作的儿时小游戏——

共11条 1/2 1 2 跳转至

【分享开发笔记,赚取电动螺丝刀】基于M5StickCPlus制作的儿时小游戏——弹珠迷宫

助工
2025-08-04 17:39:04     打赏
百度 原标题:四川茂县塌方最小伤者仅6岁:爸爸爷爷当场遇难  最小伤员龚钰婷左腿膝盖上部骨裂  “茂县山体塌方”续  最小伤者年仅6岁旅行途中遇飞石爸爸爷爷当场遇难  东方网7月18日消息:7月17日下午2点过,茂县石大关发生山体滑坡,事故造成10人死亡,22人受伤。

Fp6R735udpF1OqsGEKO0VRoOkPmr

1、硬件介绍:
M5StickC PLUS 是M5StickC的大屏幕版本。由电子森林“2022暑期在家一起练”活动推出的一个开发板。主控采用ESP32-PICO-D4模组,具备蓝牙4.2与WIFI功能,小巧的机身内部集成了丰富的硬件资源,如红外、RTC、麦克风、LED、IMU、按键、蜂鸣器、PMU等,在保留原有M5StickC功能的基础上加入了无源蜂鸣器,同时屏幕尺寸升级到1.14寸、135*240分辨率的TFT屏幕,相较之前的0.96寸屏幕增加18.7%的显示面积,电池容量达到120mAh,接口同样支持HAT与Unit系列产品。开发平台支持 UIFlow, MicroPython, Arduino。

2、设计思路:

思路来源于一个弹珠迷宫的游戏。小时候上学,同桌拿来的尺子上带着这样的游戏。就是一个小铁弹珠,尺子中间是镂空的,通过控制尺子的角度移动弹珠通过迷宫。到现在当时老师课上讲的什么,全忘了,这个游戏还历历在目。M5StickC PLUS 中集成了IMU,正好可以用了检测水平角度,通过重力控制小球通过迷宫。编程环境就选用Arduino。

3、设计实现:

FlZy6kNgcuFGbii8sZHs9_3fJjDn

既然是个迷宫游戏,就要先有个迷宫。大概了解了一下,生成迷宫的算法主要有三种思路,其中最小生成树算法又可以分为选点法(prim)和选边法(kruskal):随机深度优先算法。递归分割算法(TODO)。随机prim最小生成树算法。*kruskal最小生成树算法(使用并查集实现)。生成的迷宫需要在屏幕上显示,M5StickC PLUS 屏幕135*240像素的。我这里使用15*15的矩形块作为迷宫的通道和障碍物。则迷宫的规模就是15*27。使用随机深度优先算法来生成迷宫。深度优先算法过程核心是随机选择遍历上下左右四个方向的顺序,然后开始搜索。将整个迷宫看做一个【15*27】的矩阵,每个节点使用一位来存储,每一行就使用两个byte来存储。迷宫就使用一个无符号整型的数组来表示,长度为27。先使用python来生成一个迷宫。

import numpy as np
import time
import random
import copy

class Maze(object):
    def __init__(self, width=11, height=11):
        # 迷宫最小长宽为5
        assert width >= 5 and height >= 5, "Length of width or height must be larger than 5."

        # 确保迷宫的长和宽均为奇数
        self.width = (width // 2) * 2 + 1
        self.height = (height // 2) * 2 + 1
        self.start = [1, 0]
        self.destination = [self.height - 2, self.width - 1]
        self.matrix = None

    def print_matrix(self):
        for i in range(self.height):
            rowval=0
            for j in range(self.width):
                if self.matrix[i][j] == -1:
                    rowval=(rowval<<1)+1
                elif self.matrix[i][j] == 0:
                    rowval = (rowval<<1) + 0
    
            print(rowval,end=",")
            # print('')

    def generate_matrix_dfs(self):
        # 地图初始化,并将出口和入口处的值设置为0
        self.matrix = -np.ones((self.height, self.width))
        self.matrix[self.start[0], self.start[1]] = 0
        self.matrix[self.destination[0], self.destination[1]] = 0

        visit_flag = [[0 for i in range(self.width)] for j in range(self.height)]

        def check(row, col, row_, col_):
            temp_sum = 0
            for d in [[0, 1], [0, -1], [1, 0], [-1, 0]]:
                temp_sum += self.matrix[row_ + d[0]][col_ + d[1]]
            return temp_sum <= -3

        def dfs(row, col):
            visit_flag[row][col] = 1
            self.matrix[row][col] = 0
            if row == self.start[0] and col == self.start[1] + 1:
                return

            directions = [[0, 2], [0, -2], [2, 0], [-2, 0]]
            random.shuffle(directions)
            for d in directions:
                row_, col_ = row + d[0], col + d[1]
                if row_ > 0 and row_ < self.height - 1 and col_ > 0 and col_ < self.width - 1 and visit_flag[row_][
                    col_] == 0 and check(row, col, row_, col_):
                    if row == row_:
                        visit_flag[row][min(col, col_) + 1] = 1
                        self.matrix[row][min(col, col_) + 1] = 0
                    else:
                        visit_flag[min(row, row_) + 1][col] = 1
                        self.matrix[min(row, row_) + 1][col] = 0
                    dfs(row_, col_)

        dfs(self.destination[0], self.destination[1] - 1)
        self.matrix[self.start[0], self.start[1] + 1] = 0


# 这里的长和宽设置的是50,但是实际生成的迷宫长宽会是51
maze = Maze(15, 27)
maze.generate_matrix_dfs()
maze.print_matrix()

初始化各个组件:这里需要用到屏幕——用了展示、IMU ——用来感知开发板与地面的夹角、串口——用来调试程序。M5StickC Plus提供了详细的说明文档,还提供了Arduino相关的例程。参考着例程对系统进行初始化。

void setup() {
  M5.begin();
  M5.Lcd.fillScreen(TFT_DARKCYAN);
  Serial.begin(115200);
  M5.Imu.Init();          // Init IMU.  初始化IMU

  drawMaze();
  drawBall(ballpos[0], ballpos[1]);
  //  delay(1000);
  //  dispSuccess();
}

初始化后,立即绘制迷宫图案,整个迷宫仅仅需要绘制障碍物部分,在游戏过程中,障碍物时不会被覆盖,所以只需要绘制一次即可。小球要求能够在通道中顺滑地滚动,这里使用一个4像素的圆的图案作为移动的小球。

//绘制迷宫
void drawMaze() {
  bool board;
  for (int i = 0; i < MAZEHIGHT; i++) {
    //迷宫每一个数组 都是迷宫每一行的信息,有16位信息
    for (int j = 0; j < MAZEWIDE; j++) {
      board = (mazelist[i] >> j) & 1;
      //      Serial.print(board);
      //      Serial.print(" ");
      if (board) {
        //绘制障碍物
        drawBlock(j, i);
      } else {
        //无处理
      }
    }
    Serial.println();
  }
}

这个游戏的输入为重力。通过手控制M5StickC Plus开发板的水平角度,来控制小球移动。小球可以在水平方向上移动,移动方向就有2个x、y。所以只需要读取IMU的accX和accY的值即可。
M5StickC Plus中IMU使用的是MPU6886。accX和accY读取到的值就是重力在水平面上的分量。通过三角函数可以计算出开发板当前的倾斜角度。这里为了简化模型,直接使用读取到的accX和accY放大到整数,作为控制小球移动的力量。将移动小球的速度控制在一个合理的区间内,对x、y方向的力的大小由accX和accY的值做范围限制,限制在【-3,3】之间。当偏转角度较小时(为0时),循环读取IMU的数据,对屏幕不做处理。

void loop() {
  int movex = 0, movey = 0;
  short newposx, newposy;
  if (!isSuccess) {
    while (1) {
      M5.IMU.getAccelData(&accX, &accY, &accZ);
      movex = accX * IMUZOOM;
      movey = accY * IMUZOOM;
      if (movex != 0 || movey != 0) {
        break;
      }
    }
    //控制移动上限
    if (movex > MAXSPEED) {
      movex = MAXSPEED;
    }
    if (movex < -MAXSPEED) {
      movex = -MAXSPEED;
    }
    if (movey > MAXSPEED) {
      movey = MAXSPEED;
    }
    if (movey < -MAXSPEED) {
      movey = -MAXSPEED;
    }
    Serial.printf("%dt%dn", movex, movey);
    moveBall(movey, -movex);
    delay(15);
  } else {
    dispSuccess();
    delay(5000);
  }
}

当感知到M5StickC Plus水平面有偏转了,即需要小球移动时,首先检查是否在终点,在终点则游戏结束。不在终点就分别检查X,Y两个方向上是否可以移动。优先X方向。若两个方向均可移动,先在X方向移动1格,再在Y方向移动一格。若只是单方向可以移动,则仅仅处理单方向的移动。小球每次移动1个格子,在屏幕上的新位置绘制小球,然后在原来的位置使用背景色绘制一遍小球,就实现了小球移动动画的绘制。这样每次就只需要重新绘制小球所在新旧位置的圆即可,大大提升了绘制的速度,使得界面流畅。不同的移动力量对应着小球最大能移动的步数。最大力量对应着最多能移动3格。

//绘制小球
void drawBall(uint x, uint y) {
  int posx = x * BLOKESIZE ;
  int posy = y * BLOKESIZE ;
  M5.Lcd.fillCircle(posy + BALLRADIUS, posx + BALLRADIUS, BALLRADIUS, TFT_YELLOW);
}
//清除小球
void clearBall(uint x, uint y) {
  int posx = x * BLOKESIZE ;
  int posy = y * BLOKESIZE ;
  M5.Lcd.fillCircle(posy + BALLRADIUS, posx + BALLRADIUS, BALLRADIUS, TFT_DARKCYAN);
}
//判断新位置是否有障碍物,如果没有则返回false 有则true
bool isBlock(uint x, uint y) {
  uint mazecol = mazelist[x];
  if (x < 0 || x >= MAZEHIGHT) return true;
  if (y < 0 || y >= MAZEWIDE) return true;
  return (mazecol >> y) & 1;
}
//判断新位置是否为初始值或者是结束值。初始值:-1  结束值:1 其它:0
short isSpacePos(uint x, uint y) {
  if (x == 1 && y == MAZEWIDE - 1) return -1;
  if (x == MAZEHIGHT - 1 - 1 && y == 0) return 1;
  return 0;
}
//移动小球的执行动作
void dealMoveBall(short newx, short newy) {
  //判断新的坐标是否 到达特殊地址?
  short echo = isSpacePos(newx, newy);
  if(newx==ballpos[0] && newy==ballpos[1]) return;
  Serial.printf("oldpos:%d,%dttnewpos=%d,%dt%dnn", ballpos[0], ballpos[1], newx, newy, echo);
  
  if ( echo == 1) { //到达终点
    isSuccess = true;
    drawBall(newx, newy);
    clearBall(ballpos[0], ballpos[1]);
    ballpos[0] = newx;
    ballpos[1] = newy;
  } else if ( echo == 0) {
    //无障碍物
    drawBall(newx, newy);
    clearBall(ballpos[0], ballpos[1]);    
    ballpos[0] = newx;
    ballpos[1] = newy;
  }
}
//移动小球 x ,y 代表x方向和y方向上的移动,每次仅移动1
void moveBall(short movex, short movey) {
  bool xflag = false, yflag = false;
  //通过与水平夹角决定移动小球
  short moveround = abs(movex) > abs(movey) ? abs(movex) : abs(movey);
  short newx = ballpos[0], newy = ballpos[1];
  //  Serial.printf("%dn",moveround);

  if ( isSuccess ) return;      //如果已经完成了,则退出
  for (int i = 0; i < moveround; i++) {
    //每次移动一位 x 轴
    if (movex > 0) {    //右移
      newx = ballpos[0] + 1;
      movex = movex - 1;
      xflag = true;
    } else if (movex < 0) {    //左移
      newx = ballpos[0] - 1;
      movex = movex + 1;
      xflag = true;
    }

    if (movey > 0) {    //上移
      newy = ballpos[1] + 1;
      movey = movey - 1;
      yflag = true;
    } else  if (movey < 0) {    //下移
      newy = ballpos[1] - 1;
      movey = movey + 1;
      yflag = true;
    }
    //    Serial.printf("count new pos:%d,%d        newxy:%d,%dn",ballpos[0], ballpos[1],newx, newy);
    //三种可能  xy都移动
    if (!isBlock(newx, newy)) {
      //      Serial.printf("move xy:%d,%dn",newx, newy);
      dealMoveBall(newx, newy);
    } else   if (xflag && (!isBlock(newx, ballpos[1]))) {    //只移动X轴
      //      Serial.printf("move x:%d,%dn",newx, ballpos[1]);
      dealMoveBall(newx, ballpos[1]);
    } else  if (yflag && (!isBlock(ballpos[0], newy))) {     //只移动y轴
      //      Serial.printf("move y:%d,%dn",ballpos[0],newy);
      dealMoveBall(ballpos[0], newy);
    }
  }
}

游戏开局,小球在左上方的入口,通过控制M5StickC Plus的水平角度,慢慢滴让小球移动到右下角的出口。实现了小时游戏的感觉。

Fpz5B0lqc4wSJFqpw7leqY4-AgUW

FqJwA993x69YXQrGRrZaFevNDCX9

弹珠游戏.zip



专家
2025-08-04 17:50:10     打赏
2楼

感谢分享


专家
2025-08-04 17:51:26     打赏
3楼

感谢分享


专家
2025-08-04 17:52:49     打赏
4楼

感谢分享


高工
2025-08-04 09:59:39     打赏
5楼

mazelist 是16bit宽度的?感觉只有这样,这源码才能跑的通


专家
2025-08-04 09:30:56     打赏
6楼

真不错,局部刷新,是处理动画常用的而一种方式。


工程师
2025-08-04 10:48:39     打赏
7楼

image.png

大佬这作品好优秀!


院士
2025-08-04 16:57:46     打赏
8楼

小作品真棒。

arduino也是确实挺方便的,代码量大幅缩减


高工
2025-08-04 20:22:42     打赏
9楼

大佬这款屏幕的刷新速率怎么样?在考虑一个款价格便宜的屏幕?麻烦推荐一下


工程师
2025-08-04 09:58:45     打赏
10楼

超级有趣, 主要体现在制作和算法的实现过程上. 屏幕的刷新率怎么样?


共11条 1/2 1 2 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]
什么姓氏排第一 运动员心率为什么慢 kappa是什么牌子 低烧吃什么药好 笔走龙蛇是什么生肖
舌头发黑是什么原因 耳鸣是什么症状 等位基因是什么 什么地摇动 子痫前期是什么意思
薄荷泡水喝有什么功效 沉香有什么作用与功效 芝麻分是什么意思 泌尿感染吃什么药最好 西洋参不能和什么一起吃
吃稀饭配什么菜好吃 王加几念什么 缺钙吃什么补钙最快 烧高香是什么意思 在减肥期间吃什么最好
束缚是什么意思hcv8jop8ns2r.cn 食管挂什么科hcv9jop6ns5r.cn journey是什么意思qingzhougame.com 手指腱鞘炎是什么原因造成的hcv7jop7ns1r.cn 雪蛤是什么hcv8jop0ns4r.cn
嘌呤是什么意思hcv7jop6ns3r.cn 孕妇喝什么牛奶hcv8jop6ns2r.cn es什么意思hcv9jop5ns2r.cn 天可以加什么偏旁hcv9jop4ns3r.cn 写意是什么意思hcv9jop7ns5r.cn
氢是什么hcv9jop2ns7r.cn 四平八稳是什么意思gangsutong.com 秋葵有什么营养hcv7jop7ns3r.cn 杏子不能和什么一起吃hcv8jop7ns0r.cn 主心骨是什么意思hcv9jop0ns7r.cn
干燥综合症挂什么科hcv7jop4ns5r.cn 腿肿吃什么药消肿最快最有效hcv8jop1ns5r.cn 苹果越狱是什么意思啊hcv8jop4ns7r.cn 梦见办丧事是什么兆头travellingsim.com 做梦人死了是什么征兆hcv8jop7ns7r.cn
百度 技术支持:蜘蛛池 www.kelongchi.com