多執行緒遊戲(飛機大戰)的設計與實現
遊戲框架設計
在做一個飛機大戰前,我們首先要確定我們需要建幾個類,這裡我們為飛機,子彈,物品建了類。因為它們有自己的屬性和方法,方便重複建立物件並區分。飛機大戰的窗體介面和監聽器是必不可少的類,然後為了畫面的重繪以及影象的繪製,我們專門建立了一個類繼承容器,在這個類上寫程式來確定如何去畫這個遊戲介面,最後我們還為子彈,飛機,背景分別建立了執行緒類,來控制他們引數(比如座標)的修改。
遊戲規則:
1、通過上、下、左、右按鍵控制飛機移動。
2、按下空格鍵發射子彈進行攻擊。
3、敵機會對你進行攻擊,擊落敵機將獲取積分。
4、打中急救包可回覆生命值並獲取積分。
5、遊戲結束獲取分數和排名。
鍵盤監聽器控制飛機移動
搭好了框架之後我們就試著讓它們具體實現吧。我們首先讓鍵盤的上下左右鍵控制飛機移動。
加鍵盤監聽器的基本步驟:
PlaneJpanel cp = new PlaneJpanel();
cp.addKeyListener(listener);
cp.requestFocus();
public class Listener implements KeyListener, ActionListener,MouseListener
public void keyPressed(KeyEvent e) { // System.out.println("keyPressed"); switch (e.getKeyCode()) { case KeyEvent.VK_UP: bluePlane.y -= 20; if (bluePlane.y < 0) bluePlane.y = 0; // System.out.println("y-"); break; case KeyEvent.VK_DOWN: bluePlane.y += 20; if (bluePlane.y > 900) bluePlane.y = 900; break; case KeyEvent.VK_LEFT: bluePlane.x -= 20; if (bluePlane.x < 10) bluePlane.x = 10; break; case KeyEvent.VK_RIGHT: bluePlane.x += 20; if (bluePlane.x > 980) bluePlane.x = 980; break; case KeyEvent.VK_SPACE: // System.out.println("發射子彈"); addBulletMe(); break; } }
然後我們改寫畫板重繪方法,在容器類的構造方法中載入飛機照片的地址(注意遊戲中的照片儘量都用ps處理為背景透明),然後在重繪方法中畫出飛機,線上程中呼叫repaint()方法即可。
img=new ImageIcon("E:\\workspace\\mayifan\\src\\com\\myf\\plane1102\\BluePlane.png");
if(bluePlane.life>0) buffgr.drawImage(img.getImage(),bluePlane.x-bluePlane.size/2, bluePlane.y-bluePlane.size/2, bluePlane.size , bluePlane.size ,null);
連續背景的繪製和雙緩衝的新增
連續背景的繪製思路是我們在畫布中畫兩張圖(需要上下兩側顏色銜接良好的圖片),他們首尾相接,一張圖的截止是另一張圖的開始,座標迴圈變化,類似於滾動的形式在畫布上畫出,它們座標的修改需要執行緒來控制。那麼什麼是雙緩衝呢?這樣理解,我們先畫出背景,然後再畫出飛機,然後在下一次進入方法時畫出背景覆蓋飛機,再畫出飛機,這會導致飛機的閃爍。解決這個問題的辦法就是在後臺建立一個顯示緩衝區,我們在後臺定義一個畫布,獲取畫筆,先把背景和飛機等全部畫好,再一次性畫在我們要顯示的地方,這樣就解決了屏閃問題。
連續背景:
buffgr.drawImage(img1.getImage(),0, tb3.BackGroundY, tb3.xMax , tb3.yMax-tb3.BackGroundY ,null);
buffgr.drawImage(img1.getImage(),0, 0, tb3.xMax , tb3.BackGroundY ,null);
public void run()
{
xMax=cp.getWidth();
yMax=cp.getHeight();
while(true)
{
BackGroundY+=100;
if(BackGroundY>=yMax)
BackGroundY=0;
try{
Thread.sleep(200);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
雙緩衝:
private Image iBuffer;
public void paint(Graphics g)
{
super.paint(g);
if(iBuffer==null)
{
iBuffer=createImage(cp.getWidth(),cp.getHeight());
buffgr=iBuffer.getGraphics();
}
buffgr.drawImage(img1.getImage(),0, tb3.BackGroundY, tb3.xMax , tb3.yMax-tb3.BackGroundY ,null);
buffgr.drawImage(img1.getImage(),0, 0, tb3.xMax , tb3.BackGroundY ,null);
if(bluePlane.life>0)
buffgr.drawImage(img.getImage(),bluePlane.x-bluePlane.size/2, bluePlane.y-bluePlane.size/2, bluePlane.size , bluePlane.size ,null);
buffgr.drawImage(img4.getImage(),Listener.bulletsMe.get(i).x,Listener.bulletsMe.get(i).y,25,25,null);
g.drawImage(iBuffer, 0, 0, cp.getWidth(),cp.getHeight(),null);
}
}
}
飛機發射子彈的實現方法
我們在按下空格後發射子彈,這裡我們獲取飛機頭部的座標,然後建立子彈物件,給與子彈初始位置,速度,攻擊力,類別等屬性,這些屬性在子彈類的構造方法中都有實現,然後我們把子彈存入ArrayList即可,在子彈執行緒裡修改子彈座標,畫板上重繪畫出子彈,它就運動起來了。這裡貼出子彈類和建立方法,子彈的位置修改通過獨立的子彈執行緒實現,子彈的重繪即和飛機一起畫在緩衝區,這裡不贅述。
public class Bullet {
public int x=400,y=50;
public int harm=10;
public int type=0;//0:我方。1:對方
public Bullet(int x,int y,int harm,int type){
this.x=x;
this.y=y;
this.harm=harm;
this.type=type;
}
}
public void addBulletMe() {
int num = 1 + (int) (Math.random() * 20);
Bullet bullet = new Bullet(bluePlane.x - 15, bluePlane.y - 110, num, 0);
bulletsMe.add(bullet);
}
敵機的實現
我們在開始初始化一些敵機,隨機給它們初始座標,範圍是屏幕後方,在敵機執行緒裡修改它們的座標,在遊戲開始一段時間後,它們會出現在畫面裡,發射子彈的方式和我方飛機相同,區別是它們的子彈是自動新增,新增的方法寫線上程裡,然後在子彈的型別上也要和我方飛機加以區分。同樣,敵機也有它的佇列來存放物件。思路和建立子彈很接近。
for(int i=0;i<7;i++)
{
int rand1 = -(int) (Math.random() * 1000);
int rand = 200 + (int) (Math.random() * 800);
Plane PaperPlane=new Plane(rand,rand1,50,50,2,0);
PaperPlaneArray.add(PaperPlane);
}
public class Plane {
public int x=400;
public int y=400;
public int size=30;
public int life=100;
public int vy=10;
public int ExplodeFlag=0;
public Plane(int x,int y ,int size,int life,int vy,int ExplodeFlag)
{
this.x=x;
this.y=y;
this.size=size;
this.life=life;
this.vy=vy;
this.ExplodeFlag=ExplodeFlag;
}
}
特殊功能物品新增
定義一個Goods類,並定義物品的初始化屬性,執行緒內修改位置座標,飛機可以射擊它們並觸發諸如回血的效果。到這裡,你會發現,其實子彈,飛機,物品的實現思路如出一轍。先是建立物件,賦予一些初始化屬性,再線上程修改引數,最後再畫板上畫出。觸發的特效的實現方法即:線上程修改物品座標後做一個判斷,依次取出我方子彈物件,判斷子彈和物品的距離是否在一定範圍內,如果是,則把子彈和物品的座標修改到很小或很大(即螢幕座標以外,實現在螢幕上消失的效果,然後修改諸如生命值之類的屬性即可。子彈識別敵機位置的方法也是同理。我們沒有銷燬物件,只是把它們挪到了顯示區外面罷了。
for(int i=0;i<3;i++)
{
int rand4 = -(int) (Math.random() * 1000);
int rand5 = 200 + (int) (Math.random() * 800);
Goods goods= new Goods(rand5,rand4,60,1);
goodsArray.add(goods);
}
public class Goods {
public int size;
public int x;
public int y;
public int type; //1:急救包
public Goods(int x, int y , int size ,int type)
{
this.x=x;
this.y=y;
this.size=size;
this.type=type;
}
}
遊戲資料的本地存取
遊戲結束後顯示本局分數,歷史最高分和排名。我們通過IO流的方式在TXT檔案獲取歷史分數,存放到陣列,和本局分數比較,確定排名並插入本局分數,然後再存入本地。
public void result() {
JFrame jf2 = new JFrame();
jf2.setSize(100, 150);
jf2.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
jf2.setLocationRelativeTo(null);
FlowLayout flow = new FlowLayout();
jf2.setLayout(flow);
String str = String.valueOf(score);
JLabel jb = new JLabel("本局得分:" + str);
JLabel jb2 = new JLabel("歷史最高分:" + firstScore);
JLabel jb3 = new JLabel("您的排名:" + rank);
jf2.add(jb);
jf2.add(jb2);
jf2.add(jb3);
jf2.setVisible(true);
}
public void InPut() {
int n = 100;
int[] arr1 = new int[n];
int[] arr2 = new int[n];
try {
File file = new File("E:\\workspace\\mayifan\\src\\com\\myf\\plane1102\\data.txt"); // 存放陣列資料的檔案
BufferedReader in = new BufferedReader(new FileReader(file)); // 把位元組轉為字元,然後可以read
// ,字元緩衝輸入流
String line; // 行資料
// 逐行讀取,並將每個資料放入到陣列中
String[] temp = null;
while ((line = in.readLine()) != null) {
temp = line.split("\t");
for (int j = 0; j < temp.length; j++) {
arr2[j] = (int) Double.parseDouble(temp[j]);
}
}
in.close();
// 顯示讀出的資料
for (int i = 0; i < temp.length; i++) {
//System.out.print(arr2[i] + "\t");
}
for (int i = 0; i < temp.length; i++) {
if (score >= arr2[i]) {
for (int j = temp.length - 1; j >= i; j--) {
arr2[j + 1] = arr2[j];
}
arr2[i] = score;
rank = i + 1;
break;
}
}
firstScore = arr2[0];
}
FileWriter out = new FileWriter(file); // 檔案寫入流
for (int i = 0; i < temp.length + 1; i++) {
out.write(arr2[i] + "\t");
}
out.close();
} catch (IOException e) {
e.getStackTrace();
}
}
一些功能的實現
到這裡基本的遊戲框架都已經搭建完了,然後就逐漸實現一些細節就可以了,比如子彈擊中敵機新增動畫效果;實現大小,種類,攻擊力不同的飛機出現在畫面;新增更多有趣的功能道具;設定不同的關卡(我通過線上程內計時,實現飛機座標和攻擊力的定時更新實現不同關卡的更新和遊戲難度的變化);顯示出血量條和積分值;設定遊戲的的功能按鍵,比如“重新開始”。讀者都可以自己去實現,我在這裡不一一詳解,給大家自由發揮的空間。最後附上幾張遊戲的效果圖。
相關推薦
多執行緒遊戲(飛機大戰)的設計與實現
遊戲框架設計 在做一個飛機大戰前,我們首先要確定我們需要建幾個類,這裡我們為飛機,子彈,物品建了類。因為它們有自己的屬性和方法,方便重複建立物件並區分。飛機大戰的窗體介面和監聽器是必不可少的類,然後為了畫面的重繪以及影象的繪製,我們專門建立了一個類繼承容器,在這
Java多執行緒學習(基礎篇)
1. java對執行緒的支援 java對執行緒的支援主要體現在Thread類以及Runable介面上,他們都位於java.lang包下,無論是Thread類還是Runable介面,它們都有public void run()方法,這個run方法為我們提供了執行緒實際工作時的程式碼,換句
C++11 併發與多執行緒篇(未完成)
從C++11新標準開始,C++語言本身增加了對多執行緒的支援,意味著使用C++可實現多執行緒程式的可移植,跨平臺。 在標準的C++程式中,主執行緒從main()開始執行,我們自己在C++中建立的執行緒,也需要從一個函式開始執行(這個函式叫做初始函式),一旦這個函式執行完
iOS多執行緒筆記(GCD理解)
0x00 先上腦圖 0x01 iOS的三種多執行緒技術 1.NSThread 每個NSThread物件對應一個執行緒,量級較輕(真正的多執行緒)2.以下兩點是蘋果專門開發的“併發”技術,使得程式設計師可以不再去關心執行緒的具體使用問題ØNSOperation/NSOperationQueue 面向物件的
C++多執行緒系列(C++11)-uniqu_lock(四)
Data 2018/11/12 Add By WJB 在多執行緒中,有時候會出現一個方法中又一斷或者多段程式碼需要加鎖,但是並非整個方法程式碼加鎖,那麼我們就需要一個靈活的鎖-unique_lock;說明:unique_lock會降低程式碼執行效率,不推薦使用。 我們接
java多執行緒基礎(synchronize關鍵字)
基礎知識 執行緒:程序(process)就是一塊包含了某些資源的記憶體區域。作業系統利用程序把它的工作劃分為一些功能單元。 執行緒:程序中所包含的一個或多個執行單元稱為執行緒(thread)。程序還擁有一個私有的虛擬地址空間,該空間僅能被它所包含的執行
Android小知識-Java多執行緒相關(Lock使用)
本篇文章已授權微信公眾號 顧林海 獨家釋出 在JDK1.5中新增ReentrantLock類,效果類似於使用synchronized關鍵字實現執行緒間同步互斥,並且在擴充套件功能上也更加強大,比如具有嗅探鎖定、多路分支通知等功能。看下面ReentrantLock的例項程式碼: public cl
ios多執行緒操作(十二)—— 自定義NSOperation實現網路下載後回撥
- (void)main { @autoreleasepool { // 下載圖片的耗時操作 NSURL *url = [NSURL URLWithString:self.urlString]; NSData *data = [NSDa
多執行緒程式設計(python語言)
眾所周知,多執行緒程式設計是一種可以提高整個任務效能的並行處理方式。多執行緒程式設計的主要特點有以下幾個方面,本質上是非同步的;需要多個併發活動;每個活動的處理順序可能是不確定的,或者說是隨機的、不可預測的。這種程式設計任務可以被組織或劃分成多個執行流,其中每個執行
C#Socket多執行緒通訊(聊天室)
網路上的兩個程式通過一個雙向的通訊連線實現資料的交換,這個連線的一端稱為一個socket 一般有兩個端,一個伺服器端(ServerScoket)一個客戶端(ClientSocket) Socket是tcp\ip網路協議介面。內部定義了許多的函式和例程。可以看成是網路通訊的一
java多執行緒總結(系列一)
1、Java中的Thread建立和狀態 (1)新建(New) 建立後尚未啟動。ex:Thread t = new Thread(){//重寫run方法}; (2)可執行(Runnable) 可能正在執行,也可能正在等待 CPU 時間片。包含了作業系統執行緒狀態中的 Running 和 Ready。 (
Java 多執行緒基礎(十一)執行緒優先順序和守護執行緒
Java 多執行緒基礎(十一)執行緒優先順序和守護執行緒 一、執行緒優先順序 Java 提供了一個執行緒排程器來監控程式啟動後進去就緒狀態的所有執行緒。執行緒排程器通過執行緒的優先順序來決定排程哪些執行緒執行。一般來說,Java的執行緒排程器採用時間片輪轉演算法使多個執行緒輪轉獲得CPU的時間片。
基於Spring4+Hibernate4的通用資料訪問層(Dao層)設計與實現!
基於泛型的依賴注入。當我們的專案中有很多的Model時,相應的Dao(DaoImpl),Service(ServiceImpl)也會增多。而我們對這些Model的操作很多都是類似的,下面是我舉出的一些(見名知意,其它自行腦補): 1.save 2.saveAll 3.fin
java 多執行緒學習(Java中Thread 和 Runnable的區別)
今天剛學習java多執行緒 在網上查了幾篇博文學習,個人覺得寫的不錯,但是有的地方有個疑問:Java中Thread 和 Runnable的區別。 然後,在網上查詢相關資料,發現幾乎都是一樣的。 下面貼出相關的程式碼: 如果一個類繼承Thread,則不適合資源共享。但是如果實現了
多執行緒學習(1)
優點:多程序,多執行緒可以讓程式不被阻塞.可以充分利用多核cpu的優勢,提高執行效率 建立方式: (1)通過繼承Thread類,並重寫其中的run方法來出建立 Thread t = new Thread() { public void run() { // 執行 //
多執行緒學習(七)
問題 1.賣票系統(如果每個執行緒執行的程式碼相同,可以使用同一個Runnable物件,這個Runnable物件中有那個共享資料) 解決 public class MultiThreadShareDataStudy { public static void main(String
C#多執行緒基礎(多執行緒的優先順序、狀態、同步)
一、關於多執行緒的優先順序、狀態、同步指令碼如下: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System
java多執行緒知識(1)
基礎概念 1.執行緒和程序: 程序有自己的獨立空間,而執行緒共享程序的空間 執行緒通訊方便,同一程序的執行緒共享全域性變數,靜態資料 多程序更健壯,多執行緒只要有一個執行緒死掉,整個程序也死 2.同步和非同步:同步必須等該方法的呼叫返回 3.並行和
C#非同步多執行緒總結(delegate、Thread、Task、ThreadPool、Parallel、async、cancel)
同步與非同步多執行緒的區別: 1、同步方法卡介面(UI執行緒忙於計算);非同步多執行緒不卡介面(主執行緒閒置,子執行緒在計算) 2、同步方法慢(CPU利用率低、資源耗費少);非同步多執行緒快(CPU利用率高、資源耗費多) 3、同步方法是有序的;非同步方法是無序的(啟動無序、執行時間不確定、結
java多執行緒之(二)鎖
一,鎖 在物件的建立時java會為每個object物件分配一個monitor( 監視器或者監視鎖),當某個物件的同步方法(synchronized methods )被多個執行緒呼叫時,該物件的monitor將負責處理這些訪問的併發獨佔要求。 當一個執行緒呼叫一個物件的同步方法時(sy