世風日下啊,大家現在動不動就打飛機。。。
上個週末實在是無聊,就寫了個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。。不太會用,傳上去後再給出地址下載哈