小弟我也做雷电
刚做完雷电小游戏,现在总结一下。雷电小游戏中我想核心的东西就是多线程的运用,做雷电就是为了能够熟练的使用多线程,加深对线程执行过程的理解。至于现在对于多线程有了一定的了解,但不能说已经记载了血液中,能够使用多线程做出雷电,是趁热打铁的原因,如果在过几天做的话,我估计就会麻烦很多了。所以自己不看书不行。
正在执行的程序成为进程,在一个进程中,有多个线程,其中有个主线程,就是主函数,这个主线程负责启动整个程序;程序里面还存在很多其他的线程,比如负责刷新的线程,加背景音乐的线程,在雷电中有发射子弹的线程,敌机进攻的线程等。他们的启动有对象调用start();方法开始,这些线程所要执行的内容放在run();方法中。
下面说一下做雷电游戏。这个雷电中,飞机的移动和子弹的发射可以同时进行,所以每一个都应该是线程,对方的飞机(不止一个,放在一个线程里面)对方飞机发射子弹(也放在一个线程里面)。这是最初的想法,很显然是对的,后面再加一句就是,由飞机来启动子弹线程,飞机的位置就是子弹的初始位置。当然一开始就是飞机移动一下就发射一个子弹,结果就是子弹太快了,然后改进了一下,飞机移动10下在发射一个子弹,这样看起来就比较舒服。
现在说一下玩家的情况,空格键控制子弹的发射,上下左右键控制飞机的运动,当然用的就是键盘监听器,关于它的使用方式是临时问的同学很简单。里面有三个方法keyTyped();keyPressed();keyReleased();用前两个来控制子弹的发射,空格键按下去发射子弹,弹起来结束发射。
代码
// 重写键盘的方法 public void keyPressed(KeyEvent e) { x0=0; y0=0; isdisappear = false; ImageIcon img,img1; img1=addpic1(); img = addmypic(); // 按空格键控制发射子弹 if (e.getKeyCode() == KeyEvent.VK_SPACE) { //重写设置子弹发射的位置 //调用子弹发射的方法 isstart=true; // mymis.ismisstart=false; // mymis.ismisdis = false; } // 按左键向左移动 if (e.getKeyCode() == KeyEvent.VK_LEFT) { x0=-8; if(isLeftOutbound){ x0=0; } if(x<=0){ isLeftOutbound=true; } if(x>0){ isLeftOutbound=false; } } //按右键 向右移动 if (e.getKeyCode() == KeyEvent.VK_RIGHT) { x0=+8; if(isRightOutbound){ x0=0; } if(x>=650){ isRightOutbound=true; } if(x<650){ isRightOutbound=false; } } //向上移动 if (e.getKeyCode() == KeyEvent.VK_UP) { y0 =-8; if (isUpOutbound) { y0=0; } if (y <= 60) { isUpOutbound = true; } if (y > 60) { isUpOutbound = false; } } //向下移动 if (e.getKeyCode() == KeyEvent.VK_DOWN) { y0 =+8; if (isDownOutbound) { y0=0; } if (y >= 490) { isDownOutbound = true; } if (y < 490) { isDownOutbound = false; } } g.drawImage(img1.getImage(), x, y, 40, 40, null); g.drawImage(img.getImage(), x+=x0, y+=y0,40,40, null); //用空格键控制子弹的发射 if (isstart) { minmis pm = new minmis(g, x, y,false ); Thread pmh = new Thread(pm);// 包装成线程 pmh.start();// 启动线程 mymis.add(pm); try { Thread.sleep(50); } catch (Exception d) { d.printStackTrace(); } } } //用空格键控制子弹的发射情况 public void keyReleased(KeyEvent e) { if(e.getKeyCode()==KeyEvent.VK_SPACE){ isdisappear = false; Drawliskey.isstart=false; // mymis.ismisstart=true; } // Esc键退出 if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { System.exit(0); } }
还有就是重绘的问题,当我们用一个线程去控制界面的刷新,就会出现屏幕狂闪的情况,这时我们就要用到双缓冲,非轻量级组件不需要双缓冲,就是不用update();函数,双缓冲的顺序是update,paint,repaint,这些方法要放在轻量级组件中。在雷电中我是把所有的东西放在了JPanel上,所以,这些方法要在JPanel 的基础去调用。
代码
// 重写绘制自己的飞机 public void paintmyair(Graphics g) { // 重新绘制自己的飞机 ImageIcon myimg = Drawliskey.addmypic(); g.drawImage(myimg.getImage(), Drawliskey.x, Drawliskey.y, 40, 40, null); } // 重写绘制敌人的飞机的方法 public void paintair(Graphics g) { ImageIcon eneair = stealPlane.addpic(); for (int j = 0; j < nair.size(); j++) { g.drawImage(eneair.getImage(), nair.get(j).x, nair.get(j).y, 50, 50, null); } } // 重写绘制敌人子弹的方法 public void paintenemmis(Graphics g) { // 重新绘制敌机的子弹 ImageIcon misimg = enemis.addpicmis1(); for (int i = 0; i < airmis.size(); i++) { g.drawImage(misimg.getImage(), airmis.get(i).x, airmis.get(i).y, 10, 10, null); } } // 重新绘制玩家子弹的方法 public void paintmymis(Graphics g) { // 重新绘制玩家的子弹 ImageIcon mymisimg = minmis.addpicmis(); for (int m = 0; m < mymis.size(); m++) { g.drawImage(mymisimg.getImage(), mymis.get(m).x, mymis.get(m).y, 10, 10, null); } } class MyPanel extends JPanel { // 重写update方法,先将窗体上的图形画在图片对象上,再一次性显示 public void update(Graphics g) { // URL url = this.getClass().getResource("img/15.jpg"); // javax.swing.ImageIcon img1 = new javax.swing.ImageIcon(url); ImageIcon img1; img1 = Drawliskey.addpic1(); g.drawImage(img1.getImage(), 0, 0, 680, 535, null); if (offScreenImage == null) { // 截取窗体所在位置的图片 offScreenImage = this.createImage(680, 535); } // 获得截取图片的画布 Graphics gImage = offScreenImage.getGraphics(); // 获取画布的底色并且使用这种颜色填充画布 // Color c = Color.BLACK; gImage.drawImage(img1.getImage(), 0, 0, 680, 535, null); // gImage.fillRect(0, 0, 780, 640); // // 有清除上一步图像的功能,相当于gImage.clearRect(0, // // 0, WIDTH, HEIGHT) // 将截下的图片上的画布传给重绘函数,重绘函数只需要在截图的画布上绘制即可,不必在从底层绘制 paint(gImage); // 将接下来的图片加载到窗体画布上去,才能看到每次画的效果 g.drawImage(offScreenImage, 0, 0, null); } public void paint(Graphics g) { URL url = this.getClass().getResource("img/15.jpg"); javax.swing.ImageIcon img = new javax.swing.ImageIcon(url); ImageIcon img1; img1 = Drawliskey.addpic1(); g.drawImage(img1.getImage(), 0, 0, 680, 535, null); if (offScreenImage == null) { // 截取窗体所在位置的图片 offScreenImage = this.createImage(685, 535); } // 获得截取图片的画布 Graphics gImage = offScreenImage.getGraphics(); // 获取画布的底色并且使用这种颜色填充画布 // Color c = Color.BLACK; // gImage.setColor(c); // gImage.fillRect(0, 0, 780, 640); // 有清除上一步图像的功能,相当于gImage.clearRect(0, gImage.drawImage(img1.getImage(), 0, 0, 680, 535, null); super.paint(gImage); paintair(gImage);// 重新绘制敌机 paintenemmis(gImage);// 重新绘制敌机的子弹 paintmymis(gImage);// 重新玩家的子弹 paintmyair(gImage); // 重新绘制自己的飞机 // 将接下来的图片加载到窗体画布上去,才能看到每次画的效果 g.drawImage(offScreenImage, 0, 0, null); } }