线程游戏的实现 2
关于缓冲区
经过线程游戏的初步设计,直接在窗口分层绘制图像时,画面频繁闪烁;是因为分层绘图按代码顺序逐次刷新,清屏与重绘交替出现,形成视觉频闪;因此引入图像缓冲区,所有图层预先绘制至缓冲画布,整帧绘制完毕后一次性输出至窗口,解决闪烁问题。
1.缓冲初始化
所有画面元素都调用 buffG.xxx() 绘制,不要用窗口原始画笔g
// 创建缓冲图片,尺寸与游戏窗口一致BufferedImagebufferedImage=newBufferedImage(800,800,BufferedImage.TYPE_INT_BGR);// 获取缓冲画布画笔,所有绘图统一使用 buffGGraphicsbuffG=bufferedImage.getGraphics();2.最终统一刷新每一帧末尾
// 整帧统一绘制完毕,将缓冲图片贴到窗口g.drawImage(bufferedImage,0,0,null);实现背景图片的滚动效果
准备两张相同的背景图,一张放在可视窗口,一张紧贴在第一张图片正上方,两张同步向下移动,当任意一张图片完全滑出屏幕底部,立刻将它挪到另一张图片的头顶循环往复,实现无限向下滚动
1.设置滚动成员变量
// 两张背景图相同publicImageback;publicImageback2;// 背景纵向偏移publicintbgY=0;// 滚动速度数值越大,滚动越快publicintbgSpeed=2;// 窗口宽高和画布一致publicfinalintWIN_HEIGHT=800;2.构造器加载背景
back=newImageIcon("resources\\Starrysky.JPG").getImage();back2=newImageIcon("resources\\Starrysky.JPG").getImage();3.坐标更新
// 若暂停游戏,不更新角色、子弹位置,仍绘制当前画面if(!listener.isPause){bgY+=bgSpeed;// 图片滑出屏幕底部,坐标归零循环if(bgY>=WIN_HEIGHT){bgY=0;}}4.两张图片的拼接
// 第一张正常下移buffG.drawImage(back,0,bgY,null);// 第二张紧贴第一张正上方,无缝衔接buffG.drawImage(back2,0,bgY-WIN_HEIGHT,null);资源根导入照片
项目文件夹 └── src ├── com/xxx/各类Java代码包(GameThread、GameUI等) └── resources 【必须新建resources文件夹,放在src同级】 └── Starrysky.JPG代码不用写电脑C盘/D盘完整路径,使用资源相对根路径
publicGameThread(Graphicsg,MPlanemp,ArrayList<Bullet>bullets,GameListenerlistener){this.g=g;this.mp=mp;this.bullets=bullets;this.listener=listener;back=newImageIcon("resources\\Starrysky.JPG").getImage();back2=newImageIcon("resources\\Starrysky.JPG").getImage();//第二张初始放在第一张正上方bgY=0;}实现图片按钮
依靠缓冲画布绘制按钮图片,配合鼠标监听坐标判断点击区域,实现游戏内嵌图片按钮,用于开始界面切换游戏画面
1.设置按钮相关成员变量
// 开始按钮图片对象publicImageIconstartImage;// 按钮绘制范围常量,与绘图坐标匹配privatefinalintbtnX=200;privatefinalintbtnY=600;privatefinalintbtnW=200;privatefinalintbtnH=100;2.构造器加载按钮图片
startImage=newImageIcon("resources\\Start Game Button.PNG");3.画布绘制按钮图片
// 在缓冲画布绘制按钮图片,确定按钮显示位置与大小buffG.drawImage(startImage.getImage(),200,600,200,100,null);4.鼠标点击坐标判定逻辑
// 获取鼠标点击坐标,判断是否落在按钮矩形范围内intmx=e.getX();intmy=e.getY();booleanhitBtn=mx>=btnX&&mx<=btnX+btnW&&my>=btnY&&my<=btnY+btnH;// 仅在开始界面、未创建玩家时点击生效,切换游戏画面if(hitBtn&>.start&&mp==null){mp=newMPlane(350,600);gt.setMp(mp);gt.isStart(false);}5.窗口绑定全套鼠标监听
// 绑定鼠标移动监听、鼠标点击监听gamePanel.addMouseMotionListener(listener);gamePanel.addMouseListener(listener);添加背景音乐
Java原生音频类 Clip 仅支持 .wav 无损音频文件,通过自定义音频工具类实现多首音乐自由切换、游戏暂停同步控音功能
1.音频资源准备完成后,在游戏主线程、窗口类中创建音频对象
// 1. 定义背景音乐路径数组String[]musicList={"resources/bg1.wav","resources/bg2.wav","resources/bg3.wav"};// 2. 创建音频工具对象AudioMusicaudio=newAudioMusic(musicList);// 3. 加载并播放第一首audio.loadMusic(audio.currentIndex);audio.playLoop();2.控制音乐切换
(1)键盘按键切换
@OverridepublicvoidkeyPressed(KeyEvente){// F1切换下一首if(e.getKeyCode()==KeyEvent.VK_F1){audio.nextMusic();}}(2)画面图片按钮切换
绘制按钮图片,记录按钮坐标范围;鼠标点击监听中判断坐标,命中则执行切换
@OverridepublicvoidmouseClicked(MouseEvente){intx=e.getX();inty=e.getY();// 判断是否点击音乐切换按钮区域if(x>=10&&x<=110&&y>=10&&y<=60){audio.nextMusic();}}3.游戏暂停/继续同步控制音乐
if(isPause){audio.stopMusic();// 暂停游戏,停止音乐}else{audio.playLoop();// 恢复游戏,继续播放音乐}4.在画布绘制文字提示当前播放音乐
// g为缓冲画笔buffGg.setColor(Color.WHITE);g.setFont(newFont("黑体",Font.BOLD,30));g.drawString("当前背景音乐:第"+(audio.currentIndex+1)+"首",20,60);