1. 地圖滾動的原理實現
舉個簡單的例子吧,同學們都坐過火車吧,坐火車的時候都遇到過自己的火車明明是停止的但是旁邊鐵軌的火車在向後行駛,會有一種錯覺感覺自己的火車是在向前行駛吧,呵呵。飛行射擊類游戲的地圖原理和這個完全一樣。玩家在控制飛機在屏幕中飛行的位置,背景圖片一直向後滾動從而給玩家一種錯覺自己控制的飛機在向前飛行,如下圖所示兩張地圖圖片在屏幕背後交替滾動,這樣就會給玩家產生向前移動的錯覺。
2. 觸摸屏幕控制主角飛機的移動范圍
用手觸摸屏幕中的任意一個點, 程序可以得到當前點的X , Y坐標 。 以當前飛機的X Y坐標為中心計算出當前飛機X,Y坐標點與目標X,Y點的距離 。因為飛機不可能直接就飛到目標點所以分別依次對當前坐標X,Y相加一次飛機移動的步長。 這裡我們須要考慮飛機移動過程中玩家停止觸摸屏幕,如果停止觸摸飛機將原地停住不在向目標點移動,直到新的目標點出線即玩家新觸摸屏幕的X,Y坐標。
3. 主角飛機子彈的實現原理與敵機的碰撞
因為子彈的數量會有很多,敵機的數量也會很多, 所以每一顆子彈須要用一個對象來記錄這當前子彈的X,Y坐標 與在屏幕中的繪制區域,每一架敵機也是一個對象,也記錄著它的X,Y坐標與在屏幕中的繪制區域,這樣在處理碰撞的時候其實就是每一顆子彈的矩形區域 與每一架敵機的矩形區域的碰撞。通過遍歷子彈對象與敵機對象就可以計算出碰撞的結果,從而拿到碰撞的敵機對象播放死亡爆炸動畫。
說到這裡有些同學可能會想如果按照這樣的思路將會頻繁的創建子彈對象與敵機對象這樣會造成內存洩漏等嚴重的問題。仔細想一下屏幕中須要繪制的子彈數量與敵機數量肯定是有限的,我們可以初始化固定的子彈對象與敵機對象 只對這些對象進行更新邏輯與繪制 ,舉個例子 當前游戲屏幕中我最多須要5架敵機,代碼中我就只分配5個敵機對象,分別檢測這些對象 如果被子彈打中 或者向下超過屏幕底邊,這時候可以對這個對象進行屬性的重置,讓這架飛機從新出現在上方的戰場上,這樣就實現在不增加飛機對象的情況下讓玩家感覺有打不完的飛機,子彈對象同理。
簡單敵機類實現 敵人有兩個狀態 一個是生存狀態 一個是死亡狀態 代碼中根據當前狀態播放動畫。
Java代碼
- public class Enemy {
-
- /**敵人存活狀態**/
- public static final int ENEMY_ALIVE_STATE = 0;
-
- /**敵人死亡狀態**/
- public static final int ENEMY_DEATH_STATE = 1;
-
- /**敵人行走的Y軸速度**/
- static final int ENEMY_STEP_Y = 5;
-
- /**敵人圖片的寬度**/
- static final int BULLET_WIDTH = 40;
-
- /** 敵人的XY坐標 **/
- public int m_posX = 0;
- public int m_posY = 0;
-
- /** 敵人行走的動畫 **/
- private Animation mAnimation0 = null;
- /** 敵人死亡的動畫 **/
- private Animation mAnimation1 = null;
-
- /**播放動畫狀態**/
- public int mAnimState = 0;
-
- /**是否更新繪制敵人**/
- boolean mFacus = false;
-
- /**敵人狀態**/
- int mState =0;
- Context mContext = null;
-
- public Enemy(Context context, Bitmap[] frameBitmap,Bitmap[] deadBitmap) {
- mContext = context;
- mAnimation0 = new Animation(mContext, frameBitmap, true);
- mAnimation1 = new Animation(mContext, deadBitmap, false);
- }
-
- /**初始化坐標**/
- public void init(int x, int y) {
- m_posX = x;
- m_posY = y;
- mFacus = true;
- mAnimState = ENEMY_ALIVE_STATE;
- mState = ENEMY_ALIVE_STATE;
- mAnimation0.reset();
- mAnimation1.reset();
- }
-
- /**繪制敵人動畫**/
- public void DrawEnemy(Canvas Canvas, Paint paint) {
- if (mFacus) {
- if(mAnimState == ENEMY_ALIVE_STATE) {
- mAnimation0.DrawAnimation(Canvas, paint, m_posX, m_posY);
- }else if(mAnimState == ENEMY_DEATH_STATE) {
- mAnimation1.DrawAnimation(Canvas, paint, m_posX, m_posY);
- }
-
- }
- }
- /**更新敵人狀態**/
- public void UpdateEnemy() {
- if (mFacus) {
- m_posY += ENEMY_STEP_Y;
- //當敵人狀態為死亡並且死亡動畫播放完畢 不在繪制敵人
- if(mAnimState == ENEMY_DEATH_STATE) {
- if(mAnimation1.mIsend) {
- mFacus = false;
- mState = ENEMY_DEATH_STATE;
- }
- }
- }
-
- }
- }
簡單子彈類實現
Java代碼
- public class Bullet {
-
- /**子彈的X軸速度**/
- static final int BULLET_STEP_X = 3;
-
- /**子彈的Y軸速度**/
- static final int BULLET_STEP_Y = 15;
-
- /**子彈圖片的寬度**/
- static final int BULLET_WIDTH = 40;
-
- /** 子彈的XY坐標 **/
- public int m_posX = 0;
- public int m_posY = 0;
-
- /** 子彈的動畫 **/
- private Animation mAnimation = null;
-
- /**是否更新繪制子彈**/
- boolean mFacus = false;
-
- Context mContext = null;
-
- public Bullet(Context context, Bitmap[] frameBitmap) {
- mContext = context;
- mAnimation = new Animation(mContext, frameBitmap, true);
- }
-
- /**初始化坐標**/
- public void init(int x, int y) {
- m_posX = x;
- m_posY = y;
- mFacus = true;
- }
-
- /**繪制子彈**/
- public void DrawBullet(Canvas Canvas, Paint paint) {
- if (mFacus) {
- mAnimation.DrawAnimation(Canvas, paint, m_posX, m_posY);
- }
- }
- /**更新子彈的坐標點**/
- public void UpdateBullet() {
- if (mFacus) {
- m_posY -= BULLET_STEP_Y;
- }
-
- }
-
- }
這裡推展一下知識,游戲中會有不同的敵人與不同的子彈,我們可以拓展一下對象類,那敵機來說 在對象類中可以聲明一個飛機類型的成員變量 在繪制與更新敵機的時候可以根據敵機類型做相應的處理,比如敵機的繪制圖片、飛行軌跡 、彈道AI、都可以根據它來實現。
這裡在說一下代碼設計模式中的工廠模式,工廠模式初學者會可能會覺得一頭霧水,但不可否認它的抽象原理 可以讓我們的代碼變的拓展性更好。舉個例子 比如游戲開發中我們需要3個玩家職業 A 弓箭手 B 斧頭手 C魔法師 ,首先我們分析一下這3個職業的特點 它們的共同點為都需要控制人物行走點擊攻擊按鈕播放攻擊特效等, 它們的不同點為 弓箭手 攻擊為射箭 、斧頭手攻擊為近身砍怪 、魔法師則為遠程魔法攻擊。工廠模式的原理為將他們的共同點拿出來 寫在一個類中,這個類就是一個工廠類, 然後讓他們三個分別去繼承這個類 ,分別在自己的類中實現自己特殊的方法。 這樣設計代碼的話會使代碼拓展性更佳,因為可以非常方便的添加和刪除一個職業。不會因為可變因素修改大量代碼導致項目Delay。
當然好東西可定會有它的弊端,因為使用工廠模式我們會寫很多拓展類,這樣無疑會增加大量的對象與調用方法等等,從運行效率上會大打折扣,所以開發者在設計代碼的時候就要好好斟酌自己的代碼如何來設計。
初始化游戲 在這裡將代碼中須要的所有資源所有對象全部初始化,也就是說游戲中不會在分配新對象內存。
Java代碼
- private void init() {
- /**游戲背景**/
- mBitMenuBG0 = ReadBitMap(mContext,R.drawable.map_0);
- mBitMenuBG1 = ReadBitMap(mContext,R.drawable.map_1);
-
- /**創建主角飛機動畫對象**/
- mAircraft = new Animation(mContext,new int[] {R.drawable.plan_0,R.drawable.plan_1,R.drawable.plan_2,R.drawable.plan_3,R.drawable.plan_4,R.drawable.plan_5},true);
-
- /**第一張圖片津貼在屏幕00點,第二張圖片在第一張圖片上方**/
- mBitposY0 = 0;
- mBitposY1 =-mScreenHeight;
-
- /**初始化飛機的坐標**/
- mAirPosX = 150;
- mAirPosY = 400;
-
- /**這裡敵人行走動畫就1幀**/
- Bitmap []bitmap0 = new Bitmap[ENEMY_ALIVE_COUNT];
- bitmap0[0] = ReadBitMap(mContext,R.drawable.e1_0);
- /**敵人死亡動畫**/
- Bitmap []bitmap1 = new Bitmap[ENEMY_DEATH_COUNT];
- for(int i =0; i< ENEMY_DEATH_COUNT; i++) {
- bitmap1[i] = ReadBitMap(mContext,R.drawable.bomb_enemy_0 + i);
- }
-
- /**創建敵人對象**/
- mEnemy = new Enemy[ENEMY_POOL_COUNT];
-
- for(int i =0; i< ENEMY_POOL_COUNT; i++) {
- mEnemy[i] = new Enemy(mContext,bitmap0,bitmap1);
- mEnemy[i].init(i * ENEMY_POS_OFF, 0);
- }
-
- /**創建子彈類對象**/
- mBuilet = new Bullet[BULLET_POOL_COUNT];
- mBitbullet = new Bitmap[BULLET_ANIM_COUNT];
- for(int i=0; i<BULLET_ANIM_COUNT;i++) {
- mBitbullet[i] = ReadBitMap(mContext,i+R.drawable.bullet_0);
- }
- for (int i =0; i< BULLET_POOL_COUNT;i++) {
- mBuilet[i] = new Bullet(mContext,mBitbullet);
- }
- mSendTime = System.currentTimeMillis();
- }
更新游戲 更新2張背景地圖坐標點,分別遍歷子彈與敵機對象更新它們的邏輯
Java代碼
- private void updateBg() {
- /** 更新游戲背景圖片實現向下滾動效果 **/
- mBitposY0 += 10;
- mBitposY1 += 10;
- if (mBitposY0 == mScreenHeight) {
- mBitposY0 = -mScreenHeight;
- }
- if (mBitposY1 == mScreenHeight) {
- mBitposY1 = -mScreenHeight;
- }
-
- /** 手指觸摸屏幕更新飛機坐標 **/
- if (mTouching) {
-
- if (mAirPosX < mTouchPosX) {
- mAirPosX += PLAN_STEP;
- } else {
- mAirPosX -= PLAN_STEP;
- }
- if (mAirPosY < mTouchPosY) {
- mAirPosY += PLAN_STEP;
- } else {
- mAirPosY -= PLAN_STEP;
- }
-
- if (Math.abs(mAirPosX - mTouchPosX) <= PLAN_STEP) {
- mAirPosX = mTouchPosX;
- }
- if (Math.abs(mAirPosY - mTouchPosY) <= PLAN_STEP) {
- mAirPosY = mTouchPosY;
- }
- }
- /** 更新子彈動畫 **/
- for (int i = 0; i < BULLET_POOL_COUNT; i++) {
- /** 子彈出屏後重新賦值**/
- mBuilet[i].UpdateBullet();
-
- }
- /**繪制敵人動畫**/
- for(int i =0; i< ENEMY_POOL_COUNT; i++) {
- mEnemy[i].UpdateEnemy();
- /**敵機死亡 或者 敵機超過屏幕還未死亡重置坐標**/
- if(mEnemy[i].mState == Enemy.ENEMY_DEATH_STATE ¦¦ mEnemy[i].m_posY >=mScreenHeight) {
- mEnemy[i].init(UtilRandom(0,ENEMY_POOL_COUNT) *ENEMY_POS_OFF, 0);
- }
-
- }
-
- /**根據時間初始化為發射的子彈**/
- if (mSendId < BULLET_POOL_COUNT) {
- long now = System.currentTimeMillis();
- if (now - mSendTime >= PLAN_TIME) {
- mBuilet[mSendId].init(mAirPosX - BULLET_LEFT_OFFSET, mAirPosY - BULLET_UP_OFFSET);
- mSendTime = now;
- mSendId++;
- }
- }else {
- mSendId = 0;
- }
-
- //更新子彈與敵人的碰撞
- Collision();
-
- }
在漂亮的語言不如普通實用的代碼片段,老規矩每篇文章都會附帶源代碼,最後如果你還是覺得我寫的不夠詳細 看的不夠爽 不要緊我把源代碼的下載地址貼出來 歡迎大家一起討論學習。