ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Java赛车游戏源码实战:Swing主循环、碰撞检测与敌车AI解析

Java赛车游戏源码实战:Swing主循环、碰撞检测与敌车AI解析 简介Java赛车游戏源码是一份面向Java入门者与游戏开发初学者的完整2D赛车项目适合在课程设计或自学练手中理解面向对象编程的实际落地。项目围绕一辆可控赛车在赛道上的行驶与避障展开涉及Swing/JavaFX界面绘制、键盘事件监听、游戏循环、碰撞检测与帧动画等核心机制代码对类与对象的抽象、异常处理和文件读写也有示范能够帮助读者把语言基础连贯成可运行的游戏程序。压缩包内共16个文件由1个Java源文件、2个已编译的class文件以及13张PNG格式的赛车、道路和爆炸效果图片构成解压后即可对照学习代码与图像资源的组织方式整个压缩包仅212KB非常轻量。目前已有532人浏览/学习对于想快速获取一个可运行案例并拆解游戏开发思路的Java学习者来说具有不错的参考与复用价值。1. Java赛车游戏源代码课程设计的重灾区也是最值得拆的实战样本把 Java 赛车游戏源代码当课程设计题目来拆第一反应是画面能跑就行真跑起来才发现坑在别处主循环没锁帧率导致机器越快车越快、碰撞框比车图大一圈导致没撞上却判定死亡、敌车 AI 参数调猛了直接变成碰碰车。这份源码走 Swing Java 2D 路线纯 JDK 就能编译运行核心代码集中在游戏循环、场景滚动、矩形碰撞检测和敌车 AI 四块正好对应封装、继承、状态机这些 java 基础高频考点。适合三类人找 java 课程设计案例源码的学生、想拿实战项目应对 java 面试的人、想看看 java 基础语法怎么组织进完整工程的初学者。能跑只是及格线真正值钱的是帧率控制、碰撞箱缩进、难度曲线这些答辩里能展开说的细节。2. 技术选型与游戏主循环Swing Java 2D 怎么撑起一个能真正“玩”的赛车游戏2.1 渲染、输入、逻辑更新职责拆分明细拿到源码先别急着 run把类结构扫一遍比什么都重要。正经课设级别的赛车游戏不会把所有代码塞进一个 JFrame 里而是把职责拆成三块绘制归绘制、逻辑归逻辑、输入归输入。这份源码的结构基本是 GamePanel 负责渲染和按键监听GameLoop 线程负责每帧 updateEnemyManager 和 PlayerCar 各管各的数据。拆开了你才好改不然你调一个碰撞参数要在五个类里翻来找去。模块核心类职责渲染GamePanel继承 JPanelpaintComponent 里画道路、车辆、UI 文字输入KeyListener / KeyBindings采集方向键按下与松开的状态逻辑更新GameLoop推进速度、位移、碰撞检测、敌车 AI资源管理Images / Audio 工具类图片和音效的加载、缓存、回收这个拆分反推面试题特别好用。面试官问“高内聚低耦合怎么体现”你就拿这个项目说GamePanel 不知道敌车 AI 怎么算的它只管把对象的位置画出来GameLoop 不知道玩家按了哪个键它只读一个 keys 数组。数据流是单向的改一处不影响另外两处。选型上为什么不碰 libGDX 或 JavaFXlibGDX 功能强但引入了一大堆依赖和构建配置课设答辩时老师大概率要问“你为什么用这个库、它底层怎么工作”几句话很难讲清楚。JavaFX 在 Oracle JDK 8 之后就从标准 JDK 里剥离了课设机器上不一定装了 runtime。Swing 随 JDK 自带Graphics2D 的 API 画矩形、贴图、旋转都够用做 2D 赛车游戏完全足够这也是大量 Java 赛车游戏源代码默认选它的核心理由。2.2 主循环用固定时间步长替代 Thread.sleep彻底告别“机器越快车越快”很多入门版本的写法是while(true) { update(); repaint(); Thread.sleep(20); }这种写法最典型的翻车现场是同一份代码你室友的 i5 跑起来车速 180km/h你的 i9 跑起来直接飞出去。原因很简单Thread.sleep(20)只是让线程休息 20 毫秒并不保证一秒钟执行 50 次 update主频高的机器每秒多跑几十轮逻辑车速自然被拉爆。正确做法是固定时间步长用真实流逝的时间换算成逻辑帧数再执行 update。这份源码里的 GameLoop 是这么做的public class GameLoop implements Runnable { private final GamePanel panel; private volatile boolean running true; private static final double TICK_RATE 60.0; private static final double FRAME_TIME 1_000_000_000.0 / TICK_RATE; public GameLoop(GamePanel panel) { this.panel panel; } Override public void run() { long last System.nanoTime(); double acc 0; while (running) { long now System.nanoTime(); acc (now - last) / FRAME_TIME; last now; // 累积满一帧才执行一次逻辑更新保证任何机器上速度一致 while (acc 1.0) { panel.update(); acc - 1.0; } panel.repaint(); Thread.yield(); } } }核心逻辑在 acc 这个累积器上。每次循环用System.nanoTime()取高精度时间算出距离上一次循环过去了多少个逻辑帧存进 acc。只有当 acc 大于等于 1 时才执行一次 update这样不管你的机器是 60Hz 还是 240Hz 的刷新率每秒执行的逻辑更新次数都稳定在 60 次。Thread.yield()是让出 CPU 给其他线程避免这个无限循环占满整个核心更精细的做法是用LockSupport.parkNanos睡到下一帧但课设场景下 yield 已经够用。这里两个参数值得记一下TICK_RATE是逻辑帧率60 是标准值调成 30 会让游戏整体变慢但逻辑更省 CPUFRAME_TIME是每帧的纳秒数1000 毫秒除以 60 约等于 16.67 毫秒。注意这个 16.67 是逻辑帧的时间预算不是渲染帧的时间渲染交给 repaint 后由 Swing 自己调度。注意如果你发现下载的源码里主循环是while(true) { update(); repaint(); }不带任何时间控制那不管车辆物理参数写得多漂亮都是白搭先把这个循环改成固定时间步长再看其他问题。2.3 绘制管线paintComponent 与双缓冲的正确用法绘制这块最常见的姿势是在 JPanel 里重写 paintComponent所有画图代码统一从这一个入口走。这份源码的绘制顺序是有讲究的先画道路再画敌车和玩家车最后画得分和道具栏顺序反了就会出现车被路肩遮挡的违和感。Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d (Graphics2D) g; drawRoad(g2d); // 1. 道路底图 路肩 标线 drawCars(g2d); // 2. 敌车 玩家车 drawScore(g2d); // 3. UI 与 HUD g2d.dispose(); }每个 draw 方法内部只做一件事读对象属性把图片或矩形画到对应坐标。比如drawCars里遍历敌车列表对每辆敌车调用g2d.drawImage(carImage, e.x, e.y, null)玩家车单独画因为要叠加氮气特效。这样拆分的好处是以后你想加一个道具层只需要在drawCars和drawScore之间插一个方法不用动其他代码。关于双缓冲这里有个认知误区。Swing 的 JPanel 默认就是双缓冲的你直接在 paintComponent 里写绘制代码就行不需要自己再搞一个 BufferedImage 把画面先画进内存再一次性 drawImage 上去那反而多余相当于画了两遍。真正需要自己控制 BufferStrategy 的场景是你不走 JPanel、直接在 JFrame 上作画那个后面避坑章节会细说。这个项目里你只要记住继承 JPanel、重写 paintComponent、不要碰 getGraphics绘制部分基本不会出问题。3. 核心玩法拆解场景滚动、碰撞检测与敌车 AI参数是怎么定下来的3.1 道路滚动模型roadOffset 怎么换算成像素位移2D 赛车游戏里“自己在前进”的感觉完全是个错觉玩家的车永远停在屏幕中央靠下一点的位置真正动的是整条道路和周围物体。源码里用了一个roadOffset变量记录累计位移每帧把它的值按速度增加绘制时所有世界坐标的 y 值都要加上这个偏移量再上屏。理解了这个模型后面调速度、调敌车密度就都有了坐标系。public void update() { // 玩家不按加速时车速缓慢衰减但保留基础巡航速度 if (upPressed) { speed ACCEL; } else { speed * FRICTION; speed Math.max(speed, BASE_SPEED); } speed Math.min(speed, MAX_SPEED); roadOffset speed; // 敌车相对玩家向后移动相对速度决定被超车还是超车 for (EnemyCar e : enemies) { e.y BASE_SPEED speed - e.speed; if (e.y GamePanel.HEIGHT e.height) { respawnEnemy(e); } } }这里你会看到一堆魔数这是赛车游戏手感的关键也是最容易改坏的地方。我一般会先把它们抽成常量放在类的顶部而不是散落在 update 里。推荐的初始值BASE_SPEED 2.0f这是玩家的基础巡航速度单位是像素每帧ACCEL 0.4f每按一次加速键速度增加 0.4 像素每帧FRICTION 0.97f松手后速度每帧乘 0.97衰减速度适中MAX_SPEED 8.0f即每秒约 480 像素横向跨过整个 800 宽的画面需要不到 2 秒节奏偏快但街机感强。从起步到极速需要(8.0 - 2.0) / 0.4 15帧也就是约 0.25 秒手感非常灵敏适合爽快向的课设演示。你想要写实风格就把 ACCEL 降到 0.1提速拉到 2 秒左右但代价是评委等得不耐烦。回头看敌车的移动公式e.y BASE_SPEED speed - e.speed如果敌车速度大于玩家速度这个增量是负的敌车就会向前飘形成反超动画这个细节很多入门源码都没有。注意所有速度参数都是以“像素每帧”为单位的。如果你把第 2 章的 TICK_RATE 从 60 改成 30那这些速度数值必须全部减半否则游戏会以两倍速度运行。这是一条血泪经验改帧率忘了改速度症状和“机器越快车越快”一模一样。3.2 碰撞检测矩形交叠与碰撞箱缩进赛车游戏的碰撞判定九成以上方案是用矩形相交。原因很朴素车是横平竖直的矩形没有旋转角度用 AABB轴对齐包围盒判定既简单又高效。像素级碰撞在这个场景里完全没必要——一张 40x70 的车图逐像素比对每帧要算几十辆车性能开销大而且收益几乎为零因为车与车的轮廓本来就是矩形。public boolean intersects(GameObject a, GameObject b) { // 碰撞箱向内缩进避免图片边缘透明区域触发误判 int ax1 a.x HITBOX_SHRINK_X; int ay1 a.y HITBOX_SHRINK_Y; int ax2 ax1 a.width - HITBOX_SHRINK_X * 2; int ay2 ay1 a.height - HITBOX_SHRINK_Y * 2; int bx1 b.x HITBOX_SHRINK_X; int by1 b.y HITBOX_SHRINK_Y; int bx2 bx1 b.width - HITBOX_SHRINK_X * 2; int by2 by1 b.height - HITBOX_SHRINK_Y * 2; // 两个矩形在 x 轴和 y 轴的投影都相交才算真正碰撞 return ax1 bx2 ax2 bx1 ay1 by2 ay2 by1; }这段代码里最关键的不是那四个不等式而是HITBOX_SHRINK_X和HITBOX_SHRINK_Y这两个缩进值。很多初学者把碰撞框直接设成图片尺寸结果是两辆车明明只是保险杠擦了一下就被判定撞车玩家会觉得很冤枉。原因在于车辆 PNG 贴图往往带透明边缘、反光、尾翼装饰视觉轮廓和逻辑边界不一致。把检测边界向内缩 4 像素图片尺寸 40x70 的车实际碰撞箱变成 32x62玩家需要更“实在”地贴上才会触发碰撞手感明显变好。这四个参数建议一开始就写成常量HITBOX_SHRINK_X 4、HITBOX_SHRINK_Y 4。以后换个宽体车贴图只需要把缩进值调大一点不需要动判定逻辑。另外注意这个判定方法只适用于轴对齐的矩形如果以后加了车辆旋转功能这个方法就要换成分离轴定理但那是另一个故事了。3.3 敌车 AI 与难度曲线简单规则 调参别一上来就写状态机敌车 AI 不用搞得多高级这份源码里的敌车只有两条规则一是随机回归车道中央防止敌车永远贴边导致玩家打不到二是近距离检测到玩家在前面时向反方向让一下模拟避让。两条规则叠加跑起来就像模像样了。public void updateAI() { // 规则一偏离道路太远时概率性回正避免所有车都停在路边 if (Math.random() 0.02 (x ROAD_LEFT || x width ROAD_RIGHT)) { direction (x width / 2 ROAD_CENTER) ? 1 : -1; } x direction * AI_SPEED; x Math.max(x, ROAD_LEFT); x Math.min(x, ROAD_RIGHT - width); // 规则二与玩家距离太近且处于同一车道时向反方向避让 if (playerDistance 120 Math.abs(playerX - x) 80) { direction (playerX x) ? -1 : 1; } }direction只有 -1 和 1 两个值表示向左还是向右调整。规则一里的Math.random() 0.02是每帧 2% 的概率触发回正换算成秒大约是每秒 1.2 次这个频率既能保证敌车分布均匀又不会让它们像神经病一样频繁变道。避让规则里 120 像素是安全距离小于这个距离且横向重叠较大时触发避让注意避让方向是朝玩家车的反方向否则就撞上了。难度曲线一般套这样一张参数表直接按分数区间切换难度等级分数门槛敌车速度生成间隔帧说明Level 102.5180开局车少速慢熟悉手感Level 230003.2120敌车变快密度提升 1.5 倍Level 380004.090密集车流基本不能走神调难度曲线有一个容易踩的坑AI_SPEED绝对值不要比玩家的MAX_SPEED小太多。如果敌车速度只有 1.5 而玩家轻松跑到 8那玩家永远超得过去所有敌车都变成背景板。个人经验是最难级别下敌车速度取玩家极速的一半左右也就是 4.0这样既能让玩家靠技术拉开差距又保留了被前后夹击的紧张感。另外生成间隔低于 60 帧时画面会瞬间变乱建议把 60 帧作为敌车上限的硬底线。4. 从源码到可运行JDK 环境、工程导入与本项目必改的三个参数4.1 JAVA_HOME 环境变量配置与 JDK 版本选择想跑这份源码第一步是把 JDK 装到位不是只装 JRE。网上很多教程只教了 java -version 验证结果下载源码后 javac 一敲提示不是内部或外部命令那就是只配了运行环境没配编译环境。Windows 下安装 JDK 后需要手动配置两个环境变量在系统变量的新建里分别加JAVA_HOMEC:\Program Files\Java\jdk-17 PATH%JAVA_HOME%\bin;%PATH%JAVA_HOME 指向 JDK 安装根目录PATH 里追加 bin 目录是为了让 java 和 javac 命令在任意目录下都能直接调用。配置完要新开一个命令行窗口验证旧窗口不会自动刷新环境变量java -version javac -version两个命令都能输出版本号才算配置成功。至于到底装 JDK 8 还是 JDK 17取决于你下载到的源码写法。这份源码走的是传统 Swing 写法代码里到处是System.out.println、StringBuilder手动拼接没有用到var关键字、没有switch表达式那 JDK 8 完全能编译。如果你在代码里看到var speed 1.0或者List.of(...)这种写法就老老实实上 JDK 17用 JDK 8 编译会报语法错误。判断方法很简单打开源码随便翻几个类看有没有var关键字。4.2 导入 IDE源码根目录、编码与乱码处理IntelliJ IDEA 和 Eclipse 导入普通 Java 项目的套路差不多。IDEA 里走 File → New → Project from Existing Sources选中源码所在目录一路 Next重点看 Project Structure 里 src 目录有没有被标记成 Sources Root颜色要是蓝色而不是普通灰色。接着在 Project Structure → Project Settings → Project 里把 Project SDK 选成本地装的 JDKLanguage Level 跟着 SDK 走就行。Eclipse 用户则是 Import → Existing Projects into Workspace选目录后确保项目里 .classpath 文件能被识别。如果源码没有 .classpath 文件就新建一个 Java Project 再把源码目录拷进 src 下Eclipse 会自动编译。这一节最容易翻车的是编码问题。源码里的中文注释在 Windows 默认 GBK 编码下经常读不出来打开全是乱码甚至编译报错。IDEA 的解决路径是 Settings → Editor → File Encodings把 Global Encoding、Project Encoding、Properties Files 三处统一改成 UTF-8。改完还是乱码那就是源码文件本身被存的不是 UTF-8可以全选代码用 File → File Properties → File Encoding 强制切换重新加载。控制台输出中文乱码是另一件事和源码文件的编码无关。IDEA 里在 Help → Edit Custom VM Options 加一行-Dfile.encodingUTF-8保存重启 IDE控制台里System.out.println打出来的中文注释和日志信息就不会再变问号。Eclipse 则是在 Run Configurations → Arguments → VM arguments 里加同样的参数。4.3 运行前必改的三个参数窗口、速度、生成密度源码导入后先不要急着点运行找到下面三个参数改一下能帮你快速验证这份代码在自己的机器上到底什么手感。这三个参数都在非常显眼的常量区一般类顶部全是 static final 开头的那几行。参数所在位置初始值改成什么效果WIDTH / HEIGHTGamePanel 类800 x 6001024 x 768窗口变大道路宽度自动拉伸速度感不变MAX_SPEEDPlayerCar 或 GameLoop8.05.0极速降低约 40%整体节奏变缓和适合首次试玩演示SPAWN_INTERVALEnemyManager120180敌车生成间隔从 2 秒改成 3 秒开局压力明显减小窗口尺寸改动要注意的是如果代码里道路的左右边界ROAD_LEFT和ROAD_RIGHT是写死的常量而不是按窗口比例计算的那改宽窗口会把道路甩到屏幕外面这一点跑一次就能看出来。MAX_SPEED 降到 5.0 以后敌车难度表里的速度也要等比调整否则会出现玩家车没人家快的尴尬局面。SPAWN_INTERVAL 是敌车生成的帧间隔120 帧即 2 秒单位是帧不是秒改成 180 就是 3 秒。改完这三个参数后跑起来看三件事控制台有没有异常堆栈、碰撞发生时画面上的车贴是否和逻辑判定一致、得分上涨是否平稳。如果跑起来画面一闪而过直接退出多半是窗口初始化位置写在了资源加载之前这个放下一章讲。提示每改一次参数都完全关闭程序再重新启动Swing 应用在 IDE 里的热重载经常残留旧的静态状态改完参数点了运行但跑的还是上一份 class这种问题排查起来最费时间。5. 避坑排查Swing 赛车游戏最容易翻车的五个地方5.1 机器越快车越快帧率没锁住的典型症状现象同一套源码室友电脑上裸眼目测车速 80 码换到你的笔记本上直接飙到 140 码甚至碰撞判定都出现穿透。原因主循环里直接写while(true) { update(); repaint(); }update 的调用频率完全跟随机器性能走性能越强每秒逻辑更新次数越多车辆位移自然越大。解决按第 2 章的 Fixed Timestep 改主循环并且在累积器上加一层上限// 累积器上限 5 帧防止卡顿恢复后一次性补帧造成瞬移 acc Math.min(acc, 5.0);不加这行的后果是当电脑卡了半秒后恢复acc 会累积到 30 多帧理论上游戏要一口气执行 30 次 update 把所有滞后补回来表现就是画面突然瞬移一大截。设上限 5 帧意味着就算卡顿恢复后最多补 5 帧逻辑剩余的延迟直接丢弃画面连贯性远大于精确补帧。5.2 窗口闪烁、画面残影绘制没走 Swing 的绘制管线现象拖动窗口时赛车身后拖出一条很长的残影松开鼠标画面才恢复正常严重时整个画面在抖。原因在 main 线程或 Timer 线程里直接调用panel.getGraphics().drawImage()画图这绕过了 Swing 的 RepaintManager。窗口被拖动或遮挡后Swing 自动重绘区域只知道要重绘背景不知道你的游戏画面上次画在哪里于是旧图残影和新图叠加。解决所有绘制代码统一放进paintComponent(Graphics g)任何需要刷新画面的地方只调panel.repaint()Swing 内部会调度绘制管线。这条路走通之后窗口怎么拖动残影都不会出现。5.3 getGraphics() 返回 null窗口还没显示就开画现象程序启动时立刻抛 NullPointerException定位到panel.getGraphics()这一行返回值是 null。原因JFrame虽然调了setVisible(true)但窗口还没真正显示到屏幕上时组件还没有创建对应的绘制上下文getGraphics()拿不到东西。解决别再用getGraphics()裸画要么走第 2.3 节的 JPanel paintComponent 路线要么在窗口完成显示后使用 BufferStrategyframe.setIgnoreRepaint(true); // 关闭 Swing 自身绘制改用 BufferStrategy frame.setVisible(true); frame.createBufferStrategy(2); BufferStrategy bs frame.getBufferStrategy(); // 每帧绘制先拿 Graphics画完再 show Graphics g bs.getDrawGraphics(); g.drawImage(frameImg, 0, 0, null); g.dispose(); bs.show();注意setIgnoreRepaint(true)和 paintComponent 是互斥的选了 BufferStrategy 就别再往 JPanel 上画了。课设项目我一般建议用 paintComponent代码量少也不会因为忘掉setIgnoreRepaint出现双通道绘制的奇怪闪烁。5.4 方向键按住就连续跳位按键状态没保存现象按一下左方向键车向左跳 20 像素按住不放后越跳越快松开还会再动一两帧才停。原因操作系统对键盘按键有自动重复机制按住左键时keyPressed事件会以很高频率反复触发。如果每次触发就直接修改玩家坐标等于一秒触发几十次位移车自然像瞬移。解决keyPressed 里只记录按键状态不直接动坐标真正的位移在 update 里每帧只消费一次boolean[] keys new boolean[256]; public void keyPressed(KeyEvent e) { keys[e.getKeyCode()] true; // 只记录状态不做位移 } public void keyReleased(KeyEvent e) { keys[e.getKeyCode()] false; } // update() 中每帧读取一次 if (keys[KeyEvent.VK_LEFT]) { player.x - MOVE_SPEED; // MOVE_SPEED 按像素每帧计算 }这个方案把“事件触发”和“逻辑更新”彻底解耦不管系统一秒钟自动重复多少次 keyPressedupdate 里每帧最多移动一次手感就稳定了。更进一步的做法是使用 Swing 的 KeyBindings 机制它能把按键映射到 Action 对象不会因为键盘焦点丢失而失效但改造量稍大课设场景用布尔数组已经足够。5.5 图片读取 FileNotFoundException相对路径不可靠现象源码在你自己电脑上跑得好好的发到同学机器上或者打成 jar 包运行启动就报java.io.FileNotFoundException: res\car.png。原因代码写的是new File(res/car.png)这个相对路径的基点是“当前工作目录”。IDEA 里运行工作目录默认是项目根目录Eclipse 里可能是工程目录打成 jar 后根本没有这个路径基线一变就崩。解决把图片放进源代码资源目录比如 src/resources/images/car.png然后改用类加载器从 classpath 读取try (InputStream in GamePanel.class.getResourceAsStream(/images/car.png)) { BufferedImage img ImageIO.read(in); // img 可缓存下来重复使用 } catch (IOException e) { e.printStackTrace(); }getResourceAsStream的参数以/开头表示从 classpath 根目录查找不依赖当前工作目录。资源打进 jar 包后这个读取方式依然有效你就再也不用操心换电脑换 IDE 导致的路径问题。这是所有 Swing 项目里最值得提前改掉的坏习惯。6. 进阶玩法伪3D路面、粒子加速与排行榜持久化把课设变成面试亮点6.1 伪3D路面一个 focal 参数搞定远小近大赛车游戏的速度感一半来自贴图位移一半来自路面远小近大的收缩。纯 2D 平铺画面怎么看都像玩具但给它加一个投影关系后效果立刻不一样。原理很简单给每个路面物体记录一个世界 y 坐标worldY越远worldY 越小就越靠近消失点缩放比例由焦距决定double focal 320.0; double scale focal / (focal worldY); int sx (int)(centerX (obj.x - centerX) * scale); // 朝向中心收拢 int sy (int)(centerY obj.y * scale); // 向上压缩改动只涉及绘制坐标的计算所有物体的逻辑位置和碰撞检测完全不需要动。focal 取 320 左右手感比较自然太大会让物体横移幅度过大太小会变成鱼眼镜头。6.2 氮气加速与粒子尾焰给加速键加一个简单的粒子系统玩家按上键时每帧往排气管位置追加两个粒子粒子只负责位置、速度和生命周期三个状态生命周期结束回收数组空间。实现一个最小对象池避免频繁 new 对象触发 GC 卡顿课设面试聊到这里就是加分项。6.3 排行榜持久化ObjectOutputStream 写本地排行榜用ObjectOutputStream存 ArrayListScore 类实现 Serializable存到系统用户目录下的 race_score.dat。注意 Score 里的字段要全部可序列化否则运行时抛NotSerializableException。加载时先new File(file).exists()判断文件是否存在不存在就返回空列表避免首跑就崩。这套源码我拆完第一版时就踩过“机器越快车越快”的坑后来做其他桌面小游戏也遇到同样问题。从那以后我拿到任何一份 Swing 游戏代码第一件事都是先找主循环、确认 update 有没有锁帧率第二件事才是看画面和玩法。这两个顺序反了你后面调得再仔细手感也永远不对。希望帮到你。本文还有配套的精品资源点击获取
返回列表