多线程—飞机大战(加入播放音乐功能版本)
读者须知:
飞机大战基本功能代码在项目历程专栏中,这里仅展示播放音乐功能如何添加
实现效果:
代码实现:
(一)导入第三方包/库:
这里面具有Player类播放器,可以播放mp3文件,解决了java默认仅支持wav格式文件的问题。
当然如果想要获取wav格式文件也可以,那就下载一个Adobe Audiority软件,在里面转换MP3文件,就可以获得wav格式。
(二)创建FileMusic类储存音乐文件(MP3格式)
package DemoProject.fjm0601;import java.io.File;public class FileMusic {static File fileBack = new File("D:\\CloudMusic\\MP3\\William Black,Linney - Higher Now.mp3");}
(三)创建音乐线程类
3.1 获取音乐线程
3.2 停止音乐线程
package DemoProject.fjm0601;import javazoom.jl.decoder.JavaLayerException;
import javazoom.jl.player.Player;import javax.sound.sampled.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;public class ThreadMusic {static Player playerBack ;static Thread thBack;public static SourceDataLine line;public static Thread getPlayBkTh(File fileBack){try {FileInputStream fsBack = new FileInputStream(fileBack);playerBack = new Player(fsBack);thBack = new Thread() {public void run() {try {playerBack.play();} catch (JavaLayerException e) {throw new RuntimeException(e);}}};return thBack;} catch (FileNotFoundException e) {throw new RuntimeException(e);} catch (JavaLayerException e) {throw new RuntimeException(e);}}public static void stopBk(){playerBack.close();}}
将player对象变为静态以跨类调用
(四)在GameWD类中的paint()方法中,在state == 1 的时候,获取线程并开始播放
播放状态由isPlay控制(一开始默认false),当运行到isPlay == false,则播放,并改值为true。
public void paint(Graphics g){BaseBoard=createImage(getWidth(),getHeight());//creatImage()这个方法返回的是VolatileImage对象或者是未初始化的Image,很有可能会丢失,所以这不稳定Graphics gImage=BaseBoard.getGraphics();if(state==0){gImage.drawImage(DemoProject.fjm0601.GameWDUtils.bgObj,0,0,null);paint0(gImage,this);}else if(state==1){if(isPlay == false){
* thBack.start();System.out.println("此处可再次运行");isPlay = true;}objectList.removeAll(removeList);removeList.clear();objectList.addAll(addList);addList.clear();for(GameObj object:objectList){object.paintSelf(gImage);}gImage.setColor(Color.CYAN);gImage.setFont(new Font("幼圆",Font.BOLD,20));gImage.drawString("获得分数"+" "+Score,30,70);}g.drawImage(BaseBoard,0,0,null);}
(五)在GameWD的paint2()方法中,改isPlay为false,并销毁线程,停止播放。然后再次创建播放线程,以备“点击重新开始游戏”后能再度播放。
public static void paint2(GameWD gm){isPlay = false;//线程在这里死亡,无法重生(start)thBack.interrupt();//仅仅使用interrupted是无法使用阻断player的play方法的,因为play听不见interrupted方法。ThreadMusic.stopBk();thBack = new Thread() {public void run() {try {ThreadMusic.playerBack = new Player(new FileInputStream(FileMusic.fileBack));ThreadMusic.playerBack.play();//player关闭后不可重置和线程一样都只有一次生命} catch (JavaLayerException e) {throw new RuntimeException(e);} catch (FileNotFoundException e) {throw new RuntimeException(e);}}};Graphics gImage = gm.getGraphics();gImage.setColor(Color.BLACK);gImage.fillRect(0,0,gm.getWidth(),gm.getHeight());gImage.setColor(Color.RED);gImage.setFont(new Font("幼圆",Font.BOLD,40));gImage.drawString("Game Over",200,200);gImage.setColor(Color.CYAN);gImage.drawString("查看排行榜",200,580);//创建文本输入框和确定按钮JTextField jtf = new JTextField();gImage.setFont(new Font("幼圆",Font.BOLD,40));gImage.setColor(Color.WHITE);//drawString的y是字符的底部y坐标gImage.drawString("请点击下方,输入您的姓名",50,400);//JTextField的y是文本输入框顶部的y坐标jtf.setBounds(150,400,300,25);JButton confirmBtn = new JButton("确定");confirmBtn.setBounds(240,435,100,40);confirmBtn.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {String name = jtf.getText();try {writeFile(name,Score);} catch (IOException ex) {throw new RuntimeException(ex);}}});gm.add(jtf);gm.add(confirmBtn);JFrame[] jFrame = new JFrame[1];int[] currentTimes = {0};int[] previousTimes = {0};gm.addMouseListener(new MouseAdapter() {@Overridepublic void mouseClicked(MouseEvent e) {super.mouseClicked(e);int x = e.getX();int y = e.getY();createRankJf(x,y,currentTimes,previousTimes, jFrame);}});//创建查看排行榜图片://gImage.drawImage(GameWDUtils.RankObj,200,480, null);}
问:为什么不将创建线程这个行为封装成一个方法?
答:因为创建方法后,将旧的变量名传入,并且赋入新值,原先的引用并不会修改为指向新的内存地址。因为引用传参是修改不了原先的引用的。
解决方法:将void方法修改为具有返回值的方法,在方法里修改引用后返回,就可以获得新的且与原来变量名一致的线程了