|
Android游戲開發(fā)之處理音樂與音效太鼓達人游戲原理
同學們在玩游戲的時候應(yīng)該都會發(fā)現(xiàn)游戲中會有兩種形式來播放音樂 ,一般設(shè)置選項中會明確標明 設(shè)置游戲音樂 與設(shè)置游戲音效。 客觀的分析一下這兩種形式的音樂,游戲背景音樂同時只會播放一首也就是說兩首背景音樂不會同時播放,除非一首播放完畢或者切換場景等 才會播放下一首。而游戲音效 比如主角與敵人揮動武器的聲音 被攻擊中的聲音等,這些聲音比較短而且播放很頻繁很有可能會同時播放游戲音效。
1.使用MediaPlayer播放游戲音樂
創(chuàng)建MediaPlayer對象 將Context與資源文件傳入。 - /**創(chuàng)建MediaPlayer對象**/
MediaPlayer mMediaPlayer = MediaPlayer.create(mContext, R.raw.v3);
/**設(shè)置為循環(huán)播放**/
mMediaPlayer.setLooping(true);
復(fù)制代碼 判斷聲音是否正在播放,如果沒有播放則開始播放游戲音樂。 - if(!mMediaPlayer.isPlaying()) {
mMediaPlayer.start();
}
復(fù)制代碼 判斷聲音是否正在播放,如果正在播放則停止播放游戲音樂。 - /**關(guān)閉音樂**/
if(mMediaPlayer.isPlaying()) {
mMediaPlayer.stop();
}
復(fù)制代碼 這里強調(diào)一下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) //從完整文件路徑名載入 第二個參數(shù)為優(yōu)先級。
創(chuàng)建音效 - /**創(chuàng)建一個聲音播放池**/
//參數(shù)1為聲音池同時播放的流的最大數(shù)量
//參數(shù)2為播放流的類型
//參數(shù)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);
復(fù)制代碼 播放音效
play (int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate)
leftVolume 表示對左音量設(shè)置 rightVolume 表示右音量設(shè)置 , loop 表示 循環(huán)次數(shù) rate表示速率最低0.5最高為2,1代表正常速度 - mSoundPool.play(mSound_0, 1, 1, 0, 0, 1);
復(fù)制代碼 這里強調(diào)一下SoundPool可以同時播放多個音樂。
下面向大家介紹一下這個DEMO中的重點,太鼓達人游戲開發(fā)的原理,圖片資源全部源于互聯(lián)網(wǎng)。
進入游戲界面 使用MediaPlayer來播放背景聲音, 玩家擊打鼓盤使用soundpool播放游戲音效。配合這下面的DEMO 請大家繼續(xù)閱讀。
菜單界面
游戲界面
1.處理觸摸點與鼓盤的碰撞
我們先分析一下鼓盤的組成結(jié)構(gòu),它是由兩個圓形組成的一個大圓形中間一個小圓形。玩家觸摸屏幕后會拿到觸摸的X,Y坐標 然后利用數(shù)學公事 (x1 – x2)2 + (y1 – y2)2 < (r1 + r2)2 計算出點與大圓形的距離與小圓形的距離,根據(jù)兩點之間的距離就可以計算出當前觸摸的點是在藍色的鼓盤中 還是紅色的鼓盤中,判斷一下X坐標在圓形左邊還是右邊就可以拿到觸摸的是左邊的鼓盤還是右邊的鼓盤。
檢測碰撞的部分源代碼 - private void Collision(int x, int y) {
//在這里進行碰撞檢測
//檢測的原理是點與圓形的碰撞
//利用數(shù)學公事 (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) {
//表明點在紅盤區(qū)域
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 {
//表明點在藍盤區(qū)域
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) {
//設(shè)置狀態(tài) UI根據(jù)這個狀態(tài)顯示擊打成功還是擊打失敗
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);
}
}
復(fù)制代碼 2 .音符的移動
游戲中我們可以發(fā)現(xiàn)各種音符會從屏幕左邊向右移動,我覺得原作肯定是有一個音符編輯器 在開發(fā)中策劃來編輯這個音符包括 位置 出現(xiàn)的是頻率 時間 音符的類型 等等 最后編輯器會把數(shù)據(jù)生成出來 在程序中去讀取這些數(shù)據(jù)并顯示出來,作為學習來說我們沒必要想那么多我強調(diào)的還是開發(fā)的原理 任何平臺的游戲它使用的算法 數(shù)據(jù)結(jié)構(gòu) 基本都是一樣的,今后我會在教程中陸續(xù)向大家貫穿這些思想。
代碼實現(xiàn)上我把音符一樣封成一個音符類,和上節(jié)教程類似每一個音符由又向左移動 根據(jù)隨機數(shù) 來設(shè)置音符的類型 為紅色還是藍色。 程序中一樣只申請了5塊 音符的對象,玩家點擊鼓盤后然后以音符對象檢測它的XY坐標是是否在點擊區(qū)域 如果在點點擊區(qū)域 在判斷玩家敲打的鼓盤音符與當前音符是否類型一樣如果一樣則表示擊打成功 屏幕中顯示good圖片,如果失敗則顯示bad圖片。被擊中的鼓點 或者沒有擊中向左超過擊打范圍 直接重置它們的坐標 讓它們進入下一個輪回判定中。
簡單的音符類實現(xiàn) 現(xiàn)在只有兩種音符 一個是紅色 一個是藍色 - 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;
}
}
復(fù)制代碼 玩家擊打某個鼓盤后 瞬間鼓點圖片會消失 然后在顯示這樣會讓玩家感覺自己已經(jīng)點中鼓盤。 這個效果可以根據(jù)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();
}
復(fù)制代碼 游戲效果圖
游戲的更新 - 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);
}
}
}
復(fù)制代碼 游戲的繪制 - 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);
/**擊打區(qū)域**/
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();
}
復(fù)制代碼 以后寫教程每個demo的代碼量會越來越多 所以貼代碼在博客中可能大家看的就不是很清楚,不過我會盡量在博客中把原理說清楚 還是建議大家都去下載我的源碼來閱讀學習。源代碼中我會寫詳細的注釋,還是那句老話在漂亮的語言不如普通實用的代碼片段,老規(guī)矩每篇文章都會附帶源代碼,最后如果你還是覺得我寫的不夠詳細 看的不夠爽 不要緊我把源代碼的下載地址貼出來 歡迎大家一起討論學習
下載地址:http://download.csdn.net/source/3512973</a |
上一篇: android:gravity和android:layout_gravity區(qū)別下一篇: android的消息推送
|