基于CircuitPython的嵌入式滑动拼图游戏开发:从displayio到状态机实践
1. 项目概述为PyBadge打造一款滑动拼图游戏如果你手头有一块PyBadge或者PyBadge LC除了跑跑官方示例有没有想过用它来做点更有趣、更“复古”的东西我最近就折腾了这么一件事用CircuitPython给这块小巧的开发板写了一个完整的滑动拼图游戏。这可不是简单的“Hello World”级别应用它涉及到显示管理、用户输入、游戏逻辑、状态存储等一系列在嵌入式设备上开发完整应用时会遇到的典型问题。整个过程下来感觉像是把PyBadge的硬件潜力给“榨”了一遍从displayio的图层管理到adafruit_debouncer的按键消抖每一个环节都踩过坑也积累了不少心得。这个项目的核心就是在一块240x135像素的彩色屏幕上实现一个经典的滑动拼图。玩家通过PyBadge上的方向键和A/B按钮移动打乱的图块最终还原成一幅完整的图片。听起来简单但要在内存和性能都受限的微控制器上流畅运行并且提供良好的用户体验就需要在代码结构和资源管理上多花些心思。它非常适合作为学习CircuitPython图形编程和事件驱动编程的练手项目做完之后你对如何在PyBadge这类设备上构建交互式应用会有一个非常扎实的理解。2. 核心思路与架构设计2.1 为什么选择滑动拼图滑动拼图是一个经典的游戏形式它几乎是为PyBadge这类带有方向键和有限屏幕的设备量身定做的。从技术实现角度看它完美匹配了PyBadge的硬件特性方向键天然对应拼图块的移动方向A/B键可以用来处理菜单和确认操作。更重要的是这个项目涵盖了嵌入式图形应用开发的几个核心模块图形渲染、用户输入处理、游戏状态逻辑以及数据持久化。通过实现它你可以系统地掌握displayio这个CircuitPython中强大的显示框架理解如何组织多个TileGrid来构建游戏界面并学会用状态机的方式来管理游戏流程。在方案选型上我放弃了使用更底层的帧缓冲framebuffer直接绘图的方式而是完全基于displayio。这是因为displayio提供了更高层次的抽象比如图层Group、位图Bitmap和瓦片网格TileGrid它们能更高效地管理屏幕上的图形元素特别是对于需要频繁更新局部区域的游戏来说TileGrid只更新索引而不重绘整个位图的特性能极大提升性能。这个选择背后的考量是开发效率与运行性能的平衡displayio虽然有一定学习曲线但一旦掌握构建复杂界面的速度会快很多。2.2 整体软件架构拆解整个游戏的代码结构可以划分为四个相对独立的层次这样设计的好处是逻辑清晰便于调试和后续扩展。第一层是硬件抽象层。这一层负责与PyBadge的物理硬件打交道主要是初始化屏幕和读取按键状态。对于屏幕我们创建一个displayio.Display对象。对于按键PyBadge上的5个方向键和2个动作键A/B通过keypad模块来扫描。这里有一个至关重要的细节必须使用adafruit_debouncer库对按键信号进行消抖处理。微控制器的按键扫描非常快物理按键的抖动会导致一次按压被误判为多次不加消抖的话游戏操控会变得极其不可控拼图块可能会“飞”出去。第二层是资源管理层。拼图游戏需要两张核心图片一张是完整的、作为目标的原图另一张是被切割成多个小图块并打乱后的当前状态图。在CircuitPython中我们需要将图片转换为PyBadge能够直接使用的格式。通常的做法是在电脑上使用像imagemagick这样的工具将图片转换为适合屏幕大小的位图例如240x135像素并保存为.bmp格式。然后利用CircuitPython的displayio库中的OnDiskBitmap来加载这些图片资源。这一层管理着这些位图对象以及从中派生出的TileGrid。第三层是游戏逻辑层。这是整个项目的“大脑”。它要维护一个代表拼图状态的二维数组比如一个4x4的网格其中15个元素是图块编号1个是空位。玩家的每一次移动操作都对应着这个二维数组中空位与相邻图块位置的交换。逻辑层需要判断移动是否合法空位是否在目标方向执行交换并随时检查当前状态是否已经与胜利状态即按顺序排列一致。这一层的代码应该是纯逻辑的与具体的显示和输入方式解耦。第四层是表现层与状态机。这一层负责将游戏逻辑层的状态“可视化”到屏幕上并管理游戏的整体流程。我使用一个简单的状态机来管理不同的游戏界面比如“主菜单界面”、“游戏进行界面”和“胜利界面”。根据当前状态表现层决定显示哪些TileGrid图层并响应不同的按键事件如在菜单中切换选项在游戏中移动图块。displayio.Group在这里大显身手我们可以创建多个Group分别对应菜单UI、游戏拼图盘和胜利提示通过动态地将它们加入或移出主显示Group来实现界面的切换。3. 关键技术细节与实现要点3.1 使用displayio构建游戏界面displayio是CircuitPython图形应用的基石理解其核心概念是成功的关键。首先是一个显示总线Display Bus它代表了与屏幕硬件的连接方式如SPI。然后我们创建显示对象Display它管理着最终的输出。屏幕上显示的所有内容都组织在一个或多个群组Group中Group可以嵌套形成树状结构。对于拼图游戏我们至少需要三个主要的Group背景Group可能包含一个全屏的底色或背景图。拼图盘Group这是一个最重要的Group它包含一个TileGrid对象。这个TileGrid的每个“格子”Tile对应拼图的一个小图块。UI覆盖层Group用于显示分数、菜单按钮或胜利文字等。它应该位于拼图盘之上。创建拼图盘TileGrid的步骤是准备一张被切割好的源位图Source Bitmap。假设我们做4x4拼图就需要准备一张包含了16个均匀切分小图的大图或者用程序将一张完整图片在内存中切割并打乱。创建一个调色板ColorConverter或Palette如果是索引颜色位图。实例化TileGrid对象指定源位图、像素尺寸、瓦片尺寸即每个小图块的大小如60x45、默认瓦片索引矩阵一个二维列表决定初始时每个格子显示源位图的哪一部分。将这个TileGrid添加到拼图盘Group中再将Group添加到显示对象的主Group中。注意TileGrid的“瓦片索引”是我们控制显示内容的关键。游戏过程中我们不移动图块本身而是更新这个索引矩阵。比如将空位旁边的图块索引移动到空位的位置并将原位置设为“空”的索引通常是一个空白图块。这种方式比直接重绘位图高效得多。3.2 游戏逻辑与状态管理游戏的核心逻辑是一个二维数组我们称之为puzzle_state。对于4x4拼图它是一个4行4列的列表。数组中的每个元素存储的是对应位置上应该显示的图块编号1-15以及一个特殊值比如0代表空位。初始化我们需要生成一个随机的、但可解的初始状态。滑动拼图不是所有随机排列都能解开的。一个简单的生成可解状态的方法是从完成状态开始模拟一系列随机的、合法的移动移动空位。这样得到的状态一定是可解的。移动逻辑当玩家按下方向键时根据当前空位坐标和方向计算出目标图块的坐标。检查目标坐标是否在棋盘范围内。如果合法则交换puzzle_state中空位和目标位置的数值。更新TileGrid的瓦片索引矩阵以反映puzzle_state的变化。检查puzzle_state是否等于完成状态的数组。状态检查胜利条件就是puzzle_state与有序数组[1,2,3,...15,0]完全一致。这里可以优化不必每次移动后全盘比较可以只检查最后移动的图块是否到了正确位置并维护一个“正确位置图块数”的计数器。3.3 用户输入与交互优化PyBadge的按键通过keypad.Keys或keypad.ShiftRegisterKeys取决于具体型号来读取。代码会不断扫描返回被按下的键码。消抖是必须的直接使用扫描结果会导致问题。我们必须为每个按键创建一个Debouncer对象。它的原理是当检测到按键状态变化时会等待一个很短的时间例如0.05秒如果状态保持稳定才确认这次变化。这样就能过滤掉物理抖动产生的毛刺信号。import keypad import board from adafruit_debouncer import Debouncer # 初始化按键矩阵 keys keypad.Keys(...) # 为每个按键创建消抖器假设键码列表是[key_up, key_down, key_left, key_right, key_a, key_b] key_pins [board.KEY_UP, board.KEY_DOWN, ...] # 根据实际板子定义 debounced_keys [] for pin in key_pins: # 这里需要根据keypad的实际用法获取switch对象以下为概念代码 switch ... # 获取对应按键的开关状态对象 debounced_keys.append(Debouncer(switch))在主循环中我们需要先更新所有Debouncer对象然后检查它们的.rose属性是否从False变为True即按下事件。交互反馈好的游戏需要有及时的反馈。当图块移动时可以伴随一个简短的声音通过audioio驱动蜂鸣器或屏幕闪烁。在菜单选择时高亮当前选项。这些细微的效果能极大提升用户体验。4. 完整实现步骤与代码解析4.1 环境准备与依赖安装首先确保你的PyBadge或PyBadge LC已经刷好了最新版本的CircuitPython固件。你可以从CircuitPython官网下载对应的.uf2文件按住板子上的复位键或通过双击复位按钮进入引导程序将电脑识别出的U盘中的旧固件文件替换为新下载的.uf2文件即可。接下来我们需要准备必要的库文件。除了CircuitPython内置的displayio、keypad等我们还需要一个外部库adafruit_debouncer。这是因为CircuitPython的核心库为了保持精简没有内置软件消抖功能。访问CircuitPython的官方库包发布页面。下载最新版本的adafruit_debouncer库通常是一个.mpy或包含__init__.py的文件夹。将下载的adafruit_debouncer.mpy文件或整个文件夹拖入你的PyBadge在电脑上显示的CIRCUITPY驱动器下的lib文件夹中。如果lib文件夹不存在就新建一个。你的CIRCUITPY驱动器的文件结构应该大致如下CIRCUITPY/ ├── lib/ │ └── adafruit_debouncer.mpy ├── code.py └── (其他资源文件如图片)code.py是主程序入口CircuitPython启动后会自动执行它。4.2 图片资源处理与加载PyBadge的屏幕是240x135像素为了获得最佳效果我们的拼图原图也应该是这个尺寸。你可以用任何图片编辑软件如Photoshop、GIMP甚至是在线的工具将图片调整为此大小并保存为索引颜色Indexed Color的BMP格式。索引颜色可以减小文件体积并且displayio能很好地支持。假设我们有一张background.bmp作为游戏背景一张puzzle_image.bmp作为要被切割的拼图原图。我们需要将puzzle_image.bmp在概念上切割成M行N列。在代码中我们并不需要物理上切割成多个文件而是通过TileGrid来定义如何从一张大图中取出一小块。首先将图片文件放入CIRCUITPY驱动器根目录或某个子目录。然后在code.py中加载它们import displayio import board # 释放任何可能已存在的显示资源用于软重启后清理 displayio.release_displays() # 初始化SPI总线PyBadge LC的屏幕通常使用SPI spi board.SPI() tft_cs board.TFT_CS tft_dc board.TFT_DC tft_reset board.TFT_RESET # 创建显示总线和显示对象 display_bus displayio.FourWire(spi, commandtft_dc, chip_selecttft_cs, resettft_reset) display displayio.Display(display_bus, width240, height135, rotation0) # 加载背景图 background_bitmap displayio.OnDiskBitmap(/background.bmp) background_tilegrid displayio.TileGrid(background_bitmap, pixel_shaderbackground_bitmap.pixel_shader) background_group displayio.Group() background_group.append(background_tilegrid) # 加载拼图源图 puzzle_bitmap displayio.OnDiskBitmap(/puzzle_image.bmp) # 创建拼图TileGrid。假设做4x4拼图每个图块就是60x45像素 (240/460, 135/4≈33.75取整33但为了整除我们通常选择能整除的尺寸这里用3x3或4x5等以下以4x4为例实际需调整图片尺寸) TILE_WIDTH 60 TILE_HEIGHT 45 PUZZLE_ROWS 4 PUZZLE_COLS 4 # 创建一个初始的瓦片索引矩阵例如按顺序排列 initial_tile_grid displayio.TileGrid( puzzle_bitmap, pixel_shaderpuzzle_bitmap.pixel_shader, widthPUZZLE_COLS, heightPUZZLE_ROWS, tile_widthTILE_WIDTH, tile_heightTILE_HEIGHT, default_tile0 # 初始显示第一个图块通常不是我们想要的后面会打乱 ) puzzle_group displayio.Group() puzzle_group.append(initial_tile_grid) # 将背景和拼图组添加到主显示组背景在下层 main_group displayio.Group() main_group.append(background_group) main_group.append(puzzle_group) display.show(main_group)4.3 游戏主循环与状态机实现游戏的主循环负责协调所有模块读取输入、更新游戏逻辑、刷新显示。我们用一个状态变量game_state来标识当前处于哪个界面。import time from adafruit_debouncer import Debouncer # 定义游戏状态 STATE_MENU 0 STATE_PLAYING 1 STATE_SOLVED 2 current_state STATE_MENU last_move_time 0 MOVE_COOLDOWN 0.2 # 移动冷却时间防止按键过快 # 初始化按键和消抖器此处为概念代码实际引脚需查阅PyBadge文档 keys keypad.Keys(...) key_objects [...] # 获取keypad的键对象列表 debouncers [Debouncer(k) for k in key_objects] # 初始化拼图逻辑状态 puzzle_state [[0 for _ in range(PUZZLE_COLS)] for _ in range(PUZZLE_ROWS)] empty_pos (PUZZLE_ROWS-1, PUZZLE_COLS-1) # 初始空位在右下角 def init_puzzle(): # 生成一个可解的随机初始状态 # 1. 先填充有序数字 num 1 for r in range(PUZZLE_ROWS): for c in range(PUZZLE_COLS): if not (r PUZZLE_ROWS-1 and c PUZZLE_COLS-1): puzzle_state[r][c] num num 1 # 2. 模拟随机移动空位多次来打乱 # ... (实现随机移动函数) # 3. 根据打乱后的puzzle_state更新TileGrid的瓦片索引 update_tilegrid_from_state() def update_tilegrid_from_state(): for r in range(PUZZLE_ROWS): for c in range(PUZZLE_COLS): tile_index puzzle_state[r][c] # 将逻辑状态映射到TileGrid的瓦片索引。 # 注意TileGrid的default_tile参数是一个“索引”指向源位图中的某个图块。 # 我们需要一个映射关系逻辑数字 - 源位图瓦片索引。 # 假设我们的源位图是按顺序排列的16个格子那么逻辑数字1就对应索引0数字2对应索引1...数字15对应索引14空位(0)对应索引15一个空白格。 visual_index tile_index - 1 if tile_index ! 0 else 15 initial_tile_grid[r][c] visual_index def handle_move(direction): # direction: up, down, left, right global empty_pos er, ec empty_pos target_r, target_c er, ec if direction up and er 0: target_r er - 1 elif direction down and er PUZZLE_ROWS - 1: target_r er 1 elif direction left and ec 0: target_c ec - 1 elif direction right and ec PUZZLE_COLS - 1: target_c ec 1 else: return False # 移动非法 # 交换逻辑状态 puzzle_state[er][ec], puzzle_state[target_r][target_c] puzzle_state[target_r][target_c], puzzle_state[er][ec] # 更新空位位置 empty_pos (target_r, target_c) # 更新显示 update_tilegrid_from_state() return True def check_win(): # 检查是否拼好 num 1 for r in range(PUZZLE_ROWS): for c in range(PUZZLE_COLS): if not (r PUZZLE_ROWS-1 and c PUZZLE_COLS-1): if puzzle_state[r][c] ! num: return False num 1 return puzzle_state[PUZZLE_ROWS-1][PUZZLE_COLS-1] 0 # 主循环 while True: current_time time.monotonic() # 1. 更新所有按键消抖器状态 for debouncer in debouncers: debouncer.update() # 2. 根据当前游戏状态处理输入 if current_state STATE_PLAYING: # 处理方向键移动并加入冷却时间限制 if current_time - last_move_time MOVE_COOLDOWN: moved False if debouncers[KEY_UP].rose: # 假设KEY_UP是第一个消抖器 moved handle_move(up) elif debouncers[KEY_DOWN].rose: moved handle_move(down) elif debouncers[KEY_LEFT].rose: moved handle_move(left) elif debouncers[KEY_RIGHT].rose: moved handle_move(right) if moved: last_move_time current_time # 可以在这里触发一个声音或震动反馈 if check_win(): current_state STATE_SOLVED # 切换到胜利界面例如显示一个“You Win!”的Group # 处理A/B键例如A键提示B键返回菜单 if debouncers[KEY_A].rose: # 显示提示例如高亮一个错误放置的图块 pass if debouncers[KEY_B].rose: current_state STATE_MENU # 切换回菜单Group elif current_state STATE_MENU: # 处理菜单导航用方向键上下选择A键确认 if debouncers[KEY_A].rose: # 开始新游戏 init_puzzle() current_state STATE_PLAYING # ... 其他菜单逻辑 elif current_state STATE_SOLVED: # 胜利界面按任意键返回菜单 if any(d.rose for d in debouncers): current_state STATE_MENU # 3. 根据current_state更新显示切换不同的Group # 这部分逻辑可以放在状态处理中也可以单独一个函数 # display.show(对应的group) time.sleep(0.01) # 短暂睡眠降低CPU占用5. 性能优化与内存管理实战在PyBadge这样的微控制器上内存和CPU都是稀缺资源。不加优化的话游戏可能会卡顿甚至因为内存不足而崩溃。1. 位图格式与内存使用索引颜色8位或更低的位图比真彩色16位或24位位图节省大量内存。我们的拼图源图就是索引颜色。另外OnDiskBitmap是一种流式位图它不会一次性将整个图片加载到内存而是按需从存储中读取这对大图片非常友好。但TileGrid使用的源位图需要常驻内存。因此要确保同时存在于内存中的大位图数量尽可能少。在我们的设计中只有拼图源图这一个大的OnDiskBitmap常驻内存。2. 避免频繁创建销毁对象在主循环中绝对要避免创建新的Bitmap、TileGrid或Group。这些对象的创建和销毁开销很大。应该在程序初始化时就创建好所有需要的图形对象并通过显示或隐藏它们如从Group中添加或移除来切换界面。3. 局部刷新与脏矩形虽然TileGrid通过更新索引实现了高效刷新但如果你有需要动态绘制的UI元素如计时器文本可以考虑“脏矩形”技术。即只记录需要更新的屏幕区域然后只重绘那一部分。不过对于PyBadge的小屏幕和本游戏的简单UI通常全屏刷新的开销也可以接受。CircuitPython的displayio底层会进行一些优化。4. 游戏逻辑优化check_win()函数每次移动后都全盘检查对于4x4拼图没问题但拼图规模变大后可能成为瓶颈。一个优化方法是维护一个“正确位置计数器”。初始化时计算有多少图块在正确位置。每次移动后只检查被移动的那个图块和空位原来所在位置的两个图块看它们的位置正确性是否发生了变化并更新计数器。当计数器等于总图块数时游戏胜利。5. 使用time.monotonic()进行帧率控制主循环中的time.sleep(0.01)是一种简单的帧率控制但它可能导致输入响应略有延迟。更精细的做法是计算每一帧的耗时如果低于目标帧时间如1/30秒则睡眠剩余时间。这能平衡响应速度和CPU功耗。TARGET_FPS 30 frame_duration 1.0 / TARGET_FPS while True: frame_start time.monotonic() # ... 处理输入、逻辑、渲染 ... frame_time time.monotonic() - frame_start if frame_time frame_duration: time.sleep(frame_duration - frame_time)6. 常见问题排查与调试技巧在开发过程中你几乎一定会遇到下面这些问题。这里我把踩过的坑和解决方法记录下来希望能帮你节省时间。问题一屏幕一片空白或显示混乱。可能原因1显示总线初始化不正确。检查board.SPI()使用的引脚是否正确匹配你的PyBadge型号PyBadge和PyBadge LC的引脚定义可能不同。仔细核对TFT_CS,TFT_DC,TFT_RESET的引脚对象。可能原因2displayio.release_displays()未被调用。如果你的代码在软重启按CtrlD或保存代码后自动重启后运行之前创建的显示资源可能没有释放。务必在初始化显示前调用displayio.release_displays()。可能原因3图片格式不支持。CircuitPython的OnDiskBitmap对BMP格式支持最好且最好是未压缩的。确保图片是BMP格式并且用工具保存为“Windows位图”而非其他变体。尝试用官方示例中的图片替换你的图片以排除图片问题。排查方法先运行一个最简单的测试程序比如只显示一种纯色。如果这个能成功再逐步添加TileGrid和图片。问题二按键无反应或反应异常连击。几乎可以确定是消抖问题。如果你没有使用adafruit_debouncer或者消抖时间参数设置不当就会出现这个问题。确保正确初始化了Debouncer对象并在主循环中调用了每个消抖器的.update()方法。检查键码映射。PyBadge的按键矩阵返回的键值可能不是直观的。使用一个简单的打印程序按下每个键查看串行终端输出的键值确保你的代码中判断的是正确的键值。硬件连接问题。虽然PyBadge是集成板但也要检查是否有物理损坏。可以运行Adafruit官方的按键测试示例来验证硬件。问题三游戏运行缓慢、卡顿。检查主循环中的阻塞操作。避免在循环中进行复杂的计算或文件读写。特别是time.sleep()如果睡眠时间过长会直接导致卡顿。使用非阻塞的延时方式比如上面提到的基于time.monotonic()的帧率控制。内存不足。使用import gc; print(gc.mem_free())在代码不同位置打印剩余内存。如果你看到内存持续减少直到非常低说明存在内存泄漏。确保没有在循环内不断创建新的对象如列表、字符串、图形对象。图片太大或颜色深度太高。如前所述使用索引颜色位图并确保图片尺寸刚好是屏幕或图块尺寸的整数倍避免TileGrid进行运行时缩放。问题四图块显示错位或颜色不对。TileGrid参数错误。仔细检查tile_width和tile_height是否与源位图中每个小图块的实际像素尺寸完全一致。一个像素的偏差都会导致错乱。瓦片索引映射错误。puzzle_state中的逻辑编号1-15必须正确映射到TileGrid的瓦片索引0-14。空位0也需要映射到一个正确的索引比如15对应源位图中的空白格。确保update_tilegrid_from_state()函数中的映射逻辑是正确的。调色板问题。如果使用索引颜色源位图的调色板pixel_shader必须正确。通常OnDiskBitmap会自动处理。但如果颜色显示异常可以尝试将图片转换为标准的256色或16色调色板。调试技巧善用串行终端Serial Console。通过USB连接电脑使用Mu编辑器、Thonny或screen/putty等工具打开串行终端。使用print()语句输出变量状态如empty_pos,current_state 按键值内存剩余量。这是嵌入式开发最直接的调试手段。分阶段测试。不要试图一次性写完所有功能。先写一个程序只显示图片再增加按键读取然后实现拼图逻辑最后整合状态机。每完成一个阶段都进行测试可以快速定位问题所在模块。使用版本控制。即使是一个小项目也建议在电脑上使用Git进行版本管理。每次实现一个稳定功能就提交一次。当新代码导致问题时可以轻松回退到上一个可工作的版本。7. 项目扩展与进阶玩法一个基础滑动拼图完成后你可以在此基础上添加更多功能让它变成一个更丰满、更个性化的项目。1. 多关卡与图片选择在CIRCUITPY驱动器上存放多张图片如/puzzles/pic1.bmp,/puzzles/pic2.bmp。在游戏菜单中增加一个“选择图片”的选项遍历/puzzles/目录下的文件让玩家选择。根据选择的图片动态计算拼图块的大小需确保图片尺寸能被行列数整除。2. 难度系统不仅仅是4x4可以实现3x3简单、4x4中等、5x5困难甚至6x6专家等不同网格尺寸。难度切换可以通过修改PUZZLE_ROWS和PUZZLE_COLS常量并动态调整TileGrid的参数和游戏逻辑数组大小来实现。注意网格变大后打乱算法和胜利检查可能需要优化。3. 游戏数据记录使用open(/stats.txt, a).write(...)的方式将玩家的完成时间、移动步数记录到一个文本文件中。可以增加一个“历史最佳成绩”界面在游戏开始时读取文件并显示最快完成时间和最少步数。注意CircuitPython的文件操作是追加模式长时间运行可能会写满存储。可以设计一个简单的循环记录只保留最近10次成绩。4. 音效与震动PyBadge具备一个小型蜂鸣器可以通过pwmio或audioio播放简单的音效。为移动、成功、失败等动作添加不同的提示音。虽然PyBadge没有震动马达但你可以通过快速开关屏幕背光来模拟一个视觉反馈。5. 更精美的UI使用adafruit_display_text或adafruit_bitmap_font库来显示更漂亮的文字而不是简单的位图文字。为菜单选项添加滚动效果、高亮动画。在拼图过程中为当前选中的图块如果有选中功能添加一个闪烁的边框效果。6. 移植到其他CircuitPython设备这个项目的核心代码游戏逻辑、状态机、displayio和keypad的使用是通用的。要移植到其他屏幕尺寸的开发板如PyGamer, Matrix Portal等你只需要修改屏幕初始化参数尺寸、旋转、总线类型。按键引脚定义。图片资源尺寸以适应新屏幕。可能还需要调整TileGrid的瓦片大小和行列数。这个项目就像一把钥匙帮你打开了用CircuitPython在嵌入式硬件上开发完整交互应用的大门。理解了这些概念后你完全可以举一反三开发出属于自己的贪吃蛇、打飞机甚至更复杂的RPG游戏。最重要的是动手去试把代码烧录进去看着它运行起来遇到问题再逐个击破这个过程本身就是最大的乐趣。