世风日下啊,大家现在动不动就打飞机。。。
上个周末实在是无聊,就写了个Java版的微信打飞机游戏。。拿上来和大家交流交流,不喜勿喷(网络上这个资源已经很多了很多了)
目前实现的功能:
1、敌方有两类飞机:小飞机和大BOSS飞机(小飞机1滴血;BOSS有10滴血,我方我只设置一滴血,可以修改blood参数)
2、药丸:有蓝色药丸和红色药丸
3:蓝色药丸能双发子弹 (有时间限制)
4:红色药丸全屏爆炸 屏幕下方显示已有的红色药丸数目
5:按键说明:游戏暂停p 游戏继续s 游戏重开始 r 方向键控制上下左右 红色药丸使用空格
先上几副图看看吧(那个。。。飞机爆炸有点土。。。用一陀屎黄色的东西代替哈。。)
(注意)游戏制作中细节的地方:
(1)图一中左下角的显示
(2)双发子弹的设计,如果有一发击中敌机,另一发也应该继续往前走
那个图形美化实在是不善长,就借用微信上的飞机截图做素材哈,,,版权归原作者所有。
------------------------------------------------------------------------
1 简单入门
其实做这个游戏主要你规划好有什么角色,每帧他们都在做什么就好了,然后用什么数据结构去表示(应该说压根就不设计什么算法,数据结构的东西)
在进行冗长的说明之前,我先举个例子,比如如何实现一个物体在界面中随着时间移动(暂时不考虑边界问题)
实现: 1:实时绘制背景 为黑色 2:一红色小球从界面顶部下落 3:随着时间增长,速度增快 4:为了不出先卡吨,实现双缓冲机制 |
首先,我们需要一个JFrame窗口,这个继承就好,然后实现paint()方法,来实现我们的绘制,制作背景简单,可是为什么要实时更新呢?
因为这里我们所看见的动画就是每帧图像快速拼接起来的,而每幅图像都需要我们重新绘制,否则都是在原有图像上再次绘制,所以,如果每次都不刷新界面,那么会有上一次的滞留图像。
paint方法如下:
@Override public void paint(Graphics g) { //super.paint(arg0); //before draw, move first time++; y += velocity + time; //draw Color c = g.getColor(); g.setColor(Color.BLACK); g.fillRect(0, 0, 480, 800); g.setColor(Color.RED); g.fillOval(x, y, 10, 10); g.setColor(c); }
可是,如果绘制物体过多,我们会发现,存在卡顿现象,这也是所有显示都会遇到的问题,一般采用双缓冲方式解决,我们这里不是什么大型游戏,就简单用下面方法实现双缓冲机制,即我们先绘制好下一帧图像,再显示,而不是边显示边绘制。
我们在先申明一个公共变量Image buffer;专门存储下一帧图像,并且在update()函数里实现绘制
@Override public void update(Graphics g) { //super.update(g); if(buffer == null) buffer = this.createImage(480, 800); Graphics gBuffer = buffer.getGraphics(); paint(gBuffer);//先绘制在缓冲中 g.drawImage(buffer, 0, 0, null); }
当然,这里update()传入的g参数就是我们这个窗口的画笔。
随着时间增加,速度增快,这个在paint()函数里已经简单实现了。。
然后就是怎么实现实时绘制,当然是需要开一个定时器啦。。那就开一个线程专门负责更新图像不久好了?
这里关键的更新就是利用mf.update(mf.getGraphics());然后就调用我们刚才实现的update(),然后里面再调用paint()
class MainThread implements Runnable{ MainFrame mf = null; public MainThread(MainFrame mf) { this.mf = mf; } @Override public void run() { while(true){ try{ Thread.sleep(50); mf.update(mf.getGraphics()); }catch(Exception e){ e.printStackTrace(); } } } }
上面这个流程很清晰了,就是让线程负责时间刷新,然后通知窗口,要更新啦!!窗口对象调用更新函数,更新函数通知逻辑绘制函数paint(),要更新啥,你说了算。。。所以把这个程序分为两部分:
UI更新:逻辑更新(目前都是在paint()里面具体实现的)
所以这个程序一封装,以后我们要先游戏,不久只需要在paint()函数里做手脚不就行了么?
下面给出这个小demo的代码
package ylf.graphics; import java.awt.Color; import java.awt.Graphics; import java.awt.Image; import javax.swing.JFrame; /** * @author ylf */ public class MainFrame extends JFrame{ int time ; int velocity ; int x ; int y; Image buffer = null; public MainFrame() { init(); this.setSize(480, 800); this.setVisible(true); new Thread(new MainThread(this)).start(); } public void init(){ time = 0; velocity = 0; x = 240; y = 0; } @Override public void paint(Graphics g) { //super.paint(arg0); //before draw, move first time++; y += velocity + time; //draw Color c = g.getColor(); g.setColor(Color.BLACK); g.fillRect(0, 0, 480, 800); g.setColor(Color.RED); g.fillOval(x, y, 10, 10); g.setColor(c); } @Override public void update(Graphics g) { //super.update(g); if(buffer == null) buffer = this.createImage(480, 800); Graphics gBuffer = buffer.getGraphics(); paint(gBuffer);//先绘制在缓冲中 g.drawImage(buffer, 0, 0, null); } class MainThread implements Runnable{ MainFrame mf = null; public MainThread(MainFrame mf) { this.mf = mf; } @Override public void run() { while(true){ try{ Thread.sleep(50); mf.update(mf.getGraphics()); }catch(Exception e){ e.printStackTrace(); } } } } public static void main(String[] args) { MainFrame m = new MainFrame(); } }
2 打飞机逻辑
好了,能自己实现上面这段就说明:你已经会打飞机啦!
下面教你怎么打更comfortable
继续刚才的,既然我们可以把飞机的逻辑业务都抽取出来,那么这个Frame类我们就不去打扰了,我们独立出一个Controller的类,该类负责游戏业务控制,比如我方飞机的对象持有,敌方一群飞机对象持有(用什么数据结构?当然就用链表呗。。)该逻辑也就是控制分数,还有游戏开始,暂停以及重新开始一些外围以及游戏运行的整体逻辑。
我把Controller的onDraw()函数贴出来,大家就清楚这个游戏逻辑
定期安排敌人飞机出现
定期安排药丸出现
刷新我们飞机位置 状态
刷新敌人飞机位置 状态
刷新爆炸动画
刷新药丸个数显示
刷新分数
public void onDraw(Graphics g) { ///////游戏逻辑///////////// //schedular produce plane-other //定期安排出飞机,出飞机的类型有飞机工厂来生成 if((++readyOther)%10==0){ readyOther = 0; Random rand = new Random(); OtherPlaneFactory.getPlanes(rand.nextInt(2), others, this); } //schedular produce equipment //定期安排药丸出现 其实这里也可以弄一个药丸工厂 if((++readyPowerful)%600==0){ Random rand = new Random(); if(rand.nextBoolean()) equipments.add(new PowerEquipment(rand.nextInt(MainFrame.FRAME_WIDIH), 0, this)); else equipments.add(new BombEquipment(rand.nextInt(MainFrame.FRAME_WIDIH), 0, this)); } /////下面就是把逻辑分配给这些游戏角色了 Color oldColor = g.getColor(); myPlane.onDraw(g); //我方飞机逻辑控制 for(int i=0;i<others.size();i++) others.get(i).onDraw(g); //敌方飞机逻辑控制 for(int i=0;i<explosions.size();i++) explosions.get(i).onDraw(g); //爆炸的绘制 // 下面这段是为了实现界面左下角的药丸个数提醒的绘制,实现方法有很多,我这里粗略实现了 int bombNum = 0; for(int i=0;i<equipments.size();i++){ Equipment e = equipments.get(i); e.onDraw(g); if(e.getType() == Equipment.TYPE_BOMB){ if(!((BombEquipment)e).isLive()){ bombNum++; } } } if(bombNum != 0){ g.drawImage(bombNumImg, 10, MainFrame.FRAME_HEIGHT-50, null); g.drawString(""+bombNum, 85, MainFrame.FRAME_HEIGHT-22); } //绘制分数 g.setColor(Color.BLACK); g.drawImage(pauseFace, 10, 30, null); g.drawString(""+score, 60, 50); g.setColor(oldColor); }
3 打飞机的角色
主要的还是角色的设置才是学习java的途径
如何把java的一些设计原则使用的好就在这里。
接口的封装和字符类的继承,我觉得最好能用接口就使用接口,还有功能不能定的太死,最好能用插拔接口的形式。
例如某个物体是需要能运动的。我们不需要对所有角色都继承一个有onMove()的父类,而是可以用实现了Moveable这个接口。这种灵活性比使用类好的多。当然我实现上也有许多缺陷,仅供参考:下载地址
http://download.csdn.net/detail/ylf13/6870955
还在弄git。。不太会用,传上去后再给出地址下载哈