【讀書筆記《Android遊戲編程之從零開始》】16.遊戲開發基礎(動畫)

1. Animation動畫
 
在Android 中,系統提供了動畫類 Animation ,其中又分爲四種動畫效果:
● AlphaAnimation:透明度漸變更畫
● ScaleAnimation:漸變尺寸縮放動畫;
● TranslateAnimation:移動動畫
● RotateAnimation:旋轉動畫
 
這4種動畫效果的建立方法以下:
(1)  AlphaAnimation 透明度漸變更畫
Animation alphaA = new AlphaAnimation(float fromAlpha,float toAlpha)
第一個參數:動畫開始時的透明度
第二個參數:動畫結束時的透明度
兩個參數的取值範圍爲[0,1],從徹底透明到徹底不透明。
 
(2)ScaleAnimation 漸變尺寸縮放動畫
Animation scaleA = new ScaleAnimation(float fromX,float toX,float fromY,float toY,int pivotXType,float pivotXValue,int pivoteYType,float pivoteYValue)
第一個參數:動畫起始時X座標上的伸縮比例
第二個參數:動畫結束時X座標上的伸縮比例
第三個參數:動畫起始時Y座標上的伸縮比例
第四個參數:動畫結束時Y座標上的伸縮比例
第五個參數:動畫在X軸相對於物體的位置類型
第六個參數:動畫相對於物體X座標的位置
第七個參數:動畫在Y軸相對於物體的位置類型
第八個參數:動畫相對於物體Y座標的位置
其中位置類型分爲如下三種:
Animation.ABSOLUTE:相對位置是屏幕左上角,絕對位置;
Animation.RELATIVE_TO_SELF:相對位置是自身 View, 取值爲 0 時,表示相對因而自身左上角,取值爲1是相對於自身的右下角;
Animation.RELATIVE_TO_PAREND:相對父類 View 的位置。
 
(3)TranslateAnimation 移動動畫
Animation translateA = new TranslateAnimation(float fromXDelta,float toXDelta,float fromYDelta,float YDelta)
第一個參數:動畫起始時X軸上的位置
第二個參數:動畫結束時X軸上的位置
第三個參數:動畫起始時Y軸上的位置
第四個參數:動畫結束時Y軸上的位置
 
(4)RotateAnimation 旋轉動畫
Animation rotateA = new RotateAnimation(float fromDegrees,float toDegrees,int pivotXType,float pivotXValue,int pivotYType,float pivotYValue)
第一個參數:動畫起始時旋轉角度
第二個參數:動畫旋轉到的角度
第三個參數:動畫在X軸相對於物件位置類型
第四個參數:動畫相對於物件的X座標的開始位置
第五個參數:動畫在Y軸相對於物體的位置類型
第六個參數:動畫相對於物體的Y座標的位置
無論哪種動畫,都有一些通用方法,好比:
restart():從新播放動畫;
setDuration(int time):設置動畫播放時間,單位是毫秒。
 
下面是實例演示,效果以下:
新建項目,遊戲框架爲 View 遊戲框架, 具體步驟參照「10.遊戲開發基礎(View 遊戲框架)」。
修改 MyView 類,代碼以下:
package com.example.ex4_10;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.KeyEvent;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.RotateAnimation;
import android.view.animation.ScaleAnimation;
import android.view.animation.TranslateAnimation;

public class MyView extends View implements AnimationListener{
    private Paint paint;
    public MyView(Context context) {
        super(context);
        paint = new Paint();
        paint.setColor(Color.WHITE);
        paint.setTextSize(20); 
        setFocusable(true);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Bitmap bmp = BitmapFactory.decodeResource(this.getResources(), R.drawable.pic01);
        //黑色背景
        canvas.drawColor(Color.BLACK);
        canvas.drawText("方向鍵 ↑ 漸變透明度動畫效果", 80, this.getHeight()-80, paint);
        canvas.drawText("方向鍵 ↓ 漸變尺寸伸縮動畫效果", 80, this.getHeight()-60, paint);
        canvas.drawText("方向鍵 → 畫面轉換位置移動動畫效果", 80, this.getHeight()-40, paint);
        canvas.drawText("方向鍵 ← 畫面轉移旋轉動畫效果", 80, this.getHeight()-20, paint);
        //繪製位圖,居中
        canvas.drawBitmap(bmp, this.getWidth()/2-bmp.getWidth()/2, this.getHeight()/2-bmp.getHeight()/2, paint);
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if(keyCode==KeyEvent.KEYCODE_DPAD_UP)
        {
            //漸變透明效果
            Animation  m1 = new AlphaAnimation(0.1f, 1.0f);
            //設置動畫播放時間爲3秒
            m1.setDuration(3000);
            //啓動動畫效果
            this.startAnimation(m1);
        }else if(keyCode==KeyEvent.KEYCODE_DPAD_DOWN)
        {
            //漸變尺寸縮放動畫效果
            Animation m2 = new ScaleAnimation(0.0f, 2.0f, 1.5f, 1.5f,Animation.RELATIVE_TO_PARENT,0.5f,Animation.RELATIVE_TO_PARENT,0.0f);
            m2.setDuration(2000);
            this.startAnimation(m2);            
        }else if(keyCode==KeyEvent.KEYCODE_DPAD_LEFT)
        {
            //移動動畫效果
            Animation m3 = new TranslateAnimation(0, 100, 0, 100);
            m3.setDuration(2000);
            this.startAnimation(m3);    
        }else if(keyCode==KeyEvent.KEYCODE_DPAD_RIGHT)
        {
            //旋轉動畫效果,這裏是旋轉360°
            Animation m4 = new  RotateAnimation(0.0f, 360.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
            m4.setDuration(2000);
            this.startAnimation(m4);    
        }
        return super.onKeyDown(keyCode, event);
    }

    /**
     * 動畫開始時響應的函數
     */
    @Override
    public void onAnimationStart(Animation animation) {
        
    }

    /**
     * 動畫結束時響應的函數
     */
    @Override
    public void onAnimationEnd(Animation animation) {
        
    }

    /**
     * 動畫重播時響應的函數
     */
    @Override
    public void onAnimationRepeat(Animation animation) {
        
    }

    
}
MyView

記住,Animation 的每種動畫都是對整個畫布進行操做。html

 

2.自定義動畫android

(1)動態位圖
在以前隨筆中,曾在屏幕上讓文本字符串跟隨玩家手指移動,從而行程一個動態的效果;那麼讓一張位圖造成動態效果也很容易,只要不斷改變位圖的X或者Y軸的座標便可。下面利用一張位圖造成海的波浪效果。

新建項目,遊戲框架爲SurfaceView 框架,準備圖片water.png以下:canvas

修改MySurfaceView 類,代碼以下:數組

package com.example.ex4_11;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;

public class MySurfaceView extends SurfaceView implements Callback, Runnable {

    // 用於控制SurfaceView 的大小、格式等,而且主要用於監聽SurfaceView 的狀態
    private SurfaceHolder sfh;
    // 聲明一張波浪圖
    private Bitmap bmp;
    // 聲明波浪圖的X,Y軸座標
    private int bmpX, bmpY;
    // 聲明一個畫布
    private Canvas canvas;
    // 聲明一個線程
    private Thread th;
    // 線程消亡的標識符
    private boolean flag;

    public MySurfaceView(Context context) {
        super(context);
        // 實例SurfaceView
        sfh = this.getHolder();
        // 爲SurfaceView添加狀態監聽
        sfh.addCallback(this);
    }

    /**
     * SurfaceView 視圖建立,響應此函數
     */
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        bmp = BitmapFactory.decodeResource(this.getResources(),
                R.drawable.water);
        // 讓位圖初始化X軸正好充滿屏幕
        bmpX = -bmp.getWidth() + this.getWidth();
        // 讓位圖繪製在畫布的最下方,且圖片Y座標正好是(屏幕高-圖片高)
        bmpY = this.getHeight() - bmp.getHeight();
        flag = true;
        // 實例線程
        th = new Thread(this);
        // 啓動線程
        th.start();
    }

    /**
     * SurfaceView 視圖狀態發生改變時,響應此函數
     */
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {

    }

    /**
     * SurfaceView 視圖消亡時,響應此函數
     */
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        flag = false;
    }

    private void myDraw() {
        try {
            canvas = sfh.lockCanvas();
            if (canvas != null) {
                //刷屏,畫布白色
                canvas.drawColor(Color.WHITE);
                //繪製位圖
                canvas.drawBitmap(bmp, bmpX, bmpY, new Paint() );
            }
        } catch (Exception e) {
            // TODO: handle exception
        } finally {
            if (canvas != null) {
                sfh.unlockCanvasAndPost(canvas);
            }
        }
    }

    /**
     * 遊戲邏輯
     */
    private void logic() {
            bmpX += 5;
    }

    @Override
    public void run() {
        while (flag) {
            long start = System.currentTimeMillis();
            myDraw();
            logic();
            long end = System.currentTimeMillis();
            try {
                if (end - start < 50) {
                    Thread.sleep(50 - (end - start));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

}
MySurfaceView

 

(2)幀動畫框架

前面是利用改變位圖的X或者Y座標行程動畫效果。固然在遊戲開發中,不少動態的幀數不單單隻有一幀,而所謂幀動畫,其實就是一幀一幀按照必定的順序進行播放實現的。下面是實例演示,效果以下:ide


新建項目,遊戲框架爲 SurfaceView 框架,在項目中導入下面6張png圖片:函數


修改MySurfaceView 類,代碼以下:post

package com.example.ex4_11;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;

public class MySurfaceView extends SurfaceView implements Callback, Runnable {

    // 用於控制SurfaceView 的大小、格式等,而且主要用於監聽SurfaceView 的狀態
    private SurfaceHolder sfh;
    // 聲明一個畫布
    private Canvas canvas;
    // 聲明一個線程
    private Thread th;
    // 線程消亡的標識符
    private boolean flag;
    private Paint paint;
    // 首先聲明6個容量的位圖數組
    private Bitmap wifiBmp[] = new Bitmap[6];
    // 對應6張圖片的Id
    private int[] wifiBmpId = new int[] { R.drawable.wifi01, R.drawable.wifi02,
            R.drawable.wifi03, R.drawable.wifi04, R.drawable.wifi05,
            R.drawable.wifi06 };
    // 記錄當前播放幀
    private int currentFrame;

    public MySurfaceView(Context context) {
        super(context);
        // 實例SurfaceView
        sfh = this.getHolder();
        // 爲SurfaceView添加狀態監聽
        sfh.addCallback(this);
        paint = new Paint();
        // 畫筆無鋸齒
        paint.setAntiAlias(true);
        // 將幀圖放入幀數組
        for (int i = 0; i < wifiBmp.length; i++) {
            wifiBmp[i] = BitmapFactory.decodeResource(this.getResources(),
                    wifiBmpId[i]);
        }
    }

    /**
     * SurfaceView 視圖建立,響應此函數
     */
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        flag = true;
        // 實例線程
        th = new Thread(this);
        // 啓動線程
        th.start();
    }

    /**
     * SurfaceView 視圖狀態發生改變時,響應此函數
     */
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {

    }

    /**
     * SurfaceView 視圖消亡時,響應此函數
     */
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        flag = false;
    }

    private void myDraw() {
        try {
            canvas = sfh.lockCanvas();
            if (canvas != null) {
                // 刷屏,畫布白色
                canvas.drawColor(Color.WHITE);
                // 繪製位圖,居中顯示
                canvas.drawBitmap(
                        wifiBmp[currentFrame],
                        this.getWidth() / 2 - wifiBmp[currentFrame].getWidth()
                                / 2,
                        this.getHeight() / 2
                                - wifiBmp[currentFrame].getHeight() / 2, paint);
            }
        } catch (Exception e) {
            // TODO: handle exception
        } finally {
            if (canvas != null) {
                sfh.unlockCanvasAndPost(canvas);
            }
        }
    }

    /**
     * 遊戲邏輯
     */
    private void logic() {
        currentFrame++;
        // 當播放的當前幀大於而且等於幀數組時,重置當前幀爲0
        if (currentFrame >= wifiBmp.length) {
            currentFrame = 0;
        }
    }

    @Override
    public void run() {
        while (flag) {
            long start = System.currentTimeMillis();
            myDraw();
            logic();
            long end = System.currentTimeMillis();
            try {
                if (end - start < 500) {
                    Thread.sleep(500 - (end - start));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

}
MySurfaceView

 

(3)剪切圖動畫動畫

剪切圖動畫相似於幀動畫的形式,惟一的區別就是動態幀所有放在了一張圖片中,而後經過設置可視區域完成。簡單的解釋,繪圖時經過控制X軸或者Y軸座標,來逐幀顯示要顯示的圖片部分,達到動態的效果。下面是實例,運行效果和前面的幀動畫同樣。步驟以下:this

 

新建項目,遊戲框架爲 SurfaceView 框架,在項目中導入下面這張png圖片:

修改MySurfaceView 類,代碼以下:

package com.example.ex4_11;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;

public class MySurfaceView extends SurfaceView implements Callback, Runnable {

    // 用於控制SurfaceView 的大小、格式等,而且主要用於監聽SurfaceView 的狀態
    private SurfaceHolder sfh;
    // 聲明一個畫布
    private Canvas canvas;
    // 聲明一個線程
    private Thread th;
    // 線程消亡的標識符
    private boolean flag;
    private Paint paint;
    // 建立位圖
    private Bitmap bmp = BitmapFactory.decodeResource(this.getResources(),
            R.drawable.wifi_all);
    // 記錄當前播放幀
    private int currentFrame;

    public MySurfaceView(Context context) {
        super(context);
        // 實例SurfaceView
        sfh = this.getHolder();
        // 爲SurfaceView添加狀態監聽
        sfh.addCallback(this);
        paint = new Paint();
        // 畫筆無鋸齒
        paint.setAntiAlias(true);

    }

    /**
     * SurfaceView 視圖建立,響應此函數
     */
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        flag = true;
        // 實例線程
        th = new Thread(this);
        // 啓動線程
        th.start();
    }

    /**
     * SurfaceView 視圖狀態發生改變時,響應此函數
     */
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {

    }

    /**
     * SurfaceView 視圖消亡時,響應此函數
     */
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        flag = false;
    }

    private int minW = bmp.getWidth() / 6;

    private void myDraw() {
        try {
            //居中點X軸座標
            int cW = this.getWidth() / 2 - minW / 2;
            //居中點Y軸座標
            int cH = this.getHeight() / 2 - bmp.getHeight() / 2;
            canvas = sfh.lockCanvas();
            if (canvas != null) {
                // 刷屏,畫布白色
                canvas.drawColor(Color.WHITE);
                canvas.save();
                // 設置畫布的可視區域,大小爲每幀的大小,居中
                canvas.clipRect(cW, cH, cW + minW, cH + bmp.getHeight());
                // 繪製位圖,居中
                canvas.drawBitmap(bmp, cW - currentFrame * minW, cH, paint);
                canvas.restore();
            }
        } catch (Exception e) {
            // TODO: handle exception
        } finally {
            if (canvas != null) {
                sfh.unlockCanvasAndPost(canvas);
            }
        }
    }

    /**
     * 遊戲邏輯
     */
    private void logic() {
        currentFrame++;
        // 當播放的當前幀大於而且等於幀數組時,重置當前幀爲0
        if (currentFrame >= 6) {
            currentFrame = 0;
        }
    }

    @Override
    public void run() {
        while (flag) {
            long start = System.currentTimeMillis();
            myDraw();
            logic();
            long end = System.currentTimeMillis();
            try {
                if (end - start < 500) {
                    Thread.sleep(500 - (end - start));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

}
MySurfaceView
相關文章
相關標籤/搜索