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