【讀書筆記《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 的每種動畫都是對整個畫布進行操作。

 

2.自定義動畫

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

新建項目,游戲框架為SurfaceView 框架,准備圖片water.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 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坐標行程動畫效果。當然在游戲開發中,很多動態的幀數不僅僅只有一幀,而所謂幀動畫,其實就是一幀一幀按照一定的順序進行播放實現的。下面是實例演示,效果如下:


新建項目,游戲框架為 SurfaceView 框架,在項目中導入下面6張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;
    // 首先聲明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軸坐標,來逐幀顯示要顯示的圖片部分,達到動態的效果。下面是實例,運行效果和前面的幀動畫一樣。步驟如下:

 

新建項目,游戲框架為 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM