<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Java 版植物大戰(zhàn)僵尸思路和源碼分享!

          共 2162字,需瀏覽 5分鐘

           ·

          2020-09-08 08:13


          點(diǎn)擊上方“逆鋒起筆”,公眾號(hào)回復(fù)?PDF
          領(lǐng)取大佬們推薦的學(xué)習(xí)資料
          來源 |?https://urlify.cn/byeEjy
          有誰沒玩過植物大戰(zhàn)僵尸嗎?用Java語言開發(fā)了自己的植物大戰(zhàn)僵尸游戲。雖然系統(tǒng)相對(duì)簡(jiǎn)單,但是麻雀雖小五臟俱全,對(duì)游戲開發(fā)感興趣的小伙伴可以學(xué)習(xí)一下。

          游戲設(shè)計(jì)

          植物大戰(zhàn)僵尸中有一個(gè)小游戲關(guān)卡,屏幕的正上方有一個(gè)滾輪機(jī),會(huì)隨機(jī)生成植物,玩家可以選中植物后自由選擇草坪來進(jìn)行安放?;诖擞螒蚰J?,我將該關(guān)卡抽取出來,單獨(dú)做成了一個(gè)簡(jiǎn)易版的植物大戰(zhàn)僵尸。游戲的畫面大概如下:

          屏幕左側(cè)會(huì)自動(dòng)生成植物的卡牌,單擊選中后可以放置在草坪上。右側(cè)會(huì)自動(dòng)生成僵尸,不同的僵尸移動(dòng)速度不同,血量不同,還有的僵尸有隱藏獎(jiǎng)勵(lì),比如:全屏僵尸靜止、全屏僵尸死亡等。當(dāng)時(shí)竟然沒有做游戲的暫停的功能,導(dǎo)致現(xiàn)在截圖的時(shí)機(jī)很難把控,那這里就先說一下游戲暫停的功能應(yīng)該怎么做吧。
          最簡(jiǎn)單的一種暫停方式是鼠標(biāo)移出屏幕,游戲暫停。所以這里需要引入一個(gè)鼠標(biāo)監(jiān)聽器事件。

          public?void?mouseMoved(MouseEvent e)?{
          ??// 當(dāng)游戲處于運(yùn)行狀態(tài)時(shí)
          ??if?(status == start) {
          ????// 通過鼠標(biāo)移動(dòng)事件的對(duì)象獲取當(dāng)前鼠標(biāo)的位置
          ????int?x = e.getX();
          ????int?y = e.getY();
          ????// 如果鼠標(biāo)超出了游戲界面
          ????if?(x > Game.WIDTH || y > Game.HEIGHT) {
          ??????// 將游戲的狀態(tài)改為暫停狀態(tài)
          ??????status = pause;
          ????}
          ??}
          }

          當(dāng)然,這只是一個(gè)簡(jiǎn)單的通過監(jiān)聽鼠標(biāo)的位置來改變游戲狀態(tài)方法。還可以使用鍵盤監(jiān)聽器,當(dāng)按下某個(gè)鍵時(shí)游戲暫停,這樣的用戶體驗(yàn)更好。但原理是一樣的,這里就不展示代碼了。
          游戲?qū)ο?/span>
          首先分析一下游戲中有哪些對(duì)象。各式各樣的植物,各式各樣的僵尸,各式各樣的子彈。那么這里就可以抽出三個(gè)父類,分別是植物、僵尸、子彈。在面向?qū)ο笾?,子類將繼承父類所有的屬性和方法。所以可以將三大類中,共有的屬性和方法抽到各自的父類中。比如僵尸父類:
          public?abstract?class?Zombie?{
          ??// 僵尸父類
          ??// 僵尸共有的屬性
          ??protected?int?width;
          ??protected?int?height;
          ??protected?int?live;
          ??protected?int?x;
          ??protected?int?y;
          ??......
          ??// 僵尸的狀態(tài)
          ??public?static?final?int?LIFE =?0;
          ??public?static?final?int?ATTACK =?1;
          ??public?static?final?int?DEAD =?2;
          ??protected?int?state = LIFE;
          ??/*
          ??* 這里補(bǔ)充一下為什么父類是抽象類,比如每個(gè)僵尸都有移動(dòng)方法,
          ??* 但每個(gè)僵尸的移動(dòng)方式是不同,所以該方法的方法體可能是不同的,
          ??* 抽象方法沒有方法體,在子類中再去進(jìn)行重寫就可以了,
          ??* 但有抽象方法的類必須是抽象類,因此父類一般都是抽象類
          ??*/

          ??// 移動(dòng)方式
          ??public?abstract?void?step();
          ??....
          }
          植物父類、子彈父類就同理可得了。
          上面說到子類共有的方法需要抽到父類中,那么部分子類共有的方法該如何處理呢?比如,豌豆射手、寒冰射手可以發(fā)射子彈,堅(jiān)果墻就沒有射擊的這個(gè)行為。所以這里就需要用到接口(Interface)。
          public?interface?Shoot?{
          ??// 射擊接口 - 將部分子類共有的行為抽取到接口中
          ??// 接口中的方法默認(rèn)是public abstract的,規(guī)范的編碼應(yīng)該將該字段舍去
          ??public?abstract?Bullet[]?shoot();
          }
          到此為止,游戲?qū)ο蟮膶傩?、方法基本都定義完了,至于圖片的顯示以及如何將圖片畫出來,只需要使用相應(yīng)的API即可,這里就不做描述了。工作一年回過來看看,這里能優(yōu)化的地方還有很多,比如對(duì)象的血量、攻擊力、移動(dòng)等都可以統(tǒng)統(tǒng)寫入到配置文件中,這樣在做游戲參數(shù)的調(diào)整時(shí),不需要去修改代碼相關(guān)的內(nèi)容,只需要修改配置文件里面的參數(shù)即可。

          游戲內(nèi)容

          現(xiàn)在我們有了游戲的對(duì)象,該開始讓對(duì)象加入到游戲中來,接著讓他們動(dòng)起來,最后還得讓他們打起來。首先,讓對(duì)象加入到游戲中來我是這么做的,這里還是以僵尸為例:
          // 首先要有一個(gè)僵尸的集合
          // 僵尸集合
          private?List zombies =?new?ArrayList();
          // 接著定義隨機(jī)生成僵尸方法
          public?Zombie?nextOneZombie()?{
          ????Random rand =?new?Random();
          ????// 控制不同種類僵尸出現(xiàn)的概率
          ????int?type = rand.nextInt(20);
          ????if(type<5) {
          ??????return?new?Zombie0();
          ????}else?if(type<10) {
          ??????return?new?Zombie1();
          ????}else?if(type<15) {
          ??????return?new?Zombie2();
          ????}else?{
          ??????return?new?Zombie3();
          ????}
          }

          // 僵尸入場(chǎng)
          // 設(shè)置進(jìn)場(chǎng)間隔
          /*
          * 這里補(bǔ)充一下為什么要設(shè)置進(jìn)場(chǎng)的間隔
          * 因?yàn)橛螒虻倪\(yùn)行是基于定時(shí)器的,
          * 每隔一段時(shí)間定時(shí)器就會(huì)執(zhí)行一次你所加入定時(shí)器的方法,
          * 所以這里需要設(shè)置進(jìn)場(chǎng)間隔來控制游戲的速度。
          */

          int?zombieEnterTime =?0;
          public?void?zombieEnterAction()?{
          ??zombieEnterTime++;
          ??????// 對(duì)自增量zombieEnterTime進(jìn)行取余計(jì)算
          ????if(zombieEnterTime%300==0) {
          ??????// 滿足條件就調(diào)用隨機(jī)生成僵尸方法,并將生成的僵尸加入到僵尸的集合中
          ??????zombies.add(nextOneZombie());
          ????}
          }
          最早時(shí)候我用的數(shù)據(jù)結(jié)構(gòu)是數(shù)組,但在后續(xù)的編碼中發(fā)現(xiàn),對(duì)僵尸對(duì)象有很多的遍歷以及增刪操作,數(shù)組的增刪操作是十分麻煩復(fù)雜的,所以我就換成了集合。在工作中也一樣,先思考在編碼,選擇正確的數(shù)據(jù)結(jié)構(gòu)往往能起到事半功倍的效果。
          植物入場(chǎng)的設(shè)計(jì),是我當(dāng)時(shí)自認(rèn)為很精妙的一個(gè)點(diǎn)。先說一下當(dāng)時(shí)在編碼中發(fā)現(xiàn)的問題。首先植物入場(chǎng)時(shí)是在滾輪機(jī)上的,滾輪機(jī)上的移動(dòng)就會(huì)涉及到追擊和停止的問題。追擊的方式當(dāng)然是追前一個(gè)植物卡牌,但當(dāng)?shù)谝粋€(gè)植物卡牌被選中放置到草地上后,那該如何追擊呢?
          最開始我的做法是給植物多加幾個(gè)狀態(tài)來解決這個(gè)問題,但是發(fā)現(xiàn)狀態(tài)過多會(huì)導(dǎo)致if判斷中的條件將大大增加,并且在嘗試后還是沒有實(shí)現(xiàn)想要的效果,于是我就將植物集合一分為二,在后面的游戲功能設(shè)計(jì)中,回頭過來看才發(fā)現(xiàn)將植物集合分為滾輪機(jī)上的集合和戰(zhàn)場(chǎng)上的集合實(shí)在是太精妙了。請(qǐng)聽我娓娓道來:
          // 滾輪機(jī)上的植物,狀態(tài)為stop和wait
          private?List plants =?new?ArrayList();
          // 戰(zhàn)場(chǎng)上的植物,狀態(tài)為life和move -move為被鼠標(biāo)選中移動(dòng)的狀態(tài),這里設(shè)計(jì)不合理,會(huì)引發(fā)后面的一個(gè)BUG
          private?List plantsLife =?new?ArrayList();
          // 植物在滾輪機(jī)上的碰撞判定
          public?void?plantBangAction()?{
          ????// 遍歷滾輪機(jī)上植物集合,從第二個(gè)開始
          ????for(int?i=1;i??????// 如果第一個(gè)植物的y大于0,并且是stop狀態(tài),則狀態(tài)改為wait
          ??????if(plants.get(0).getY()>0&&plants.get(0).isStop()) {
          ????????plants.get(0).goWait();
          ??????}
          ??????// 如果第i個(gè)植物y小于i-1個(gè)植物的y+height,則說明碰到了,改變i的狀態(tài)為stop
          ??????if((plants.get(i).isStop()||plants.get(i).isWait())&&
          ??????????(plants.get(i-1).isStop()||plants.get(i-1).isWait())&&
          ??????????plants.get(i).getY()<=plants.get(i-1).getY()+plants.get(i-1).getHeight()
          ??????????) {
          ????????plants.get(i).goStop();
          ??????}
          ??????/*
          ???????* 如果第i個(gè)植物y大于于i-1個(gè)植物的y+height,則說明還沒碰到或者第i-1個(gè)
          ???????* 植物被移走了,改變i的狀態(tài)為wait,可以繼續(xù)往上走
          ???????*/

          ??????if(plants.get(i).isStop()&&
          ??????????plants.get(i).getY()>plants.get(i-1).getY()+plants.get(i-1).getHeight()) {
          ????????plants.get(i).goWait();
          ??????}
          ????}
          ??}
          ??// 檢測(cè)滾輪機(jī)上的植物狀態(tài)
          ??public?void?checkPlantAction1()?{
          ????// 迭代器
          ????Iterator it = plants.iterator();
          ????while(it.hasNext()) {
          ??????Plant p = it.next();
          ??????/*
          ???????* 如果滾輪機(jī)集合里有move或者life狀態(tài)的植物
          ???????* 則添加到戰(zhàn)場(chǎng)植物的集合中,并從原數(shù)組中刪除
          ???????*/

          ??????/*
          ??????* 現(xiàn)在發(fā)現(xiàn)把滾輪機(jī)上move狀態(tài)的植物添加到
          ??????* 戰(zhàn)場(chǎng)上植物集合的最佳操作時(shí)間點(diǎn)應(yīng)該是
          ??????* 等植物狀態(tài)變?yōu)閘ife后再添加。
          ??????* /
          ??????if(p.isMove()||p.isLife()) {
          ????????plantsLife.add(p);
          ????????it.remove();
          ??????}
          ????}
          ??}
          當(dāng)然,滾輪機(jī)上的對(duì)植物狀態(tài)判斷的代碼還是顯得生澀,也正是自己想優(yōu)化這段代碼時(shí)萌生了分享游戲設(shè)計(jì)過程和游戲代碼的念頭。那么下面就說說,這段代碼該如何優(yōu)化:
          // 先對(duì)狀態(tài)做下說明
          // wait - 植物卡牌在滾輪機(jī)上移動(dòng)狀態(tài),因?yàn)槭堑戎皇髽?biāo)選中,所以取名為wait
          // stop - 植物卡牌在滾輪機(jī)上停止?fàn)顟B(tài),有兩種情況,1 - 到頂了 2 - 撞到上一個(gè)卡牌了
          // 開始對(duì)以下代碼進(jìn)行優(yōu)化
          // 如果第i個(gè)植物y小于i-1個(gè)植物的y+height,則說明碰到了,改變i的狀態(tài)為stop
          // if((plants.get(i).isStop()||plants.get(i).isWait())&&
          // (plants.get(i-1).isStop()||plants.get(i-1).isWait())&&
          // plants.get(i).getY()<=plants.get(i-1).getY()+plants.get(i-1).getHeight()
          // ) {
          // plants.get(i).goStop();
          // }
          // 優(yōu)化后的代碼是這樣的
          // 將一個(gè)復(fù)雜的boolean拆成多個(gè)if條件
          if?(!(plants.get(i).isStop()||plants.get(i).isWait()) {
          ??break;
          }
          if?(!(plants.get(i-1).isStop()||plants.get(i-1).isWait())) {
          ??break;
          }
          if?(!(plants.get(i).getY()<=plants.get(i-1).getY()+plants.get(i-1).getHeight())) {
          ??break;
          }
          plants.get(i).goStop();
          boolean條件當(dāng)然也可以進(jìn)行優(yōu)化,甚至還可以簡(jiǎn)化一下植物的狀態(tài)。這里因?yàn)橛螒虻囊?guī)則,僵尸只能攻擊在草坪上的植物,所以把帶放置的植物和草坪上的植物分為兩個(gè)集合,是十分合理精妙的。在判斷僵尸是否攻擊植物,只需要去遍歷草坪上的植物集合即可。如果不拆分,當(dāng)要判斷僵尸是否攻擊植物的時(shí)候,需要遍歷的集合將是所有的植物集合,并且需要增加至少2個(gè)狀態(tài)來區(qū)分植物是在草坪上還是在滾輪機(jī)上,這段代碼想想就是又臭又長(zhǎng)。
          接下來該讓對(duì)象們都動(dòng)起來了。之前說到在父類中的移動(dòng)方法是抽象方法,在各自的子類中都進(jìn)行重寫后,不同的對(duì)象移動(dòng)方式就是各式各樣的了。
          // 子彈移動(dòng)
          public?void?BulletStepAction()?{
          ??for(Bullet b:bullets) {
          ????b.step();
          ??}
          }
          //僵尸移動(dòng)
          //設(shè)置移動(dòng)間隔
          int?zombieStepTime =?0;
          public?void?zombieStepAction()?{
          ??if(zombieStepTime++%3==0) {
          ????for(Zombie z:zombies) {
          ??????//只有活著的僵尸會(huì)移動(dòng)
          ??????if(z.isLife()) {
          ????????z.step();
          ??????}
          ????}
          ??}
          }
          看著代碼中對(duì)集合復(fù)雜的遍歷,不得不感概lambda表達(dá)式真是個(gè)好東西:
          // 子彈移動(dòng)
          public?void BulletStepAction() {
          ??bullets.forEach((b)->b.step());
          ??....
          }
          這里好像還是沒法展示lambda表達(dá)式強(qiáng)大的功能,請(qǐng)看下面的例子:
          // 為了應(yīng)對(duì)產(chǎn)品不斷變更的需求,前輩們總結(jié)經(jīng)驗(yàn)得出的設(shè)計(jì)模式已經(jīng)能在一定程度上應(yīng)對(duì)此問題
          // 設(shè)計(jì)模式,聲明策略接口,在實(shí)現(xiàn)類中完成過濾邏輯
          public?List filterStudentByStrategy(List students, SimpleStrategy strategy){
          ???????List filterStudents =?new?ArrayList<>();
          ???????for?(Student student : filterStudents) {
          ???????????if(strategy.operate(student)){
          ???????????????filterStudents.add(student);
          ???????????}
          ???????}
          ???????return?filterStudents;
          }
          // 當(dāng)需求變更時(shí),只需要在策略接口的實(shí)現(xiàn)類中,變更判斷邏輯即可
          public?interface?SimpleStrategy<T>?{
          ????public?boolean operate(T t);
          }
          但好像還是有點(diǎn)麻煩,又要寫接口,又要寫實(shí)現(xiàn)類,后續(xù)的維護(hù)也是個(gè)頭疼問題,這個(gè)時(shí)候救世主lambda表達(dá)式就出現(xiàn)了:
          // 無需接口便可實(shí)現(xiàn)需求的快速變更
          List lambdaStudents =
          ??students.stream().filter(student -> student.getGender()==1).collect(Collectors.toList());
          讓我們看看上面到底發(fā)生了啥。首先將數(shù)據(jù)的集合流化,接著調(diào)用過濾方法,強(qiáng)大lambda表達(dá)式讓代碼變得簡(jiǎn)潔,并且判斷條件的修改可在代碼中直接維護(hù)無需在策略接口的實(shí)現(xiàn)類維護(hù)。最后在轉(zhuǎn)成集合,返回一個(gè)滿足產(chǎn)品需求的集合。
          回到正題,如何讓對(duì)象們打起來呢?下面以僵尸攻擊植物為例:
          // 僵尸的超類中定義了僵尸的攻擊方法,
          // 由于僵尸們的攻擊行為是相同,所以這里是普通方法
          // 僵尸攻擊植物
          public?boolean?zombieHit(Plant p)?{
          ????int?x1 =?this.x-p.getWidth();
          ????int?x2 =?this.x+this.width;
          ????int?y1 =?this.y-p.getHeight();
          ????int?y2 =?this.y+this.width;
          ????int?x = p.getX();
          ????int?y = p.getY();
          ????return?x>=x1 && x<=x2 && y>=y1 && y<=y2;
          }

          結(jié)合圖片來看,上述代碼應(yīng)該就更好理解。黑框P代表植物,黑框Z代表植物,虛線是指兩者接觸的極限距離,當(dāng)僵尸進(jìn)入虛線內(nèi),就保證可以攻擊到植物。
          // 僵尸攻擊
          // 設(shè)置攻擊間隔
          int?zombieHitTime =?0;
          public?void?zombieHitAction()?{
          ??if(zombieHitTime++%100==0) {
          ????for(Zombie z:zombies) {
          ??????// 如果戰(zhàn)場(chǎng)上沒有植物,則把所有僵尸的狀態(tài)改為life
          ??????/*
          ??????* 這里補(bǔ)充一下為什么要先將所有的僵尸的狀態(tài)先改成life狀態(tài),也就是移動(dòng)狀態(tài)
          ??????* 因?yàn)橄旅鎸?duì)僵尸是否攻擊的植物的判斷,是從遍歷戰(zhàn)場(chǎng)上的植物集合開始的
          ??????* 假如有只僵尸在吃植物,把戰(zhàn)場(chǎng)上唯一的一個(gè)植物吃掉了,
          ??????* 那么僵尸的狀態(tài)將從攻擊改成移動(dòng)呢?
          ??????* 所以這里運(yùn)用了逆向的思想,先將所有的僵尸改為移動(dòng)狀態(tài)
          ??????* 如果符合攻擊的條件,那么再改為攻擊狀態(tài),
          ??????* 即便是戰(zhàn)場(chǎng)上沒有植物,那么僵尸還依然是移動(dòng)的狀態(tài)
          ??????*/

          ??????if(!z.isDead()) {
          ????????z.goLife();
          ??????}
          ??????// 這里應(yīng)該有個(gè)對(duì)戰(zhàn)場(chǎng)上植物集合的判斷在進(jìn)行遍歷
          ??????for(Plant p:plantsLife) {
          ????????// 如果僵尸是活的,并且植物是活的,并且僵尸進(jìn)入攻擊植物的范圍
          ????????/*
          ????????* 這里有個(gè)BUG,僵尸竟然會(huì)攻擊鼠標(biāo)選中還未放下的植物,
          ????????* 所以下面的判斷條件中應(yīng)該還需要移除被鼠標(biāo)選中狀態(tài)下植物
          ????????*/

          ????????if(z.isLife()&&!p.isDead()&&z.zombieHit(p)&&!(p?instanceof?Spikerock)) {
          ??????????// 僵尸狀態(tài)改為攻擊狀態(tài)
          ??????????z.goAttack();
          ??????????// 植物掉血
          ??????????p.loseLive();
          ????????}
          ??????}
          ????}
          ??}
          }

          如果出現(xiàn)了一些效果的偏移,造成的原因是圖片大小不一造成的坐標(biāo)偏移,因?yàn)閳D片都是網(wǎng)上找的,所以效果不是太理想。關(guān)注公眾號(hào) 逆鋒起筆,回復(fù) pdf,下載你需要的各種學(xué)習(xí)資料。
          至此,游戲的基本功能基本實(shí)現(xiàn)了。Java是一門面向?qū)ο蟮恼Z言,萬物皆對(duì)象,特征皆屬性,行為皆方法。肉眼能看到的僵尸、植物、草坪都是對(duì)象,對(duì)象的特性比如血量、移動(dòng)速度都是屬性,對(duì)象的行為比如移動(dòng)、攻擊、死亡都是方法。
          下面說說對(duì)游戲功能的優(yōu)化。

          游戲優(yōu)化

          1.放置植物的優(yōu)化

          已經(jīng)放置過植物的草地不能再放置植物了。之前是將草地設(shè)計(jì)成empty和hold兩種狀態(tài),現(xiàn)在來看其實(shí)只需要返回一個(gè)true和false就行了,將整個(gè)植物集合定義成一個(gè)虛擬的boolean集合即可。

          2.移除植物的優(yōu)化

          設(shè)計(jì)思路是新增一個(gè)鏟子對(duì)象:
          // 鏟子集合
          private?List shovels =?new?ArrayList();
          // 鏟子入場(chǎng)
          public?void?shovelEnterAction()?{
          ??// 鏟子只有一把
          ??if(shovels.size()==0) {
          ????shovels.add(new?Shovel());
          ??}
          }
          // 使用鏟子
          Iterator it = shovels.iterator();
          Iterator it2 = plantsLife.iterator();
          while(it.hasNext()) {
          ??Shovel s = it.next();
          ??// 如果鏟子是移動(dòng)狀態(tài),就遍歷植物集合
          ??if(s.isMove()) {
          ????while(it2.hasNext()) {
          ??????Plant p = it2.next();
          ??????int?x1 = p.getX();
          ??????int?x2 = p.getX()+p.getWidth();
          ??????int?y1 = p.getY();
          ??????int?y2 = p.getY()+p.getHeight();
          ??????if((p.isLife()||((Blover) p).isClick())&&Mx>x1&&Mxy1&&My????????// 移除植物
          ????????it2.remove();
          ????????// 移除鏟子
          ????????it.remove();
          ????????shovelCheck =?false;
          ??????}
          ????}
          ??}
          }

          看著這極其復(fù)雜好像很厲害的代碼,我又萌生了痛下狠手的想法,但為了保持原生,我忍住。于是乎還發(fā)現(xiàn)了一個(gè)BUG。如果選中鏟子后,戰(zhàn)場(chǎng)上唯一的植物被僵尸吃掉了,那么這個(gè)鏟子將一直跟隨著鼠標(biāo)無法達(dá)到使用后消除的效果了。解決方案當(dāng)然也很簡(jiǎn)單,當(dāng)戰(zhàn)場(chǎng)上植物集合的size為0時(shí),清空鏟子集合即可。
          3.游戲可玩性的優(yōu)化
          上文在游戲設(shè)計(jì)中提到的擊殺僵尸后可能隨機(jī)獲得獎(jiǎng)勵(lì)類型是這樣實(shí)現(xiàn)的。還是從設(shè)計(jì)分析開始,并非擊殺任何類型的僵尸都可以獲得獎(jiǎng)勵(lì),所以獎(jiǎng)勵(lì)應(yīng)該放在接口中:
          public?interface?Award?{
          ??// 獎(jiǎng)勵(lì)接口
          ??/*
          ??* 這里還是存在代碼不規(guī)范的問題
          ??* 接口的方式默認(rèn)是public abstract
          ??* 接口中的變量默認(rèn)是public static final
          ??* 這些默認(rèn)的字段應(yīng)該舍去
          ??*/

          ??// 全屏靜止
          ??public?static?final?int?CLEAR =?0;
          ??// 全屏清除
          ??public?static?final?int?STOP =?1;
          ??public?abstract?int?getAwardType();
          }
          當(dāng)僵尸死亡時(shí),需要去判斷該僵尸是否有獎(jiǎng)勵(lì)接口,如果有則執(zhí)行相應(yīng)獎(jiǎng)勵(lì)的方法:
          // 檢測(cè)僵尸狀態(tài)
          public?void?checkZombieAction()?{
          ??// 迭代器
          ??Iterator it = zombies.iterator();
          ??while(it.hasNext()) {
          ????Zombie z = it.next();
          ????// 僵尸血量小于0則死亡,死亡的僵尸從集合中刪除
          ????if(z.getLive()<=0) {
          ??????// 判斷僵尸是否有獎(jiǎng)勵(lì)的接口
          ??????if(z instanceof Award) {
          ????????Award a = (Award)z;
          ????????int?type = a.getAwardType();
          ????????switch(type) {
          ????????case?Award.CLEAR:
          ??????????for(Zombie zo:zombies) {
          ????????????zo.goDead();
          ??????????}
          ??????????break;
          ????????case?Award.STOP:
          ??????????for(Zombie zom:zombies) {
          ????????????zom.goStop();
          ????????????timeStop =?1;
          ????????????//zombieGoLife();
          ??????????}
          ??????????break;
          ????????}
          ??????}
          ??????z.goDead();
          ????????it.remove();
          ????}
          ????// 僵尸跑進(jìn)房子,而游戲生命減一,并刪除僵尸
          ????if(z.OutOfBound()) {
          ??????gameLife--;
          ??????it.remove();
          ????}
          ??}
          }

          4.添加游戲背景音樂

          bgm是一個(gè)游戲的靈魂之一。這里給游戲添加背景音樂,我的選擇是新建一條線程專門用來執(zhí)行音樂的解析和播放:
          // 啟動(dòng)線程加載音樂
          Runnable r =?new?zombieAubio("bgm.wav");
          Thread t =?new?Thread(r);
          t.start();

          public?class?zombieAubio?implements?Runnable{
          ??// 讀音頻WAV格式專用線程
          ??private?String filename;
          ??public?zombieAubio(String wavfile){
          ??????filename=wavfile;
          ??}
          ??......
          這里需要注意的是,Java中解析音樂的API只支持WAV格式的文件,文件格式的轉(zhuǎn)換大多數(shù)音樂播放器都可以做到。

          后續(xù)優(yōu)化

          1.植物種類的擴(kuò)充及對(duì)應(yīng)功能的實(shí)現(xiàn)
          比如殺傷力最大的玉米加農(nóng)炮。需要4個(gè)小玉米進(jìn)行合成,那么在判斷是否能夠合成玉米加農(nóng)炮時(shí),需要對(duì)植物集合進(jìn)行遍歷來做坐標(biāo)的判斷,所以這邊建議最好把可合成的植物單獨(dú)放在一個(gè)集合中,這樣在做合成判斷的時(shí)候會(huì)簡(jiǎn)單很多,當(dāng)集合的size小于4時(shí),就可以提示合成失敗了。冰凍西瓜的設(shè)計(jì)思路也是如此。
          2.動(dòng)作類僵尸的加入,如撐桿跳僵尸、跳舞僵尸等
          說一下?lián)螚U跳僵尸的設(shè)計(jì)思路,此類僵尸和其他僵尸相比,多了一種跳的行為,所以會(huì)有一個(gè)單獨(dú)的方法和單獨(dú)的狀態(tài)。并且,跳只能觸發(fā)一次,所以撐桿跳僵尸的狀態(tài)變化應(yīng)該是行走->遇到植物跳過去->再遇到植物就開始攻擊,在執(zhí)行狀態(tài)變化的時(shí)候,應(yīng)該要去考慮當(dāng)前的狀態(tài)是否還可跳躍。
          3.當(dāng)植物攻擊范圍內(nèi)不存在僵尸時(shí),植物停止攻擊
          這個(gè)就簡(jiǎn)單拉,在植物執(zhí)行攻擊方法時(shí),校驗(yàn)一下是否有Y坐標(biāo)相同的僵尸即可。

          源碼分享

          公眾號(hào)回復(fù):植物大戰(zhàn)僵尸

          - 推薦閱讀 -

          抓了!程序員因受不了 996,怒建 6 個(gè)涉黃平臺(tái),涉案 5000 余萬元



          點(diǎn)贊+在看,小編感恩大家??

          瀏覽 60
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美人妻精品 | 在线免费观看小黄片 | www.操比| 免费a视频在线观看 | 精品一区二区三区四区五区六区七区八区九区 |