用 Java Swing 从零实现贪吃蛇的过程记录——游戏循环、碰撞检测、双缓冲消除闪烁、最高分持久化。
游戏循环是整个游戏的核心驱动力。使用 javax.swing.Timer 而非自己写 while 循环——Timer 在 EDT(事件分发线程)上回调,天然与 Swing 的渲染机制同步,避免线程安全问题。
关键参数:Timer(delay, actionListener),delay 初始为 150ms,每吃 5 个食物减少 10ms,最低 60ms——实现难度递增。
蛇的身体用 LinkedList<Point> 存储:
addFirst(newHead) + removeLast()——头部增、尾部删,O(1) 时间复杂度addFirst(newHead),不 removeLast()——身体自然增长直接 paintComponent(Graphics g) 作画时,如果绘制内容多、帧率高,会出现肉眼可见的闪烁。原因:Swing 默认的绘制是直接在屏幕上逐元素绘制,用户能看到中间状态。
解决方案——双缓冲:
new BufferedImage(width, height, TYPE_INT_ARGB)g.drawImage(buffer, 0, 0, null)用户看到的是完整的每一帧,不会有半成品画面。
| 碰撞类型 | 检测方式 |
|---|---|
| 撞墙 | 蛇头坐标 x < 0 或 x >= gridWidth 或 y < 0 或 y >= gridHeight |
| 撞自己 | 遍历蛇身(skip 蛇头),检查是否有坐标与蛇头重合 |
| 吃到食物 | 蛇头坐标与食物坐标重合 |
游戏结束时,将当前分数与历史最高分比较。历史最高分存储在本地文件中:
读取:Files.readString(Path.of("highscore.txt"))
写入:Files.writeString(Path.of("highscore.txt"), String.valueOf(newHigh))
最大收获:这个项目让我真正理解了"双缓冲"的必要性。之前看书上提到双缓冲觉得是高级技巧,直到自己写贪吃蛇看到画面疯狂闪烁,才明白为什么需要它——那一刻的顿悟比看十页书都管用。