Java项目2——增强版飞机大战游戏 我们要对第一版的飞机大战游戏进行修改,发现了第一版的飞机大战游戏代码里的各种不合理性,比如音乐处理逻辑代码和游戏主类代码混淆,显得非常混乱,其次游戏开始没有一个按钮,随处可见的画面切换,这种没有什么高级感,要想要高级感就得加几个按钮,其次是没有游戏暂停,这次要加入一个暂停功能,并且可以绘制发光字体,下面主要列出几个经过修改的Java文件:1.首先是把音乐处理逻辑代码和游戏主类代码进行一个分离操作:packageorg.example.audio;importorg.example.GamePanel;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importjava.io.File;importjava.net.URL;importjava.util.*;importjava.util.jar.JarEntry;importjava.util.jar.JarFile;publicclassAudioFileFinder{publicstaticfinalListURLmusicUrls=newArrayList();privatestaticfinalLoggerlogger=LoggerFactory.getLogger(AudioFileFinder.class);publicstaticvoidfindAudioFiles(Stringpath){try{// 获取sounds目录的URL(开发环境或JAR环境)EnumerationURLsoundsDirs=GamePanel.class.getClassLoader().getResources(path);while(soundsDirs.hasMoreElements()){URLsoundsDirUrl=soundsDirs.nextElement();if("jar".equals(soundsDirUrl.getProtocol())){// 解析JAR文件路径StringjarPath=soundsDirUrl.getPath().split("!")[0].replace("file:","");try(JarFilejar=newJarFile(jarPath)){EnumerationJarEntryentries=jar.entries();while(entries.hasMoreElements()){JarEntryentry=entries.nextElement();Stringname=entry.getName();// 过滤sounds目录下的音频文件if(name.startsWith("sounds/")!entry.isDirectory()(name.endsWith(".mp3")||name.endsWith(".wav"))){// 使用类加载器获取资源URLURLaudioUrl=GamePanel.class.getClassLoader().getResource(name);if(audioUrl!=null){musicUrls.add(audioUrl);System.out.println("找到"+musicUrls.size()+"个音频文件");}}}}}elseif("file".equals(soundsDirUrl.getProtocol())){// 开发环境处理(保持不变)Filedir=newFile(soundsDirUrl.toURI());File[]files=dir.listFiles((f)-f.getName().endsWith(".mp3")||f.getName().endsWith(".wav"));if(files!=null){for(Filefile:files){musicUrls.add(file.toURI().toURL());System.out.println("找到"+musicUrls.size()+"个音频文件");}}}}}catch(Exceptione){logger.error("加载音频失败: {}",e.getMessage());}}}AudioFileFinder 类详细解释类作用概述这个 Java 类专门用于扫描游戏资源中的音频文件(.mp3 和 .wav),支持两种环境:开发环境:直接从文件系统加载生产环境:从 JAR 包中加载扫描到的音频文件 URL 会存储在静态列表musicUrls中,供游戏后续使用核心代码解析1. 静态变量定义publicstaticfinalListURLmusicUrls=newArrayList();privatestaticfinalLoggerlogger=LoggerFactory.getLogger(AudioFileFinder.class);musicUrls:存放所有找到的音频文件的 URL(静态共享,全局可访问)logger:日志记录器,用于错误跟踪(SLF4J 接口)2. findAudioFiles 方法publicstaticvoidfindAudioFiles(Stringpath){入参:path指定音频资源目录(示例:"sounds")双环境处理机制场景1:JAR 环境(生产环境)if("jar".equals(soundsDirUrl.getProtocol())){StringjarPath=soundsDirUrl.getPath().split("!")[0].replace("file:","");try(JarFilejar=newJarFile(jarPath)){while(entries.hasMoreElements()){JarEntryentry=entries.nextElement();if(name.startsWith("sounds/")!entry.isDirectory()(name.endsWith(".mp3")||name.endsWith(".wav"))){URLaudioUrl=GamePanel.class.getClassLoader().getResource(name);musicUrls.add(audioUrl);}}}}处理流程:解析 JAR 文件路径(去除 URL 中的file:前缀和!后缀)打开 JAR 文件遍历所有条目过滤条件:路径以sounds/开头非目录文件扩展名为.mp3或.wav通过类加载器获取资源 URL添加至全局列表场景2:文件系统环境(开发环境)elseif("file".equals(soundsDirUrl.getProtocol())){Filedir=newFile(soundsDirUrl.toURI());File[]files=dir.listFiles((f)-f.getName().endsWith(".mp3")||f.getName().endsWith(".wav"));for(Filefile:files){musicUrls.add(file.toURI().toURL());}}处理流程:将 URL 转换为本地 File 对象列出目录中所有音频文件将文件路径转为 URL 格式添加至全局列表错误处理}catch(Exceptione){logger.error("加载音频失败: {}",e.getMessage());}捕获所有异常并记录错误日志使用{}占位符避免字符串拼接(SLF4J 特性)技术亮点双环境自适应自动识别jar://和file://协议无缝切换处理逻辑资源安全加载使用ClassLoader.getResource()确保跨平台兼容性JarFile 使用 try-with-resources 自动关闭实时进度反馈System.out.println("找到"+musicUrls.size()+"个音频文件");(注:实际项目建议改为日志输出)高效文件过滤使用 lambda 表达式简化文件过滤扩展名检查避免冗余文件扫描典型使用场景在游戏初始化阶段调用:// 游戏启动代码中AudioFileFinder.findAudioFiles("sounds");ListURLgameMusic=AudioFileFinder.musicUrls;之后游戏音频系统可直接使用musicUrls中的资源注意事项路径规范:资源目录必须位于类路径下线程安全:musicUrls是静态变量,需注意并发访问日志优化:System.out建议替换为日志分级输出资源释放:JAR 文件资源通过 try-with-resources 确保释放这个设计完美解决了游戏开发中常见的资源加载痛点,通过协议自适应机制实现了开发/生产环境无缝切换,是游戏资源加载的典型实现方案。packageorg.example.audio;importorg.example.GamePanel;importjavax.sound.sampled.*;importjava.io.InputStream;importjava.net.URL;importstaticorg.example.GamePanel.state;publicclassBackgroundAudioPlayer{publicThreadplaybackThread;publicClipcurrentMusicClip;publicintcurrentMusicIndex=0;publicfloatvolume=0.5f;/** * 启动音乐循环播放(线程安全) */publicvoidplayMusicLoop(){if(!AudioFileFinder.musicUrls.isEmpty()){playbackThread=newThread(()-{try{playCurrentMusic();}catch(Exceptione){if(!(einstanceofInterruptedException)){System.err.println("播放失败: "+e.getMessage());}}});playbackThread.setDaemon(true);playbackThread.start();}System.out.println("游戏状态"+state);System.out.println("是否暂停"+GamePanel.paused);}/** * 播放当前音乐(带格式兼容处理) */publicvoidplayCurrentMusic()throwsException{URLmusicUrl=AudioFileFinder.musicUrls.get(currentMusicIndex);try(InputStreamaudioStream=musicUrl.openStream();AudioInputStreamrawStream=AudioSystem.getAudioInputStream(audioStream)){// 自动处理MP3转换(WAV无需转换)AudioFormatbaseFormat=rawStream.getFormat();AudioFormattargetFormat=newAudioFormat(AudioFormat.Encoding.PCM_SIGNED,baseFormat.getSampleRate(),16,baseFormat.getChannels(),baseFormat.getChannels()*2,baseFormat.getSampleRate(),false);try(AudioInputStreampcmStream=AudioSystem.getAudioInputStream(targetFormat,rawStream)){closeCurrentClip();// 释放旧资源currentMusicClip=AudioSystem.getClip();currentMusicClip.open(pcmStream);setVolume(volume);currentMusicClip.addLineListener(event-{if(event.getType()==LineEvent.Type.STOP){// 仅当播放自然结束时切换歌曲(非暂停且播放位置已达末尾)if(!GamePanel.paused.get()currentMusicClip.getFramePosition()=currentMusicClip.getFrameLength()){currentMusicIndex=(currentMusicIndex+1)%AudioFileFinder.musicUrls.size();try{playCurrentMusic();}catch(Exceptione){thrownewRuntimeException(e);}}}});currentMusicClip.start();// 阻塞直到播放完成(替代同步锁)while(currentMusicClip.isRunning()){Thread.sleep(100);}}}}/** * 设置音量(分贝转换) */publicvoidsetVolume(floatvolume){this.volume=volume;if(currentMusicClip!=nullcurrentMusicClip.isControlSupported(FloatControl.Type.MASTER_GAIN)){FloatControlgainControl=(FloatControl)currentMusicClip.getControl(FloatControl.Type.MASTER_GAIN);floatdB=(float)(Math.log(volume)/Math.log(10)*20);dB=Math.max(gainControl.getMinimum(),Math.min(gainControl.getMaximum(),dB));gainControl.setValue(dB);}}publicvoidcloseCurrentClip(){if(currentMusicClip!=null){currentMusicClip.close();currentMusicClip=null;}}}2.修改主类代码packageorg.example;importcom.google.common.collect