Android遊戲開發之處理音樂與音效太鼓達人遊戲原理(二十一)

Android遊戲開發之處理音樂與音效太鼓達人遊戲原理




雨鬆MOMO原創文章如轉載,請註明:轉載自雨鬆MOMO的博客原文地址:http://blog.csdn.net/xys289187120/article/details/6680469








同學們在玩遊戲的時候應該都會發現遊戲中會有兩種形式來播放音樂 ,一般設置選項中會明確標明 設置遊戲音樂 與設置遊戲音效。 客觀的分析一下這兩種形式的音樂,遊戲背景音樂同時只會播放一首也就是說兩首背景音樂不會同時播放,除非一首播放完畢或者切換場景等 纔會播放下一首。而遊戲音效 比如主角與敵人揮動武器的聲音 被攻擊中的聲音等,這些聲音比較短而且播放很頻繁很有可能會同時播放遊戲音效。


1.使用MediaPlayer播放遊戲音樂

創建MediaPlayer對象 將Context與資源文件傳入。

/**創建MediaPlayer對象**/ MediaPlayer mMediaPlayer = MediaPlayer.create(mContext, R.raw.v3); /**設置爲循環播放**/ mMediaPlayer.setLooping(true);
判斷聲音是否正在播放,如果沒有播放則開始播放遊戲音樂。

if(!mMediaPlayer.isPlaying()) { mMediaPlayer.start(); }
判斷聲音是否正在播放,如果正在播放則停止播放遊戲音樂。

/**關閉音樂**/ if(mMediaPlayer.isPlaying()) { mMediaPlayer.stop(); }
這裏強調一下MediaPlayer同一時間只能播放一個音樂


2.使用SoundPool播放遊戲音效


Soundpool的加載:

int load(Context context, int resId, int priority) //從資源中載入 比如 R.raw.id
int load(FileDescriptor fd, long offset, long length, int priority) //從FileDescriptor 對象載入
int load(AssetFileDescriptor afd, int priority) //從AssetFileDescriptor 對象載入
int load(String path, int priority) //從完整文件路徑名載入 第二個參數爲優先級。



創建音效


/**創建一個聲音播放池**/ //參數1爲聲音池同時播放的流的最大數量 //參數2爲播放流的類型 //參數3爲音樂播放效果 mSoundPool = new SoundPool(2,AudioManager.STREAM_MUSIC,100); //讀取音效 mSound_0 = mSoundPool.load(mContext, R.raw.voic_p1, 0); mSound_1 = mSoundPool.load(mContext, R.raw.voic_p1, 0);
播放音效
play (int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate)
leftVolume 表示對左音量設置 rightVolume 表示右音量設置 , loop 表示 循環次數 rate表示速率最低0.5最高爲2,1代表正常速度


mSoundPool.play(mSound_0, 1, 1, 0, 0, 1);
這裏強調一下SoundPool可以同時播放多個音樂。


下面向大家介紹一下這個DEMO中的重點,太鼓達人遊戲開發的原理,圖片資源全部源於互聯網。
進入遊戲界面 使用MediaPlayer來播放背景聲音, 玩家擊打鼓盤使用soundpool播放遊戲音效。配合這下面的DEMO 請大家繼續閱讀。



菜單界面








遊戲界面






1.處理觸摸點與鼓盤的碰撞

我們先分析一下鼓盤的組成結構,它是由兩個圓形組成的一個大圓形中間一個小圓形。玩家觸摸屏幕後會拿到觸摸的X,Y座標 然後利用數學公事 (x1 – x2)2 + (y1 – y2)2 < (r1 + r2)2 計算出點與大圓形的距離與小圓形的距離,根據兩點之間的距離就可以計算出當前觸摸的點是在藍色的鼓盤中 還是紅色的鼓盤中,判斷一下X座標在圓形左邊還是右邊就可以拿到觸摸的是左邊的鼓盤還是右邊的鼓盤。


檢測碰撞的部分源代碼

private void Collision(int x, int y) { //在這裏進行碰撞檢測 //檢測的原理是點與圓形的碰撞 //利用數學公事 (x1 – x2)2 + (y1 – y2)2 < (r1 + r2)2 //判斷點是在藍盤中還是紅盤中 int Condition = ((x - mDrumCenterX) * (x - mDrumCenterX)) +((y - mDrumCenterY) * (y - mDrumCenterY)) ; int Result = mBlueRadius * mBlueRadius; if(Condition < Result) { int redResoult = mRedRadius*mRedRadius; if(Condition<redResoult) { //表明點在紅盤區域 if(x <mDrumCenterX) { //紅盤左邊 mRedClipX = mDrumCenterX; mRedClipWidth = (mRed.getWidth() >> 1); mmDrumRedPosX = mDrumCenterX; mPonitState = POINT_RED_LEFT; }else { //紅盤右邊 mRedClipX = 0; mRedClipWidth = (mRed.getWidth() >> 1); mmDrumRedPosX=0; mPonitState = POINT_RED_RIGHT; } }else { //表明點在藍盤區域 if(x <mDrumCenterX) { //藍盤左邊 mBlueClipX = mDrumCenterX; mBlueClipWidth = (mBlue.getWidth() >> 1); mmDrumBluePosX = mDrumCenterX; mPonitState = POINT_BLUE_LEFT; }else { //藍盤右邊 mBlueClipX = 0; mBlueClipWidth = (mBlue.getWidth() >> 1); mmDrumBluePosX=0; mPonitState = POINT_BLUE_RIGHT; } } CheckCollision(); } } /**檢測玩家擊鼓是否碰撞**/ private void CheckCollision() { Note mNoteTemp = null; for (int i = 0; i < NOTE_COUNT; i++) { // 利用絕對值的方式尋找一個大概擊中的範圍 if (Math.abs(mNote[i].m_posX - mItemposX) <= mItemposW) { mNoteTemp = mNote[i]; } } boolean isCollision = false; if (mNoteTemp != null) { switch (mPonitState) { case POINT_RED_LEFT: case POINT_RED_RIGHT: if (mNoteTemp.getType() == Note.NOTE_STATE_RED) { //表明擊中了紅圓形 isCollision = true; } break; case POINT_BLUE_LEFT: case POINT_BLUE_RIGHT: if (mNoteTemp.getType() == Note.NOTE_STATE_BLUE) { //表明擊中了藍圓形 isCollision = true; } break; } } if(isCollision) { //設置狀態 UI根據這個狀態顯示擊打成功還是擊打失敗 mCollisionState = COLLISION_GREAT; //播放遊戲音效 mSoundPool.play(mSound_0, 1, 1, 0, 0, 1); }else { mCollisionState = COLLISION_BAD; //播放遊戲音效 mSoundPool.play(mSound_1, 1, 1, 0, 0, 1); } }

2 .音符的移動







遊戲中我們可以發現各種音符會從屏幕左邊向右移動,我覺得原作肯定是有一個音符編輯器 在開發中策劃來編輯這個音符包括 位置 出現的是頻率 時間 音符的類型 等等 最後編輯器會把數據生成出來 在程序中去讀取這些數據並顯示出來,作爲學習來說我們沒必要想那麼多我強調的還是開發的原理 任何平臺的遊戲它使用的算法 數據結構 基本都是一樣的,今後我會在教程中陸續向大家貫穿這些思想。
代碼實現上我把音符一樣封成一個音符類,和上節教程類似每一個音符由又向左移動 根據隨機數 來設置音符的類型 爲紅色還是藍色。 程序中一樣只申請了5塊 音符的對象,玩家點擊鼓盤後然後以音符對象檢測它的XY座標是是否在點擊區域 如果在點點擊區域 在判斷玩家敲打的鼓盤音符與當前音符是否類型一樣如果一樣則表示擊打成功 屏幕中顯示good圖片,如果失敗則顯示bad圖片。被擊中的鼓點 或者沒有擊中向左超過擊打範圍 直接重置它們的座標 讓它們進入下一個輪迴判定中。

簡單的音符類實現 現在只有兩種音符 一個是紅色 一個是藍色



public class Note { /** 音符的X軸速度 **/ static final int NOTE_STEP_X = 15; /** 紅色音符**/ static final int NOTE_STATE_RED = 0; /** 藍色音符**/ static final int NOTE_STATE_BLUE = 1; /** 音符的XY座標 **/ public int m_posX = 0; public int m_posY = 0; /**音符類型**/ private int mType = 0; /** 音符的動畫 **/ private Animation mAnimation = null; Context mContext = null; /**控制**/ private boolean mFauce = false; public Note(Context context) { mContext = context; mFauce = false; } /**重置音符**/ public void initStart(Bitmap[] frameBitmap, int type,int x, int y) { mAnimation = new Animation(mContext, frameBitmap, true); mType = type; m_posX = x; m_posY = y; mFauce = true; } /** 繪製音符 **/ public void DrawNote(Canvas Canvas, Paint paint) { if (mFauce) { mAnimation.DrawAnimation(Canvas, paint, m_posX, m_posY); } } /** 更新音符的座標點 **/ public void UpdateNote() { if (mFauce) { m_posX -= NOTE_STEP_X; } } //獲得音符類型 public int getType(){ return mType; } /**是否顯示**/ public void setFacus(boolean facus) { mFauce = facus; } }



玩家擊打某個鼓盤後 瞬間鼓點圖片會消失 然後在顯示這樣會讓玩家感覺自己已經點中鼓盤。 這個效果可以根據clipRext來把圖片切割出來顯示在屏幕中。

/** * 繪製圖片中的一部分圖片 * * @param bitmap * @param x * @param y * @param src_x * @param src_y * @param src_width * @param src_Height */ private void DrawClipImage(Bitmap bitmap, int x, int y, int src_x, int src_y, int src_xp, int src_yp) { mCanvas.save(); mCanvas.clipRect(x, y, x + src_xp, y + src_yp); mCanvas.drawBitmap(bitmap, x - src_x, y - src_y, mPaint); mCanvas.restore(); }


遊戲效果圖






遊戲的更新

private void updateGame() { if (mPlayID < NOTE_COUNT) { Long now = System.currentTimeMillis(); if (now - mStartTime >= START_TIME) { mStartTime =now; int random = UtilRandom(0, 2); int type = 0; if (random == 0) { type = Note.NOTE_STATE_RED; } else { type = Note.NOTE_STATE_BLUE; } mNote[mPlayID].initStart( new Bitmap[] { mNoteBitmap[random] }, type, mNotePosX, mNotePosY); mPlayID++; } } else { mPlayID = 0; } for(int i =0 ; i <NOTE_COUNT; i ++) { mNote[i].UpdateNote(); if(mNote[i].m_posX <= mItemposX) { mNote[i].setFacus(false); } } }

遊戲的繪製

public void renderGame() { /** 繪製遊戲菜單 **/ mCanvas.drawBitmap(mBitGameBG, 0, 0, mPaint); /**繪製小人動畫**/ mNpcAnim.DrawAnimation(mCanvas, mPaint, mNpcPosX, mNpcPosY); /**繪製鼓盤**/ mCanvas.drawBitmap(mDrum, 0, mDrumPosY, mPaint); /**藍**/ DrawClipImage(mBlue,mmDrumBluePosX,mmDrumEffectPosY,mBlueClipX,0,mBlueClipWidth,mBlueClipHeight); /**紅**/ DrawClipImage(mRed,mmDrumRedPosX,mmDrumEffectPosY,mRedClipX,0,mRedClipWidth,mRedClipHeight); /**擊打區域**/ mCanvas.drawBitmap(mBitGameItem, mItemposX, mItemposY, mPaint); /**繪製音符**/ for(int i =0 ; i <NOTE_COUNT; i ++) { mNote[i].DrawNote(mCanvas, mPaint); } /**播放點擊動畫**/ if(mCollisionState == COLLISION_GREAT ) { mCanvas.drawBitmap(mGreat, 0,0, mPaint); }else if(mCollisionState == COLLISION_BAD) { mCanvas.drawBitmap(mBad, 0,0, mPaint); } setDrumPoint(); }

以後寫教程每個demo的代碼量會越來越多 所以貼代碼在博客中可能大家看的就不是很清楚,不過我會盡量在博客中把原理說清楚 還是建議大家都去下載我的源碼來閱讀學習。源代碼中我會寫詳細的註釋,還是那句老話在漂亮的語言不如普通實用的代碼片段,老規矩每篇文章都會附帶源代碼,最後如果你還是覺得我寫的不夠詳細 看的不夠爽 不要緊我把源代碼的下載地址貼出來 歡迎大家一起討論學習雨鬆MOMO希望可以和大家一起進步。


下載地址:http://download.csdn.net/source/3512973