主頁 >  其他 > Java實作坦克大戰小游戲(原始碼+注釋)

Java實作坦克大戰小游戲(原始碼+注釋)

2021-04-27 12:30:15 其他

通過本專案能夠更直觀地理解應用層和運輸層網路協議, 以及繼承封裝多型的運用. 網路部分是本文敘述的重點, 你將看到如何使用Java建立TCP和UDP連接并交換報文, 你還將看到如何自己定義一個簡單的應用層協議來讓自己應用進行網路通信

一.主要設計內容:

1、需要有圖形用戶界面,讓用戶能看到游戲給出的反饋,
2、不同隊伍的坦克,須顯示不同的外觀,以區分敵我,
3、坦克能夠開火,攻擊敵方,但不能攻擊隊友,
4、需要有不可被子彈穿透的墻體,以及墻體有可被摧毀和不可被摧毀兩種,
5、敵方坦克有生命值,并非被擊中一次就會爆炸,
6、游戲結束時會有勝利或失敗場景
7、游戲結束后可重新開始,

二. 程式原始碼基本結構


三.設計的環境、方法及措施:

  • 主要設備:電腦,
  • 軟體:eclipse軟體,
  • 方法措施:
  • 利用學到的java基礎應用程式知識實作需求分析上的功能模塊,并在設計與實作時分析遇到的問題并解決,對實作的模塊進行測驗,查找不足,盡量做到滿足用戶要求

四. 實作了哪些功能

此系統是使用Java語言實作坦克大戰游戲程式,玩家通過連接訪問進入游戲,通過操縱坦克來守衛基地,玩家還可以獲得超級武器來提升坦克的屬性,摧毀全部敵方坦克來取得勝利,本系統結構如下:
(1)面板功能:
對雙方坦克、基地、河道、草坪、普通墻與鐵墻等地圖元素,還實作了頁面按鈕功能,玩家可以點擊按鈕來實作相應的功能,

(2)坦克功能:
操作玩家坦克的方法,還設定了超級武器,玩家吃掉后會獲得特殊技能,

(3)子彈功能:
設定了子彈打中不同物體物件產生的不同效果,

五. 運行效果







六. 實作思路

  • 資料存盤表示: 在JPanel繪制影像,統一規定各個方塊的大小為同一大小(如墻壁,坦克之類,子彈除外),從而方便使用二維陣列存盤地圖的各個元素,

  • 關于檢測物體碰撞,這里使用了一個MyImage的父類,將坦克,墻壁
    定義為繼承這個父類的一個類,

class MyImage {
    int width = Game.width;
    int height = Game.height;
    //二維地圖的坐標
    Coord coord;
    //螢屏上的像素坐標
    int x;
    int y;
    MyImage(Coord coord) {
        x = coord.x * width;
        y = coord.y * height;
        this.coord = coord;
    }
    private Rectangle getRect() {
        return new Rectangle(x, y, width, height);
    }
    //碰撞檢測
    boolean isIntersects(MyImage other) {
        return other.getRect().intersects(getRect());
    }
}
  • 影像列印則借助遍歷兩個ConcurrentHashMap分別儲存坦克和其他型別的方塊,將這些方塊使用Map而不是使用陣列是因為管理起來比較方便,而二維陣列則是為了尋路演算法而準備的,防止了頻繁使用上面的兩個Map而導致執行緒鎖的問題,

七. 遇到的問題

ava的按鍵監聽在回應按鍵長按時會有1-2秒的延遲,導致操作手感極差
解決方法:在坦克類中設定一個布爾屬性move以及整形變數key儲存鍵入的按鍵值,創建一個執行緒來回應按鍵,

實作代碼:

//按鍵回應的執行緒類
class MyTankMove implements Runnable{
	public void run(){
		while(flag){
				GetKey(key);
			while(move){//決定是否移動
				try {
					e.printStackTrace();
					Thread.sleep(100);
				} catch (InterruptedException e) {
				e.printStackTrace();
				}
			try {
				Thread.sleep(10);//防止無按鍵時陷入死回圈導致執行緒堵塞
			} catch (InterruptedException e) {
			}
			}
	}

按鍵監聽事件,即給key和move賦值

//按鍵監聽介面
private class KeyBoradListener extends KeyAdapter{
public void keyPressed(KeyEvent e){
	super.keyPressed(e);
	int key = e.getKeyCode();
	if(key<65){//為了實作雙人對戰而設定的,,,
		if(key!=KeyEvent.VK_SHIFT&&!MyTank.isEmpty()){
			MyTank.getFirst().key=key;
			MyTank.getFirst().move=true;
		}
	}
	else{
		if(key!=KeyEvent.VK_G&&!MyTank.isEmpty()){
			switch (key){
			case KeyEvent.VK_W:key = KeyEvent.VK_UP;break;
			case KeyEvent.VK_A:key = KeyEvent.VK_LEFT;break;
			case KeyEvent.VK_S:key = KeyEvent.VK_DOWN;break;
			case KeyEvent.VK_D:key = KeyEvent.VK_RIGHT;break;
			}
			MyTank.getLast().key=key;
			MyTank.getLast().move=true;
		}
	}
}
public void keyReleased(KeyEvent e){
	super.keyReleased(e);
	int key = e.getKeyCode();
	if(key<65){
		if(!MyTank.isEmpty()){
			if(key!=KeyEvent.VK_SHIFT&&key==MyTank.getFirst().key){//避免了同時按兩個以上按鍵后會卡住
				MyTank.getFirst().move=false;
			}
			else{
				MyTank.getFirst().GetKey(key);
			}
		}
	}
	else{
		switch (key){
		case KeyEvent.VK_W:key = KeyEvent.VK_UP;break;
		case KeyEvent.VK_A:key = KeyEvent.VK_LEFT;break;
		case KeyEvent.VK_S:key = KeyEvent.VK_DOWN;break;
		case KeyEvent.VK_D:key = KeyEvent.VK_RIGHT;break;
		case KeyEvent.VK_G:key = KeyEvent.VK_SHIFT;break;
		}
		if(!MyTank.isEmpty()){
			if(key!=KeyEvent.VK_SHIFT&&key==MyTank.getLast().key){
				MyTank.getLast().move=false;
			}
			else{
				MyTank.getLast().GetKey(key);
			}
		}
	}
}
}

專案內的圖片資源如何在專案匯出后也能使用

解決方法:假設在專案的scr檔案夾中建立img檔案夾,在專案的.classpath中加一句(Eclipse),也可以通過設定專案的Modules,將影像檔案夾設定為resources(IDEA),并使用當前類的名.class.getResource("/(檔案名)")).getImage()獲得影像物件,

尋路的實作

實作思路:通過使用一個存盤了地圖內各個元素的二維陣列Game.map,使用廣度優先演算法遍歷出一條路線,將結果存放于堆疊之中,

實作代碼:

 /**
  * 使用廣度遍歷演算法,使用佇列存盤遍歷的節點
  *
  * @return 移動的路徑
  */
 private Stack<Coord> GetPath() {
     Coord target = Game.tanks.get(Game.P1_TAG).coord;
     Queue<Coord> d_q = new LinkedBlockingQueue<>();
     ArrayList<Coord> IsMove = new ArrayList<>();
     d_q.offer(coord);
     IsMove.add(coord);
     Coord last = null;
     boolean flag;
     while (!d_q.isEmpty()) {
         Coord t = d_q.poll();
         int tx = t.x;
         int ty = t.y;
         int i;
         //遍歷所有的方向
         for (i = 0; i < 4; ++i) {
             switch (i) {
                 case Game.UP:
                     ty -= 1;
                     break;
                 case Game.LEFT:
                     tx -= 1;
                     break;
                 case Game.RIGHT:
                     tx += 1;
                     break;
                 case Game.DOWN:
                     ty += 1;
                     break;
             }
             //判斷該點是否可行
             flag = true;
             Coord z = new Coord(tx, ty);
             //檢查是否為目標終點
             if (z.equals(target)) {
                 z.per = t;
                 last = z;
                 break;
             }
             //檢查該坐標是否已經遍歷了
             for (Coord c : IsMove) {
                 if (c.equals(z)) {
                     flag = false;
                     break;
                 }
             }
             if (flag) {
                 //檢查下一格是否可以抵達
                 flag =(Game.map[ty][tx] == Game.BLANK || Game.map[ty][tx] == Game.WALLS);
             }
             //該點可以用
             if (flag) {
                 //將坐標納入已經遍歷的佇列中
                 d_q.offer(z);
                 IsMove.add(z);
                 z.per = t;
                 last = z;
             }
             IsMove.add(z);
             //重新選擇方向遍歷
             tx = t.x;
             ty = t.y;
         }
         //如果沒有四個方向都遍歷完就跳出,說明已經找到了終點
         if (i != 4) {
             break;
         }
     }
     Stack<Coord> coords = new Stack<>();
     while (null != last && last.per != null) {
         coords.push(last);
         last = last.per;
     }
     return coords;
 }

八.網路聯機

客戶端連接上服務器

  • 首先客戶端通過TCP連接上服務器, 并把自己的UDP埠號發送給服務器, 這里省略描述TCP連接機制, 但是明白了連接機制后對為什么需要填寫服務器埠號和IP會有更深的理解, 它們均為TCP報文段中必填的欄位
  • 服務器通過TCP和客戶端連上后收到客戶端的UDP埠號資訊, 并將客戶端的IP地址和UDP埠號封裝成一個Client物件, 保存在容器中
  • 這里補充一點, 為什么能獲取客戶端的IP地址? 因為服務器收到鏈路層幀后會提取出網路層資料報, 源地址的IP地址在IP資料報的首部欄位中, Java對這一提取程序進行了封裝, 所以我們能夠直接在Java的api中獲取源地址的IP
  • 服務器封裝完Client物件后, 為客戶端的主機坦克分配一個id號, 這個id號將用于往后游戲的網路傳輸中標識這臺坦克
  • 同時服務器也會把自己的UDP埠號發送客戶端, 因為服務器自身會開啟一條UDP執行緒, 用于接收轉發UDP包. 具體作用在后面會講到
  • 客戶端收到坦克id后設定到自己的主戰坦克的id欄位中. 并保存服務器的UDP埠號.
    這里你可能會對UDP埠號產生疑問, 別急, 后面一小節將描述它的作用

附上這部分的代碼片段:

//客戶端
public void connect(String ip, int port){
    serverIP = ip;
    Socket s = null;
    try {
        ds = new DatagramSocket(UDP_PORT);//創建UDP套接字
        s = new Socket(ip, port);//創建TCP套接字
        DataOutputStream dos = new DataOutputStream(s.getOutputStream());
        dos.writeInt(UDP_PORT);//向服務器發送自己的UDP埠號
        DataInputStream dis = new DataInputStream(s.getInputStream());
        int id = dis.readInt();//獲得服務器分配給自己坦克的id號
        this.serverUDPPort = dis.readInt();//獲得服務器的UDP埠號
        tc.getMyTank().id = id;
        tc.getMyTank().setGood((id & 1) == 0 ? true : false);//根據坦克的id號的奇偶性設定坦克的陣營
    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        try{
            if(s != null) s.close();//資訊交換完畢后客戶端的TCP套接字關閉
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    TankNewMsg msg = new TankNewMsg(tc.getMyTank());
    send(msg);//發送坦克出生的訊息(后面介紹)

    new Thread(new UDPThread()).start();//開啟UDP執行緒
}

//服務器
public void start(){
    new Thread(new UDPThread()).start();//開啟UDP執行緒
    ServerSocket ss = null;
    try {
        ss = new ServerSocket(TCP_PORT);//創建TCP歡迎套接字
    } catch (IOException e) {
        e.printStackTrace();
    }

    while(true){//監聽每個客戶端的連接
        Socket s = null;
        try {
            s = ss.accept();//為客戶端分配一個專屬TCP套接字
            DataInputStream dis = new DataInputStream(s.getInputStream());
            int UDP_PORT = dis.readInt();//獲得客戶端的UDP埠號
            Client client = new Client(s.getInetAddress().getHostAddress(), UDP_PORT);//把客戶端的IP地址和UDP埠號封裝成Client物件, 以備后面使用
            clients.add(client);//裝入容器中

            DataOutputStream dos = new DataOutputStream(s.getOutputStream());
            dos.writeInt(ID++);//給客戶端的主戰坦克分配一個id號
            dos.writeInt(UDP_PORT);
        }catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                if(s != null) s.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

結論

該游戲是基于Java語言,使用Eclipse軟體開發的一款坦克大戰游戲, 該游戲包括對面板功能、坦克功能、子彈功能的設計,在面板功能中對雙方坦克、基地、河道、草坪、普通墻與鐵墻等地圖元素進行創建并設定其屬性,還實作了頁面按鈕功能,玩家可以點擊按鈕來實作相應的功能,在坦克功能中,設計了操作玩家坦克的方法,還設定了超級武器,玩家吃掉后會獲得特殊技能,在子彈功能中,設定了子彈打中不同物體物件產生的不同效果,另外,還實作了服務器與客戶端的連接,加載關卡等功能,玩家再游戲面板中可以實時查看自己坦克的生命數量和分數以及敵方坦克的數量,基本上完成了設計任務,總體來說,本游戲有一定的邏輯性和復雜性,對玩家有一定的吸引力,

在設計與實作游戲的程序中,遇到一些邏輯問題和技術故障都是在所難免的,例如如何加載地圖關卡和物體物件等、監探坦克與地圖元素是否碰撞等,都是需要完全克服的,該游戲還需要進一步的優化,需要在更大的程度上提升敵方坦克的智能化、在地圖中添加物體物件來增強可玩性等等,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/280561.html

標籤:其他

上一篇:Java貪吃蛇全代碼

下一篇:2021-04-26

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more