物件控制
此範例為物件移動旋轉與縮放的操作以及物件與物件的碰撞或包含判斷,右上方的android公仔為主要的物件,可以透過鍵盤方向鍵進行移動旋轉與縮放,右上方的”ˇ按鈕”為切換移動或旋轉縮放的控制項,當公仔移動到藍色區域,就會判斷是否與藍色區域進行碰撞,而一旦公仔將紅色區域完全覆蓋,就會判斷為包含狀態。
gameDemo1.JPG
圖2-3
以下為物件控制的程式碼:
專案下載:gameDemo1.rar
主程式: GameDemo1.java
package ccc.GameDemo1;
 
import ccc.GameDemo1.R;
import android.app.Activity;
 
// 引用會用到的繪圖函式庫
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
 
//引用鍵盤的事件函式庫
import android.view.KeyEvent;
 
public class GameDemo1 extends Activity {
    // 叉叉按鈕
    Button closeButton;
 
    // 可勾選的控制項
    CheckBox actCheckBox;
 
    // 畫布
    SurfaceView SurfaceView01;
 
    // 畫布處理者
    SurfaceHolder surfaceHolder;
 
    // 遊戲執行緒
    Thread gameThread;
 
    // 通知遊戲執行緒結束  
    Boolean isGameThreadOver = true;
 
    // 遊戲物件
    GameObj demoObj;
 
    // 設定每秒最大更新畫格數
    int gameFPS = 25;
 
    // 初始化鍵盤處理者 
    KeyHandler keyHandler = new KeyHandler();
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
 
        // 取得本機圖片後初始化繪圖物件
        Drawable demoDrawable = getResources().getDrawable(R.drawable.demo);
 
        // 初始化遊戲物件
        demoObj = new GameObj(demoDrawable);
 
        // 初始化可勾選的控制項 
        actCheckBox = (CheckBox) findViewById(R.id.actCheckBox);
 
        // 可勾選的事件處理
        actCheckBox.setOnClickListener(new View.OnClickListener() {
            public void onClick(View arg0) {
                // 如果已勾選則設定文字為"移動",反之設為旋轉與縮放
                if (actCheckBox.isChecked())
                    actCheckBox.setText("move");
                else
                    actCheckBox.setText("roate and scale");
            }
        });
 
        // 初始化叉叉
        closeButton = (Button) findViewById(R.id.closeButton);
 
        // 當按下"叉叉"時結束程式
        closeButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View arg0) {
                gameExit();
            }
        });
 
        // 初始化畫布
        SurfaceView01 = (SurfaceView) findViewById(R.id.SurfaceView01);
 
        // 取得畫布處理者
        surfaceHolder = SurfaceView01.getHolder();
 
        // 增加回呼方法
        surfaceHolder.addCallback(new SurfaceHolder.Callback() {
 
            public void surfaceDestroyed(SurfaceHolder arg0) {
 
            }
 
            // 當畫布被創造時
            public void surfaceCreated(SurfaceHolder arg0) {
                // 畫出一開始的等待3秒後開始
                draw(drawAction.start);
 
                // 初始化處理者
                Handler handler = new Handler();
 
                // 處理者方法
                Runnable gameStartRun = new Runnable() {
                    public void run() {
                        // 啟動遊戲
                        gameStart();
                    }
                };
 
                // 延遲3秒
                handler.postDelayed(gameStartRun, 3000);
            }
            public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2,
                    int arg3) {
            }
        });
 
    }
 
    // 按下叉叉後的事件
    void gameExit() {
        if (gameThread != null) {
            isGameThreadOver = true;
            try {
                // 等待遊戲執行緒的結束
                gameThread.join();
            } catch (InterruptedException e) {
 
            }
        }
        finish();// 結束遊戲
    }
 
    // 鍵盤按下的事件
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        keyHandler.keyDown(keyCode);
        return true;
    }
 
    // 鍵盤彈起的事件
    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        keyHandler.keyUp(keyCode);
        return true;
    }
 
    // 啟動遊戲執行緒
    void gameStart() {
        isGameThreadOver = false;
        gameThread = new Thread(gameRun);
        gameThread.start();
    }
 
    // 遊戲主迴圈
    Runnable gameRun = new Runnable() {
        public void run() {
            long delayTime = 1000 / gameFPS;
            while (!isGameThreadOver) {
                long startTime = System.currentTimeMillis();
                gameUpdate();
                draw(drawAction.game);
                long endTime = System.currentTimeMillis();
                long waitTime = delayTime - (startTime - endTime);
                if (waitTime > 0) {
                    try {
                        Thread.sleep(waitTime);
                    } catch (InterruptedException e) {
                    }
                }
            }
        }
    };
 
    // 鍵盤是否按下
    boolean isKeyDown(int keyCode) {
        return keyHandler.isKeyDown(keyCode);
    }
 
    // 更新動作
    void gameUpdate() {
        // 暫存物件的狀態
        demoObj.save();
 
        if (actCheckBox.isChecked()){
            // 當已勾選時做上下左右移動
            if (isKeyDown(KeyEvent.KEYCODE_DPAD_LEFT))
                demoObj.move(-2, 0);
            if (isKeyDown(KeyEvent.KEYCODE_DPAD_RIGHT))
                demoObj.move(2, 0);
            if (isKeyDown(KeyEvent.KEYCODE_DPAD_UP))
                demoObj.move(0, -2);
            if (isKeyDown(KeyEvent.KEYCODE_DPAD_DOWN))
                demoObj.move(0, 2);
        } else {
            // 當未勾選時按鍵變成左右旋轉與縮放
            if (isKeyDown(KeyEvent.KEYCODE_DPAD_RIGHT))
                demoObj.rotate(10);
            if (isKeyDown(KeyEvent.KEYCODE_DPAD_LEFT))
                demoObj.rotate(-10);
            if (isKeyDown(KeyEvent.KEYCODE_DPAD_UP))
                demoObj.scale(2, 2);
            if (isKeyDown(KeyEvent.KEYCODE_DPAD_DOWN))
                demoObj.scale(-2, -2);
        }
 
        // 螢幕範圍
        Rect screenRect=new Rect(0,0,SurfaceView01.getWidth(),SurfaceView01.getHeight());
 
        // 如果物件超出螢幕範圍則還原其原來的狀態
        if(!screenRect.contains(demoObj.getRect()))
            demoObj.restore();
 
        // 將灰色區域向左上角移動40pixel
        Rect scaleRect = new Rect(screenRect);
        scaleRect.offset(-40, -40);
 
        // 如果超過範圍則縮小物件的大小
        demoObj.intersect(scaleRect);
    }
 
    // 列舉繪圖狀態
    enum drawAction {
        start, game, over
    }
 
    // 更新畫面
    void draw(drawAction action) {
        Canvas canvas = null;
        try {
            // 鎖定
            canvas = surfaceHolder.lockCanvas(null);
            synchronized (surfaceHolder) {
                // 依據狀態做不同的畫面更新
                switch (action) {
                    // 畫出一開始的等待3秒後遊戲開始
                    case start:
                        drawStart(canvas);
                        break;
                    // 畫出
                    case game:
                        drawGame(canvas);
                        break;
                    // 遊戲結束清除畫面
                    case over:
                        drawOver(canvas);
                        break;
                }
            }
        } finally {
            if (canvas != null) {
                // 解除鎖定
                surfaceHolder.unlockCanvasAndPost(canvas);
            }
        }
    }
 
    // 畫出一開始的等待3秒後遊戲開始
    void drawStart(Canvas canvas) {
        clear(canvas);
        demoObj.draw(canvas);
        Paint paint = new Paint();
        paint.setARGB(255, 255, 255, 255);
        paint.setTextSize(20);
        canvas.drawText("Wait 3 Sec Game Start", 20,
                SurfaceView01.getWidth() / 2, paint);
    }
 
    // 畫出遊戲物件
    void drawGame(Canvas canvas) {
        clear(canvas);
        Rect screenRect=new Rect(0,0,SurfaceView01.getWidth(),SurfaceView01.getHeight());
        int centerX=screenRect.centerX();
        int centerY=screenRect.centerY();
 
        Rect scaleRect=new Rect(screenRect);
        scaleRect.offset(-40, -40);
 
        Rect intersectsRect=new Rect(centerX-centerX/2,centerY-centerY/2,centerX+centerX/2,centerY+centerY/2);
        Rect containsRect=new Rect(centerX-20,centerY-20,centerX+20,centerY+20);
 
        Paint paint = new Paint();
 
        paint.setARGB(255, 100, 100, 100);
        canvas.drawRect(scaleRect,paint);
        paint.setARGB(255, 0, 0, 200);
        canvas.drawRect(intersectsRect,paint);
        paint.setARGB(255, 200, 0, 0);
        canvas.drawRect(containsRect,paint);
        paint.setTextSize(20);
        paint.setARGB(255, 255, 255, 255);
 
        // 是否顯示碰撞
        if(Rect.intersects( intersectsRect,demoObj.getRect()))
            canvas.drawText("intersect", 20,
                    centerY, paint);
 
        // 是否顯示包含
        if(demoObj.contains(containsRect))
            canvas.drawText("contains",20,
                    centerY+20, paint);
        demoObj.draw(canvas);
    }
 
    // 遊戲結束時
    void drawOver(Canvas canvas) {
        clear(canvas);
    }
 
    // 清除畫面
    void clear(Canvas canvas) {
        Paint p = new Paint();
        p.setARGB(255, 0, 0, 0);
        Rect rect = new Rect(0, 0, SurfaceView01.getWidth(), SurfaceView01
                .getHeight());
        canvas.drawRect(rect, p);
    }
 
}

程式: GameObj.java

package ccc.GameDemo1;
 
//引用繪圖會用到的函式庫
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
 
//遊戲物件類別
public class GameObj {
    // 角度
    public float angle;
 
    // 儲存角度 
    private float saveAngle;
 
    // 圖片物件
    public Drawable drawable;
 
    // 是否畫出圖片
    public boolean Visible = true;
 
    // 是否啟用功能
    public boolean Enable = true;
    private Rect saveRect;
 
    // 初始化設定
    public GameObj(Drawable drawable) {
        this.drawable = drawable;
        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable
                .getIntrinsicHeight());
        this.save();
    }
 
    // 儲存狀態
    public void save() {
        if (Enable) {
            saveRect = drawable.copyBounds();
            saveAngle=angle;
        }
    }
 
    // 還原狀態
    public void restore() {
        if (Enable) {
            drawable.setBounds(saveRect);
            angle=saveAngle;
        }
    }
 
    // 增加旋轉角度
    public void rotate(float angle) {
        if (Enable) {
            this.angle += angle;
            this.angle %= 360;
        }
    }
 
    // 直接設定旋轉角度
    public void setAngle(float angle) {
        if (Enable) {
            this.angle = angle;
            this.angle %= 360;
        }
    }
 
    // 取得旋轉的角度
    public float getAngle(float angle) {
        return angle;
    }
 
    // 直接移動到該位置
    public void moveTo(int newLeft, int newTop) {
        if (Enable) {
            Rect rect = drawable.getBounds();
            drawable.setBounds(newLeft, newTop, newLeft + rect.width(), newTop
                    + rect.height());
        }
    }
 
    // 移動位置
    public void move(int dx, int dy) {
        if (Enable) {
            Rect rect = drawable.getBounds();
            drawable.setBounds(rect.left + dx, rect.top + dy, rect.right + dx,
                    rect.bottom + dy);
        }
    }
 
    // 縮放大小
    public void scale(int scaleX, int scaleY) {
        if (Enable) {
            Rect rect = drawable.getBounds();
            drawable.setBounds(rect.left - scaleX, rect.top - scaleY,
                    rect.right + scaleX, rect.bottom + scaleY);
        }
    }
 
    // 畫圖
    public void draw(Canvas canvas) {
        if (Visible) {
            canvas.save();
            canvas.rotate(angle, drawable.getBounds().centerX(), drawable
                    .getBounds().centerY());
            drawable.draw(canvas);
            canvas.restore();
        }
    }
 
    // 取得X座標的中心點
    public int centerX() {
        return drawable.getBounds().centerX();
    }
 
    // 取得Y座標的中心點
    public int centerY() {
        return drawable.getBounds().centerY();
    }
 
    // 取得範圍大小
    public Rect getRect() {
        return drawable.getBounds();
    }
 
    // 取得高度
    public int getHeight() {
        return drawable.getBounds().height();
    }
 
    // 取得寬度
    public int getWidth() {
        return drawable.getBounds().width();
    }
 
    // 取得原來的高度
    public int getSrcHeight() {
        return drawable.getIntrinsicHeight();
    }
 
    // 取得原來的寬度
    public int getSrcWidth() {
        return drawable.getIntrinsicWidth();
    }
 
    // 設定範圍大小
    public void setRect(Rect rect) {
        drawable.setBounds(rect);
    }
 
    // 一樣是設定範圍大小只是傳入的參數不同
    public void setRect(int left, int top, int right, int bottom) {
        drawable.setBounds(left, top, right, bottom);
 
    }
 
    // 是否發生碰撞
    public boolean intersect(Rect r) {
        return drawable.getBounds().intersect(r);
    }
 
    // 是否發生碰撞(傳入物件)
    public boolean intersect(GameObj obj) {
        return this.intersect(obj.getRect());
    }
 
    // 是否包含
    public boolean contains(Rect r) {
        return drawable.getBounds().contains(r);
    }
 
    // 是否包含(傳入物件)
    public boolean contains(GameObj obj) {
        return this.contains(obj.getRect());
    }
}

程式: KeyHandler.java

package ccc.GameDemo1;
 
// 引用雜湊表函式庫
import java.util.HashMap;
import java.util.Map;
 
// 鍵盤處理者
 
public class KeyHandler {
    // 鍵盤狀態矩陣
    public Map<Integer, Boolean> MapKeyDown = new HashMap<Integer, Boolean>();
 
    public KeyHandler() {
 
    }
 
    // 鍵盤按下
    public void keyDown(int keyCode) {
        MapKeyDown.put(keyCode, true);
    }
 
    // 鍵盤彈起
    public void keyUp(int keyCode) {
        MapKeyDown.put(keyCode, false);
    }
 
    // 鍵盤是否按下
    public boolean isKeyDown(int keyCode) {
        Boolean isKeyDown = MapKeyDown.get(keyCode);
        if (isKeyDown != null)
            return isKeyDown;
        else
            return false;
    }
 
    // 鍵盤是否彈起
    public boolean isKeyUp(int keyCode) {
        Boolean isKeyUp = MapKeyDown.get(keyCode);
        if (isKeyUp != null)
            return isKeyUp;
        else
            return true;
    }
}

Activity介面程式碼

<?xml version="1.0" encoding="utf-8"?>
<AbsoluteLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android">
<SurfaceView android:id="@+id/SurfaceView01" android:layout_height="fill_parent" android:layout_width="fill_parent">
</SurfaceView>
  <LinearLayout
  android:layout_width="fill_parent"
  android:layout_height="fill_parent" android:orientation="horizontal" android:gravity="right">
    <CheckBox android:layout_above="@+id/closeButton" android:layout_height="wrap_content" android:layout_width="wrap_content" android:id="@+id/actCheckBox" android:text="move" android:checked="true"></CheckBox><Button
    android:id="@+id/closeButton"
    android:layout_width="40px"
    android:layout_height="40px"
    android:text="X"
    android:layout_alignParentRight="true"
    android:layout_alignParentTop="true">
    </Button>
 
</LinearLayout>
</AbsoluteLayout>

程式影片解說