前言 html
這款安卓小遊戲是基於SurfaceView的飛行射擊類遊戲,採用Java來寫,沒有采用遊戲引擎,註釋詳細,條理比較清晰,適合初學者瞭解遊戲狀態轉化自動機和一些繼承與封裝的技巧。java
效果展現git
遊戲概述github
這裏主要涉及的技術有:①SurfaceView框架 ②角色、武器的封裝 ③輔助幀動畫 ④追蹤打擊算法 ⑤多武器實現 ⑥敵我升級策略 ⑦模擬手柄。其中SurfaceView遊戲框架我在【[安卓] 八、VIEW和SURFACEVIEW遊戲框架】有詳細介紹,接下來我還將再次分析下;對於角色、武器封裝主要涉及本遊戲的類與類之間的繼承和聯繫,從敵人、遊戲者、子彈、武器等基本元素的封裝,最後將這些基本元素在SurfaceView框架中組織起來造成整個遊戲邏輯;幀動畫主要是用來實現遊戲裏的一些動畫效果,就像我在【[MFC] 高仿Flappy bird 桌面版】中本身封裝的計分板同樣和真正的Flappy bird的計分板的飛入、退出、計分等達到一樣的效果,這裏爲了使遊戲更具遊戲的效果,也採用了一樣的處理思路,好比子彈爆炸的封裝,最終失敗時GAME OVER的出現,以及點擊小飛機從新遊戲的效果;對於追蹤打擊是我在之前小時候玩的單機遊戲中學到的,當時以爲這個武器太棒了,能打一下自動找敵人,太智能了,因而這裏就把這個有趣的追蹤拿了過來;對於敵我升級策略是比較難考慮的,起初採用過關打Boss的形式,結果發現要設計不少關卡和Boss會致使遊戲框架不明顯,並且讀起來也不容易(雖然,在必定程度上有利於遊戲的多樣性),可是腦子一轉想到了也能採用Flappy bird的形式,我讓整個遊戲是個無底洞,一直進行,可是發現若是一直進行根本沒什麼意思,陣列一直重複,玩多了也就不想玩了,因而想到利用主角打怪升級武器的思路讓主角來升級,同時根據遊戲的進程使敵人的移動速度和出現頻率也增長,使敵我雙方力量平衡,這樣就增添了一點可玩性。這裏我把工程的各類java文件放到了GitHub上【https://github.com/beautifulzzzz/Android/tree/master/槍林彈雨】,若是想參考整個工程,請看最後的網盤連接。算法
3-一、SurfaceView框架數組
該SurfaceView框架位於文件MySurfaceView.java是整個遊戲的運轉框架。以下左圖:在該框架中首先對遊戲中各類資源進行初始化,而後初始化遊戲,接着進入一個獨立的Run循環,不斷接收消息及刷新頁面。從右圖能夠看出:該框架包括構造函數、Created函數、initGame函數、myDraw函數、觸屏或是按鍵監聽的函數、logic函數、以及run循環函數。app
以下在run函數中其實就是個循環,只有當flag爲false或有異常時纔會中止;對於其餘狀況,該循環定時地進行對頁面的刷新(調用myDraw)和邏輯變化的處理(調用logic)。因此整個遊戲一旦初始化完畢,就進入該循環,而後一直定時執行繪圖和邏輯來驅動整個遊戲的發展,此外外部點擊事件會改變遊戲中主角的相應參數,在下一個邏輯被處理,來實現交互的效果。框架
1 public void run() { 2 while (flag) { 3 long start = System.currentTimeMillis(); 4 myDraw(); 5 logic(); 6 long end = System.currentTimeMillis(); 7 try { 8 if (end - start < 50) {//時間均衡處理 9 Thread.sleep(50 - (end - start)); 10 } 11 } catch (InterruptedException e) { 12 e.printStackTrace(); 13 } 14 } 15 }//run函數
3-二、角色、武器的封裝dom
在該遊戲中主要有玩家、敵人和子彈三種類型的元素。這裏對玩家的封裝請看:Player.java,對敵人的封裝請看:Enemy.java,對武器的封裝請看:Bullet.java。對於Player主要包含了構造、繪製、邏輯三個基礎的函數,分別供在SurfaceView框架中進行對象創建、繪製、邏輯運算,此外還有Player特有的按鍵監聽用來控制主角、設置爲無敵狀態(由於主角剛誕生通常都是無敵狀態,防止一出現就死了,這叫胎死腹中!)、設置和獲取主角血量(也能夠理解爲主角的命,幾條命)、獲取和設置主角分數、兩個碰撞檢測(分別是判斷主角和敵人是否有碰撞和主角是否和子彈有碰撞)。函數
對於Enemy仍是包含3個基礎的函數(構造、繪製、邏輯),此外還有一個碰撞檢測(主要是和子彈碰撞)和一個reset函數用於重置數據。對於武器類Bullet就只有3個基礎函數了(構造、繪製、邏輯)。
那麼在SurfaceView框架中是如何有機地組織這些元素的呢?咱們以Enemy爲例大體看一下MySurfaceView.java中如何使用這三個對象:
① 這裏採用向量來存儲全部的Enemy對象 private Vector<Enemy> vcEnemy;
② 在initGame中加載各類Enemy對應的圖片資源並實例化Enemy容器 vcEnemy = new Vector<Enemy>(); 此外還調用了Enemy的靜態方法reset進行重置數據 Enemy.reset();
③ 在logic中當遊戲處於GAMEING狀態時要處理背景邏輯、主角邏輯和敵人邏輯:以下,都是分別調用各自封裝好的logic函數,對於Enemy還要判斷該敵人是否已經死掉,從容器中移除來優化程序。
1 backGround.logic();//背景邏輯 2 player.logic();//主角邏輯 3 //begin-----敵人邏輯 4 for (int i = 0; i < vcEnemy.size(); i++) {//敵人邏輯 5 Enemy en = vcEnemy.elementAt(i); 6 //由於容器不斷添加敵人 ,那麼對敵人isDead斷定, 7 //若是已死亡那麼就從容器中刪除,對容器起到了優化做用; 8 if (en.isDead) { 9 vcEnemy.removeElementAt(i); 10 } else { 11 en.logic(); 12 } 13 }
而後是生成敵人的相關操做,這裏敵人出現的規律採用陣列的形式,何謂「陣列」?簡單地講就像古代打仗排兵佈陣同樣,對應的兵力要放在哪、何時出現等。這裏是用enemyArray來存儲整列的:見MySurfaceView.java成員函數定義處:private int enemyArray[][] = { { 1, 2,1 }, { 1, 1}, { 1, 3, 1, 2 }, { 1, 2 }, { 2, 3 }, { 3, 1, 3 }, { 2, 2 }, { 1, 2 }, { 2, 2 }, { 1, 3, 1, 1 }, { 2, 1 },{ 1, 3 }, { 2, 1 },{ 1, 3, 1, 1 },{ 3, 3, 3, 3 }};這個enemyArray是個二維數組,其中每一小組表示每一波敵人的類型,如{1,2,1}表明2個第1種敵人和1個第2種敵人組成一波敵人殺過來了,哈哈,想一想還挺有趣的吧!只要你稍微改一下這個數組就能實現不一樣組合的敵人攻擊陣列,有一種當大將軍的感受呀!這樣下面的生成敵人的代碼就能看懂了!
1 //生成敵人 2 count++; 3 if (count % Enemy.createEnemyTime == 0) { 4 for (int i = 0; i < enemyArray[enemyArrayIndex].length; i++) { 5 if (enemyArray[enemyArrayIndex][i] == 1){//章魚怪 6 int x = random.nextInt(screenW - 100) + 50; 7 vcEnemy.addElement(new Enemy(bmpEnemyFly, 1, x, -50)); 8 } else if (enemyArray[enemyArrayIndex][i] == 2) {//漂浮物左 9 int y = random.nextInt(20); 10 vcEnemy.addElement(new Enemy(bmpEnemyDuck, 2, -50, y)); 11 } else if (enemyArray[enemyArrayIndex][i] == 3) {//漂浮物右 12 int y = random.nextInt(20); 13 vcEnemy.addElement(new Enemy(bmpEnemyDuck, 3, screenW + 50, y)); 14 } else if(enemyArray[enemyArrayIndex][i] == 4){//Boss 15 vcEnemy.addElement(new Enemy(bmpEnemyBoss,4,-100,5)); 16 } 17 }
而後處理敵人與Player碰撞的邏輯,可見:遍歷全部的敵人調用Player的碰撞檢測函數進行碰撞檢測,若是發生碰撞,則Player命減小一個,若是Player沒有命了,就遊戲結束了。
1 //處理敵人與主角的碰撞 2 for (int i = 0; i < vcEnemy.size(); i++) { 3 if (player.isCollsionWith(vcEnemy.elementAt(i))) { 4 player.setPlayerHp(player.getPlayerHp() - 1);//發生碰撞,主角血量-1 5 if (player.getPlayerHp() <= -1) {//當主角血量小於0,斷定遊戲失敗 6 gameState = GAME_LOST; 7 } 8 } 9 }
接下來處理敵人攻擊的邏輯,這裏採用每隔必定時間敵人發射子彈,一樣的咱們仍是要遍歷Enemy容器,根據敵人的種類不一樣來設置武器的種類,最後在第19行向子彈容器中加入新產生的子彈(看到這裏,你們確定知道了這個Bullet和Enemy都是採用容器的,因此接下來確定要判斷Buller是否失效,而後從容器中剔除,此外還要像檢測Enemy和Player碰撞同樣來檢測Bullet和Player碰撞,這裏就不作說明了!)
1 //每2秒添加一個敵人子彈 2 countEnemyBullet++; 3 if (countEnemyBullet % Enemy.createBulletTime == 0) { 4 for (int i=0;i<vcEnemy.size();i++){ 5 Enemy en=vcEnemy.elementAt(i); 6 int bulletType=0; 7 switch(en.type){//不一樣類型敵人不一樣的子彈運行軌跡 8 case Enemy.TYPE_FLY://章魚怪 9 bulletType = Bullet.BULLET_FLY; 10 break; 11 case Enemy.TYPE_DUCKL://漂浮物 12 case Enemy.TYPE_DUCKR: 13 bulletType = Bullet.BULLET_DUCK; 14 break; 15 case Enemy.TYPE_BOSS://boss的子彈 16 bulletType = Bullet.BULLET_DUCK;//////,,,。,。,。, 17 break; 18 } 19 vcBullet.add(new Bullet(bmpEnemyBullet, en.x + 10, en.y + 20, bulletType)); 20 } 21 }
再接着日後是處理Player的Bullet和Enemy的碰撞狀況、Player發射子彈、Player子彈的邏輯、爆炸效果邏輯。
3-三、輔助幀動畫
輔助幀動畫主要是讓交互更加流暢天然,這裏包括控制子彈爆炸的類Boom.java;控制開始按鈕的類GameMenu.java;控制背景滾動的GameBg.java;控制遊戲結束的GameLost.java。其中Boom和GameBg比較類似,包括構造、邏輯、繪製三個基本函數,爆炸主要是幾幀的順序播放一次便可,而背景則是要涉及拼接輪流無縫播放;開始按鈕和結束按鈕沒有邏輯,而是採用觸摸監聽,而後切換遊戲狀態。
3-四、追蹤打擊算法
看下面的代碼很容易這裏的追蹤算法的思想:在子彈的logic()函數中,當判別子彈類型爲跟蹤彈得時候就計算該跟蹤彈前面是否有敵人,若是有就調整方向,若是沒有就沿直線前進(特別的這裏子彈自身的角度也要調整)
1 double minLength=100000; 2 int findPos=-1; 3 for (int i=0;i<vcEnemy.size();i++){//找離當前子彈最近的敵人下標[在子彈前面的敵人算] 4 if(vcEnemy.elementAt(i).y<bulletY){ 5 double curLength=vcEnemy.elementAt(i).getLength(bulletX, bulletY); 6 if(curLength<minLength){ 7 minLength=curLength; 8 findPos=i; 9 } 10 } 11 } 12 if(findPos!=-1){//有目標算出x方向的速度 13 double tan=1.0*(vcEnemy.elementAt(findPos).x-bulletX) 14 /(vcEnemy.elementAt(findPos).y-bulletY); 15 angle=-(int)(Math.atan(tan)*180/3.1415926); 16 if(tan<0)speedX=-speed*2; 17 else speedX=speed*2; 18 }else{//沒有目標直着前進 19 speedX=0; 20 angle=0; 21 }
3-五、多武器實現
由於武器採用類的封裝,經過kind區分不一樣武器並做出不一樣處理,因此主框架下采用vector容器來存儲各類武器,想要實現不一樣的打擊效果一方面能夠經過現有武器的組合另外一方面能夠在封裝的武器類裏繼續擴展。本遊戲一方面採用組合的方式構造單發、雙發、多發模式,另外一方面又有擴展追蹤子彈,同時又將擴展子彈再和原有子彈組合達到種類繁多的效果。
1 case 6://5發2跟蹤 2 vcBulletPlayer.add(new Bullet(bmpBullet, player.x + 3, player.y - 20, Bullet.BULLET_PLAYER)); 3 vcBulletPlayer.add(new Bullet(bmpBullet, player.x + 9, player.y - 22, Bullet.BULLET_PLAYER)); 4 vcBulletPlayer.add(new Bullet(bmpBullet, player.x + 15, player.y - 25, Bullet.BULLET_PLAYER)); 5 vcBulletPlayer.add(new Bullet(bmpBullet, player.x + 21, player.y - 22, Bullet.BULLET_PLAYER)); 6 vcBulletPlayer.add(new Bullet(bmpBullet, player.x + 27, player.y - 20, Bullet.BULLET_PLAYER)); 7 if(Bullet.num<=2) 8 vcBulletPlayer.add(new Bullet(bmpBullet, player.x + 15, player.y - 25, Bullet.BULLET_PLAYER1)); 9 break;
3-六、敵我升級策略
這裏沒有采用傳統的積分->進入商城->升級裝備的模式,也沒有采用打擊->出現獎勵->吃獎勵升級的模式,爲了簡單這裏將根據主角的積分值自動劃分主角戰鬥力。另外一方面,若是敵人的實力不相應的加強,玩家很快也會喪失興趣,這裏敵人採起梯隊的攻擊模式,當整個梯隊攻擊一輪以後,敵人的速度和攻擊頻率都會相應的加強。
1 //當整個戰鬥梯隊都過一遍以後就提高難度[這裏是交替地將敵人生成頻率和敵人攻擊頻率提高] 2 enemyArrayIndex=enemyArrayIndex+1;//15組出現效果....一輪過去提高難度 3 if(enemyArrayIndex>=15){ 4 enemyArrayIndex=0; 5 if(Enemy.createBulletTime>5 6 && Enemy.createBulletTime>=Enemy.createEnemyTime) 7 Enemy.createBulletTime-=5; 8 else if(Enemy.createEnemyTime>5 9 && Enemy.createBulletTime<=Enemy.createEnemyTime) 10 Enemy.createEnemyTime-=5; 11 }
3-七、模擬手柄
這個模擬手柄是單獨被封裝好的類Control.java,其中主要包括:構造、重置、繪製、觸摸監聽。其原理是根據小圓圓心和大圓圓心的夾角判斷所在區域並改變主角的移動方向。這裏要特別注意的是:在觸摸監聽裏當手指按住屏幕時,就計算小圓該出現的位置並重繪小圓,當手指離開時小圓歸位。
1 //根據夾角設置主角移動方向 [0不動,1up,2,left,3,down,4,right] 2 angle=angle/Math.PI*180;//將弧度轉換爲角度[控制] 3 if(angle>=-150 && angle<=-30)player.setDirect(1); 4 else if(angle>=30 && angle<=150)player.setDirect(3); 5 if(Math.abs(angle)>=120)player.setDirect(2); 6 else if(Math.abs(angle)<=60)player.setDirect(4);
後記
整個項目比較簡單,適合安卓初學者拿來玩玩,若是您以爲還不錯,請點個贊分享給更多須要的人~謝啦!!☆⌒(*^-゜)v
連接
本文連接:http://www.cnblogs.com/zjutlitao/p/4233536.html
更多精彩:http://www.cnblogs.com/zjutlitao/
GitHub連接:https://github.com/beautifulzzzz/Android/tree/master/槍林彈雨