同學們在玩游戲的時候應該都會發現游戲中會有兩種形式來播放音樂 ,一般設置選項中會明確標明 設置游戲音樂 與設置游戲音效。 客觀的分析一下這兩種形式的音樂,游戲背景音樂同時只會播放一首也就是說兩首背景音樂不會同時播放,除非一首播放完畢或者切換場景等 才會播放下一首。而游戲音效 比如主角與敵人揮動武器的聲音 被攻擊中的聲音等,這些聲音比較短而且播放很頻繁很有可能會同時播放游戲音效。
1. 使用MediaPlayer播放游戲音樂
創建MediaPlayer對象 將Context與資源文件傳入。
Java代碼
- /**創建MediaPlayer對象**/
- MediaPlayer mMediaPlayer = MediaPlayer.create(mContext, R.raw.v3);
- /**設置為循環播放**/
- mMediaPlayer.setLooping(true);
判斷聲音是否正在播放,如果沒有播放則開始播放游戲音樂。
Java代碼
- if(!mMediaPlayer.isPlaying()) {
- mMediaPlayer.start();
- }
判斷聲音是否正在播放,如果正在播放則停止播放游戲音樂。
Java代碼
- /**關閉音樂**/
- 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) //從完整文件路徑名載入 第二個參數為優先級。
創建音效
Java代碼
- /**創建一個聲音播放池**/
- //參數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代表正常速度
Java代碼
- 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坐標在圓形左邊還是右邊就可以拿到觸摸的是左邊的鼓盤還是右邊的鼓盤。
檢測碰撞代碼
Java代碼
- 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圖片。被擊中的鼓點 或者沒有擊中向左超過擊打范圍 直接重置它們的坐標 讓它們進入下一個輪回判定中。
簡單的音符類實現 現在只有兩種音符 一個是紅色 一個是藍色
Java代碼
- 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來把圖片切割出來顯示在屏幕中。
Java代碼
- /** * 繪制圖片中的一部分圖片 *
- * @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();
- }
游戲效果圖
游戲的更新
Java代碼
- 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);
- }
- }
- }
游戲的繪制
Java代碼
- 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的代碼量會越來越多 所以貼代碼在博客中可能大家看的就不是很清楚,不過我會盡量在博客中把原理說清楚 還是建議大家都去下載我的源碼來閱讀學習。源代碼中我會寫詳細的注釋,還是那句老話在漂亮的語言不如普通實用的代碼片段,老規矩每篇文章都會附帶源代碼,最後如果你還是覺得我寫的不夠詳細 看的不夠爽 不要緊我把源代碼的下載地址貼出來 歡迎大家一起討論學習。
下載地址:http://vdisk.weibo.com/s/aagXi