貪食蛇遊戲

手機遊戲-貪食蛇
遊戲玩法:在時間限制內,將蘋果一一吃盡,每吃得一個蘋果可增加遊戲分數1分,並且可增加遊戲時間3秒,但是撞到牆壁或是本身會進行扣分(每次扣除1分)。
說明:此範例可進行螢幕觸控來控制蛇的移動方向,並且也可以使用手機上的按鍵來進行遊戲,其最大的特色就是在於可進行3600方向移動與一般的貪食蛇只能上下左右有所不同。
下圖為遊戲畫面、程式的狀態圖、架構圖及流程圖::
%E8%B2%AA%E9%A3%9F%E8%9B%87.PNG
圖一-遊戲執行畫面
%E8%B2%AA%E9%A3%9F%E8%9B%87%E7%A8%8B%E5%BC%8F%E6%9E%B6%E6%A7%8B%E5%9C%96.png
圖二-程式架構圖
%E8%B2%AA%E9%A3%9F%E8%9B%87%E9%81%8A%E6%88%B2%E6%B5%81%E7%A8%8B%E5%9C%96.png
圖三-遊戲流程圖
%E7%A8%8B%E5%BC%8F%E6%B5%81%E7%A8%8B.png
圖四-程式流程
%E9%81%8A%E6%88%B2%E7%8B%80%E6%85%8B%E5%9C%96.png
圖五-遊戲狀態圖
以下為貪食蛇遊戲的程式碼:
專案下載:GameSnake1204_final2.rar

主程式:GameSnake.java

package ccc.GameSnake;
 
import android.app.Activity;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.PowerManager;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.Window;
import android.view.WindowManager;
import android.view.KeyEvent;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
 
public class GameSnake extends Activity {
    SurfaceView gameSurfaceView;
    SurfaceHolder surfaceHolder;
    Thread gameThread;
    Boolean isGameThreadStop = true;
    GameObj backimg;
    int gameFPS = 25;
    KeyHandler keyHandler = new KeyHandler();
    TouchPoint touchPoint = new TouchPoint();
    PowerManager.WakeLock wakeLock;
    drawAction nowDrawWork;
    SnakeObj snake;
    AppleObj apple;
    GameStat gameStat;
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        // 隱藏狀態列
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        // 隱藏視窗標題
        requestWindowFeature(Window.FEATURE_NO_TITLE);
 
        // 防止手機因手持方向不同 而觸發螢幕方向旋轉
        setRequestedOrientation(Configuration.ORIENTATION_PORTRAIT);
 
        // 電源管理服務取得
        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
        wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK,
                "GameSnake PowerControl");
 
        gameSurfaceView = new SurfaceView(this);
        surfaceHolder = gameSurfaceView.getHolder();
        surfaceHolder.addCallback(new SurfaceHolder.Callback() {
            public void surfaceDestroyed(SurfaceHolder arg0) {
            }
 
            public void surfaceCreated(SurfaceHolder arg0) {
                if (backimg == null) {
                    // 第一次Activity載入時
                    Resources rs = getResources();
                    backimg = new GameObj(rs.getDrawable(R.drawable.backimg));
                    SurfaceView sv = gameSurfaceView;
                    backimg.setRect(new Rect(sv.getLeft(), sv.getTop(), sv
                            .getRight(), sv.getBottom()));
                    readyGame();
                } else {
                    // 經由Activity返回載入時
                    draw(nowDrawWork);
                    openOptionsMenu();
 
                }
            }
 
            public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2,
                    int arg3) {
 
            }
        });
        setContentView(gameSurfaceView);
    }
 
    /**
     * 電源控制 防止進入休眠狀態切換
     */
    protected void powerControl(boolean needWake) {
        if (needWake && !wakeLock.isHeld()) {
            wakeLock.acquire();
        } else if (!needWake && wakeLock.isHeld()) {
            wakeLock.release();
        }
 
    }
 
    @Override
    protected void onPause() {
        pauseGame();
        super.onPause();
    };
 
    protected static final int MENU_Resume = Menu.FIRST;
    protected static final int MENU_Reset = Menu.FIRST + 1;
    protected static final int MENU_Quit = Menu.FIRST + 2;
 
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        menu.add(0, MENU_Resume, 0, "繼續");
        menu.add(0, MENU_Reset, 0, "重新開始");
        menu.add(0, MENU_Quit, 0, "離開");
        return super.onCreateOptionsMenu(menu);
    };
 
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case MENU_Resume:
            resumeGame();
            break;
        case MENU_Reset:
            readyGame();
            break;
        case MENU_Quit:
            gameExit();
            break;
 
        }
        return super.onOptionsItemSelected(item);
    }
 
    @Override
    public boolean onMenuOpened(int featureId, Menu menu) {
        pauseGame();
        return super.onMenuOpened(featureId, menu);
    };
 
    void gameExit() {
        gameThreadStop();
        if (gameThread != null) {
            try {
                gameThread.join();
            } catch (InterruptedException e) {
 
            }
        }
        finish();// 結束遊戲
    }
 
    @Override
    public boolean onTouchEvent(android.view.MotionEvent event) {
        if (nowDrawWork == drawAction.game)
            touchPoint.update(event);
        return true;
    };
 
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        keyHandler.keyDown(keyCode);
        return super.onKeyDown(keyCode, event);
 
    }
 
    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        keyHandler.keyUp(keyCode);
        return super.onKeyUp(keyCode, event);
    }
 
    public void gameThreadStart() {
        isGameThreadStop = false;
        powerControl(true);
        if (gameThread == null) {
            gameThread = new Thread(gameRun);
            gameThread.start();
        } else if (!gameThread.isAlive()) {
            gameThread = new Thread(gameRun);
            gameThread.start();
        }
    }
 
    public void gameThreadStop() {
        isGameThreadStop = true;
        powerControl(false);
    }
 
    // 準備遊戲
    void readyGame() {
        gameThreadStop();
        nowDrawWork = drawAction.ready;
        Resources rs = getResources();
        snake = new SnakeObj(GameSnake.this, backimg.getRect());
        apple = new AppleObj(rs.getDrawable(R.drawable.apple), backimg
                .getRect());
        apple.random(backimg.getRect());
        gameStat = new GameStat(System.currentTimeMillis() + 3000);
        gameThreadStart();
    }
 
    // 開始遊戲
    void startGame() {
        gameStat = new GameStat(System.currentTimeMillis() + 30000);
        nowDrawWork = drawAction.game;
    }
 
    // 暫停遊戲
    void pauseGame() {
        gameThreadStop();
        if (nowDrawWork != drawAction.over) {
            gameStat.timePause();
            draw(drawAction.pause);
        }
 
    }
 
    // 繼續遊戲
    void resumeGame() {
        if (nowDrawWork != drawAction.over) {
            gameThreadStart();
            gameStat.timeResume();
        }
    }
 
    Runnable gameRun = new Runnable() {
        public void run() {
            long delayTime = 1000 / gameFPS;
            while (!isGameThreadStop) {
                long startTime = System.currentTimeMillis();
                if (nowDrawWork == drawAction.game)
                    gameUpdate();
                draw(nowDrawWork);
                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() {
        boolean isChangeMove = false;
 
        // 觸控事件處理
        if (touchPoint.isChangeVector) {
            snake.move(touchPoint.lastVectorX, touchPoint.lastVectorY);
            isChangeMove = true;
        } else {
            // 按鍵事件處理
            if (isKeyDown(KeyEvent.KEYCODE_DPAD_RIGHT)) {
                snake.move(1, 0);
                isChangeMove = true;
            }
            if (isKeyDown(KeyEvent.KEYCODE_DPAD_LEFT)) {
                snake.move(-1, 0);
                isChangeMove = true;
            }
            if (isKeyDown(KeyEvent.KEYCODE_DPAD_UP)) {
                snake.move(0, -1);
                isChangeMove = true;
            }
            if (isKeyDown(KeyEvent.KEYCODE_DPAD_DOWN)) {
                snake.move(0, 1);
                isChangeMove = true;
            }
        }
 
        // 沒有改變移動則往之前方向移動
        if (!isChangeMove)
            snake.move();
 
        // 更新貪食蛇
        snake.update();
 
        // 吃到蘋果處理
        if (snake.isEatApple(apple)) {
            // 增加長度
            snake.add();
            // 增加時間
            gameStat.addTime(3000);
 
            // 蘋果位置變更
            while (snake.isEatApple(apple))
                apple.random(backimg.getRect());
        }
        // 更新遊戲分數
        gameStat.updateScroe(snake.getLength());
 
        // 判斷是否結束遊戲
        if (gameStat.isTimeOver())
            nowDrawWork = drawAction.over;
    }
 
    // 畫面繪圖種類
    enum drawAction {
        ready, game, pause, over
    }
 
    // 畫面繪圖處理
    void draw(drawAction action) {
        Canvas canvas = null;
        try {
            canvas = surfaceHolder.lockCanvas(null);
            synchronized (surfaceHolder) {
                draw(action, canvas);
            }
        } finally {
            if (canvas != null) {
                surfaceHolder.unlockCanvasAndPost(canvas);
            }
        }
    }
 
    // 畫面繪圖函數選擇
    void draw(drawAction action, Canvas canvas) {
        switch (action) {
        case ready:
            drawReady(canvas);
            break;
        case game:
            drawGame(canvas);
            break;
        case pause:
            drawPause(canvas);
            break;
        case over:
            drawOver(canvas);
            break;
        }
    }
 
    // 畫準備開始
    void drawReady(Canvas canvas) {
        clear(canvas);
        Paint pt = new Paint();
        pt.setTextAlign(Paint.Align.CENTER);
        pt.setARGB(255, 0, 0, 255);
        pt.setTextSize(30);
        canvas.drawText(gameStat.getCountdownTime() + "秒後遊戲開始-", backimg
                .centerX(), backimg.centerY(), pt);
        if (gameStat.isTimeOver())
            startGame();
    }
 
    // 畫遊戲中
    void drawGame(Canvas canvas) {
        clear(canvas);
        apple.draw(canvas);
        snake.draw(canvas);
        gameStat.draw(canvas);
        touchPoint.draw(canvas);
    }
 
    // 畫暫停
    void drawPause(Canvas canvas) {
        draw(nowDrawWork, canvas);
        Paint pt = new Paint();
        pt.setARGB(30, 0, 0, 100);
        canvas.drawRect(backimg.getRect(), pt);
        pt.setTextAlign(Paint.Align.CENTER);
        pt.setARGB(150, 200, 200, 200);
        pt.setTextSize(50);
        canvas.drawText("-遊戲暫停-", backimg.centerX(), backimg.centerY(), pt);
    }
 
    // 畫遊戲結束
    void drawOver(Canvas canvas) {
        // 執行緒停止
        gameThreadStop();
        drawGame(canvas);
        Paint pt = new Paint();
        pt.setARGB(30, 30, 30, 30);
        canvas.drawRect(backimg.getRect(), pt);
        pt.setTextAlign(Paint.Align.CENTER);
        pt.setARGB(100, 0, 0, 255);
        pt.setTextSize(50);
        canvas.drawText("-遊戲結束-", backimg.centerX(), backimg.centerY(), pt);
    }
 
    void clear(Canvas canvas) {
        Paint p = new Paint();
        p.setARGB(100, 0, 0, 0);
        backimg.draw(canvas);
    }
 
}

程式:GameObj.java

package ccc.GameSnake;
 
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
public class GameObj {
    /**
     * 物件顯示角度
     */
    public float angle;
 
    /**
     * 物件影像資源
     */
    public Drawable drawable;
 
    /**
     * 是否顯示
     */
    public boolean Visible = true;
 
    /**
     * 控制致能
     */
    public boolean Enable = true;
 
    /**
     * 暫存的物件位置
     */
    private Rect saveRect;
 
    /**
     * 暫存的物件角度
     */
    public float saveAngle;
 
    public GameObj(Drawable drawable) {
        this.drawable = drawable;
        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable
                .getIntrinsicHeight());
        this.save();
    }
    public GameObj(GameObj gameObj,Drawable drawable) {
        this.drawable = drawable;
        this.drawable.setBounds(gameObj.drawable.copyBounds());
        this.angle=gameObj.angle;
        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 moveTo(float newLeft, float newTop) {
        moveTo((int)newLeft,(int)newTop);
    }
 
    /**
     * 物件移動一個向量距離
     */
    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 move(float dx, float dy) {
        move((int)dx,(int)dy);
    }
 
    /**
     * 物件範圍縮放
     */
    public void scale(int addScaleX, int addScaleY) {
        if (Enable) {
            Rect rect = drawable.getBounds();
            drawable.setBounds(rect.left - addScaleX, rect.top - addScaleY,
                    rect.right + addScaleX, rect.bottom + addScaleY);
        }
    }
 
    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());
    }
 
    /**
     * 判斷物件範圍是否包函與參數點
     */
    public boolean contains(int x,int y) {
        return drawable.getBounds().contains(x, y);
    }
}

程式:SnakeObj.java

package ccc.GameSnake;
 
import java.util.ArrayList;
import java.util.List;
 
import android.app.Activity;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
 
/**
 * @author Administrator
 * 
 */
public class SnakeObj {
 
    private class exGameObj extends GameObj {
 
        /**
         *下次更新時的座標點位置
         */
        public PointF nextMove = new PointF();
 
        /**
         * 歷史的座標點位置
         */
        public PointF[] logPath = new PointF[3];
 
        public exGameObj(Drawable drawable) {
            super(drawable);
            init();
        }
 
        public exGameObj(exGameObj gameObj, Drawable drawable) {
            super(gameObj, drawable);
            init();
        }
 
        /**
         * 物件初始化
         */
        private void init() {
            for (int i = 0; i < logPath.length; i++) {
                logPath[i] = new PointF();
            }
        }
 
        /**
         *進行座標點更新
         */
        public void updataMove() {
 
            // 取得位置移動向量
            float dx = nextMove.x - logPath[0].x;
            float dy = nextMove.y - logPath[0].y;
 
            // 歷史座標更新
            for (int i = logPath.length - 1; i > 0; i--) {
                logPath[i].set(logPath[i - 1].x, logPath[i - 1].y);
            }
            logPath[0].set(nextMove.x, nextMove.y);
 
            // 座標點更新
            this.moveTo(nextMove.x - this.getWidth() / 2, nextMove.y
                    - this.getHeight() / 2);
 
            // 判斷移動向量距離大於5以上進行角度更新
            if (dx * dx + dy * dy > 4 * 4) {
                this.angle = (float) (Math.atan2(dy, dx) * 180 / Math.PI);
            }
        }
    }
 
    /**
     * 頭部物件
     */
    private exGameObj head;
 
    /**
     * 身體物件
     */
    private List<exGameObj> bodys = new ArrayList<exGameObj>();
 
    /**
     * 尾部物件
     */
    private exGameObj tail;
 
    /**
     * 資源檔
     */
    private Resources rs;
 
    /**
     * 活動限制範圍
     */
    private Rect actRect;
 
    /**
     * 目標移動向量X
     */
    private float dstVectorX = 1;
 
    /**
     * 目標移動向量Y
     */
    private float dstVectorY = 0;
 
    public SnakeObj(Activity content, Rect actRect) {
        this.actRect = actRect;
        rs = content.getResources();
        // 頭部影像資源
        Drawable d_head = rs.getDrawable(R.drawable.head);
        // 身體影像資源
        Drawable d_body = rs.getDrawable(R.drawable.body);
        // 尾部影像資源
        Drawable d_tail = rs.getDrawable(R.drawable.tail);
 
        //初始化貪食蛇物件
        head = new exGameObj(d_head);
        exGameObj body = new exGameObj(d_body);
        tail = new exGameObj(d_tail);
        init(head);
        init(body);
        init(tail);
        bodys.add(body);
    }
 
    /**
     * 設定物件起始位置
     */
    private void init(exGameObj obj) {
        float x = actRect.centerX();
        float y = actRect.centerY();
        obj.nextMove.set(x, y);
        for (int i = 0; i < obj.logPath.length; i++) {
            obj.logPath[i].set(x, y);
        }
    }
 
    /**
     * 得到目前身體長度
     */
    public int getLength() {
        return bodys.size() - 1;
    }
 
    /**
     * 畫出貪食蛇
     */
    public void draw(Canvas canvas) {
        tail.draw(canvas);
        for (int i = 0; i < bodys.size(); i++) {
            bodys.get(bodys.size() - 1 - i).draw(canvas);
        }
        head.draw(canvas);
    }
 
    /**
     * 進行貪食蛇更新
     */
    public void update() {
 
        //更新尾部位置
        updataMove(bodys.get(bodys.size() - 1), tail);
 
        //更新身體位置
        for (int i = bodys.size() - 1; i >= 0; i--) {
            exGameObj moveObj = bodys.get(i);
            if (i == 0) {
                updataMove(head, moveObj);
            } else {
                updataMove(bodys.get(i - 1), moveObj);
            }
        }
 
        //更新頭部位置
        head.updataMove();
 
        //頭與身體相撞處理
        for (int i = 1; i < bodys.size(); i++) {
            Rect h = new Rect(head.getRect());
            Rect b = new Rect(bodys.get(i).getRect());
 
            //碰撞偵測微調
            scaleRect(h, -5, -5);
            scaleRect(b, -10, -10);
 
            if (Rect.intersects(h, b)) {
                this.cut();
                break;
            }
        }
 
    }
 
    //更新貪食蛇物件節點的移動
    private void updataMove(exGameObj fd, exGameObj bk) {
        bk.updataMove();
        PointF fwp = fd.logPath[fd.logPath.length - 1];
        bk.nextMove.set(fwp.x, fwp.y);
    }
 
    /**
     * 範圍縮放長寬調整
     * @param rect
     * @param scaleX
     * @param scaleY
     */
    private void scaleRect(Rect rect, int scaleX, int scaleY) {
        rect.set(rect.left - scaleX, rect.top - scaleY, rect.right + scaleX,
                rect.bottom + scaleY);
    }
 
    /**
     * 範圍縮放長寬調整
     */
    private void scaleRect(RectF rect, int scaleX, int scaleY) {
        rect.set(rect.left - scaleX, rect.top - scaleY, rect.right + scaleX,
                rect.bottom + scaleY);
    }
 
    /**
     * 得到一個-180~180之間的角度
     */
    private float getAngle(float angle, float addAngle) {
        angle += addAngle;
        angle %= 360;
        if (angle > 180)
            angle -= 360;
        if (angle < -180)
            angle += 360;
        return angle;
    }
 
    /**
     * 設定移動向量
     * @param dx
     * 移動向量X
     * @param dy
     * 移動向量Y
     */
    public void move(float dx, float dy) {
        this.dstVectorX = dx;
        this.dstVectorY = dy;
        //目標旋轉角度
        float rotateAngle = getAngleByXY(dx, dy);
        //限制更新角度大小
        float limitAngle = 25;
        //左旋角度最大值(逆時鐘)
        float limitLeftAngle = getAngle(head.angle, limitAngle);
        //右旋角度最大值(順時鐘)
        float limitRightAngle = getAngle(head.angle, -limitAngle);
 
        //當更新目標點角度大於限制可選轉角度時 進行旋轉角度調整
        if (Math.abs(getAngle(rotateAngle, -head.angle)) > limitAngle) {            
            if (getAngle(rotateAngle, -head.angle) > 0) {
                //左旋時
                rotateAngle = limitLeftAngle;
            } else {
                //右旋時
                rotateAngle = limitRightAngle;
            }
        }
 
        //設定頭部方向
        head.angle = rotateAngle;
 
        //取得頭部下次更新座標
        double dreg = head.angle * Math.PI / 180;
        int moveDistance=6;
        dx = (float) Math.cos(dreg) * moveDistance;
        dy = (float) Math.sin(dreg) * moveDistance;
        float ndx = head.logPath[0].x + dx;
        float ndy = head.logPath[0].y + dy;
 
        //取得邊緣範圍
        RectF limitRect = new RectF(actRect);
 
        //取得物件可移動範圍
        scaleRect(limitRect, -head.getWidth() / 2, -head.getHeight() / 2);
 
        //進行邊緣碰撞偵測調整
        if (!limitRect.contains(ndx, ndy)) {
            boolean isTouchEdge = false;
            if (ndx < limitRect.left) {//左邊邊緣偵測
                if (head.angle < 0)
                    head.angle = limitLeftAngle;
                else
                    head.angle = limitRightAngle;
                ndx = limitRect.left;
                isTouchEdge = true;
            }
            if (ndx > limitRect.right) {//右邊邊緣偵測
                if (head.angle > 0)
                    head.angle = limitLeftAngle;
                else
                    head.angle = limitRightAngle;
                ndx = limitRect.right;
                isTouchEdge = true;
            }
 
            if (ndy < limitRect.top) {//頂部邊緣偵測
                if (head.angle > -90)
                    head.angle = limitLeftAngle;
                else
                    head.angle = limitRightAngle;
                ndy = limitRect.top;
                isTouchEdge = true;
            }
            if (ndy > limitRect.bottom) {//底部邊緣偵測
                if (head.angle > 90)
                    head.angle = limitLeftAngle;
                else
                    head.angle = limitRightAngle;
                ndy = limitRect.bottom;
                isTouchEdge = true;
            }
 
            if (isTouchEdge) {
                //減短長度
                this.cut();
 
                //調整目標向量
                this.dstVectorX = (float) Math.cos(head.angle * Math.PI / 180);
                this.dstVectorY = (float) Math.sin(head.angle * Math.PI / 180);
            }
        }
        //下次頭部移動點設置
        head.nextMove.set(ndx, ndy);
 
    }
 
    //移動之前方向
    public void move() {
        move(this.dstVectorX, this.dstVectorY);
    }
 
    //取得直角坐標角度
    private float getAngleByXY(float dx, float dy) {
        return (float) (Math.atan2(dy, dx) * 180 / Math.PI);
    }
 
    //增加身體長度
    public void add() {
        exGameObj newBody = new exGameObj(tail, rs.getDrawable(R.drawable.body));
        newBody.nextMove.set(tail.nextMove.x, tail.nextMove.y);
 
        for (int i = 0; i < tail.logPath.length; i++) {
            newBody.logPath[i].set(tail.logPath[i].x, tail.logPath[i].y);
            tail.logPath[i].set(tail.nextMove.x, tail.nextMove.y);
        }
        bodys.add(newBody);
 
    }
 
    //從第N個開始剪斷
    public void cut(int bodyIndex) {
        if (bodyIndex>0&&bodyIndex < bodys.size()) {
            exGameObj lastBody = bodys.get(bodyIndex-1);
            tail.setRect(lastBody.getRect());
            tail.nextMove.set(lastBody.nextMove.x, lastBody.nextMove.y);
            for (int i = 0; i < tail.logPath.length; i++) {
                tail.logPath[i].set(lastBody.logPath[i].x,
                        lastBody.logPath[i].y);
            }
 
            for (int i = bodyIndex; i < bodys.size(); i++) {
                    bodys.remove(bodys.size() - 1);
            }
 
        }
 
    }
 
    //減少一個身體長度
    public void cut() {
        cut(bodys.size() - 1);
    }
 
    //判斷是否有吃到蘋果
    public boolean isEatApple(GameObj apple) {
        return Rect.intersects(apple.getRect(), head.getRect());
    }
 
}

程式:AppleObj.java

package ccc.GameSnake;
 
import java.util.Random;
 
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
 
public class AppleObj extends GameObj{
    private Random r=new Random();
    private Rect actRect;
    public AppleObj(Drawable drawable,Rect limitRect) {
        super(drawable);
        this.actRect=limitRect;
    }
 
    /**
     * 物件移動到隨機區域
     */
    public void random(Rect limitRect){
        this.actRect=limitRect;
        this.moveTo(actRect.left+r.nextInt(actRect.width()-this.getWidth()),actRect.top+r.nextInt(actRect.height()-this.getHeight()));    
    }
 
    /**
     * 物件移動到隨機區域
     */
    public void random(){
        this.random(this.actRect);
    }
}

程式:GameStat.java

package ccc.GameSnake;
 
import android.graphics.Canvas;
import android.graphics.Paint;
 
public class GameStat {
    private Long startTime=0l;
    private Long overTime=0l;
    private Long pauseTime=0l;
    private boolean isTimePause=false;
    private int  gameScore;
 
    public GameStat(long gameOverTime){
        this.startTime=System.currentTimeMillis();
        this.overTime=gameOverTime;
    }
 
    public void updateScroe(int gameScore){
        this.gameScore=gameScore;
    }
 
    public void draw(Canvas canvas){
        Paint pt=new Paint();
        pt.setARGB(150, 255, 0, 0);
        pt.setTextSize(24);
        canvas.drawText("分數:"+this.gameScore, 20, 40, pt);
        canvas.drawText("剩餘時間:"+getCountdownTime(), 20, 80, pt);
    }
 
    /**
     * 得到距離結束時間秒數
     */
    public int getCountdownTime(){
        if(!isTimeOver()){
            if(isTimePause)
                return (int)((this.overTime-pauseTime)/1000)+1;
            else
                return (int)((this.overTime-System.currentTimeMillis())/1000)+1;
        }
        else{
            return 0;
        }
    }
 
    /**
     *增加時間
     */
    public void addTime(int addMicroseconds){
        overTime+=addMicroseconds;
    }
    public boolean isTimeOver(){
        if(isTimePause)
            return pauseTime>overTime;
        else
            return System.currentTimeMillis()>overTime;
    }
 
    /**
     * 時間暫停
     */
    public void timePause(){
        if(!isTimePause){
            pauseTime=System.currentTimeMillis();
        }
        isTimePause=true;
    }
 
    /**
     * 得到是否為時間暫停狀態
     */
    public boolean isTimePause(){
        return this.isTimePause;
    }
 
    /**
     * 時間繼續
     */
    public void timeResume(){
        if(isTimePause){
            overTime=System.currentTimeMillis()+overTime-pauseTime;
        }
        isTimePause=false;
    }
 
}

程式:KeyHandler.java

package ccc.GameSnake;
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 isKeyUp=MapKeyDown.get(keyCode);
    if(isKeyUp!=null)
        return isKeyUp;
    else
        return false;    
}
public boolean isKeyUp(int keyCode){
    Boolean isKeyUp=MapKeyDown.get(keyCode);
    if(isKeyUp!=null)
        return isKeyUp;
    else
        return true;
}
}

程式:TouchPoint.java

package ccc.GameSnake;
 
import java.util.ArrayList;
import java.util.List;
 
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PointF;
 
public class TouchPoint {
 
    //最後偵測向量X
    public float lastVectorX;
    //最後偵測向量Y
    public float lastVectorY;
 
    //是否有改變向量
    public boolean isChangeVector=false;
 
    //觸控點座標
    List<PointF> points = new ArrayList<PointF>();
 
    public TouchPoint(){
    }
 
    //處理觸控點座標
    public void update(android.view.MotionEvent event){
        for (int i = 0; i < event.getHistorySize(); i+=3) {
            points.add(new PointF(event.getHistoricalX(i), event
                    .getHistoricalY(i)));
        }
 
        changeVector();
    }
 
    //改變偵測觸控向量
    private void changeVector(){
        if (points.size() > 1) {//感測2點以上
            this.lastVectorX = points.get(points.size() - 1).x
                    - points.get(0).x;
            this.lastVectorY = points.get(points.size() - 1).y
                    - points.get(0).y;
        }
        isChangeVector=true;
    }
 
    //畫出觸控點路徑
    public void draw(Canvas canvas){
        if (points.size() > 1) {//感測2點以上
            Paint p = new Paint();
            p.setARGB(255, 0, 0, 0);
            p.setStrokeWidth(3);
            for (int i = 0; i < points.size() - 1; i++) {
                float x1=points.get(i).x;
                float y1=points.get(i).y;
                float x2=points.get(i+1).x;
                float y2=points.get(i+1).y;
                canvas.drawLine(x1,y1,x2,y2, p);
            }
        }
        this.resetPoint();
    }
 
    //重新偵測觸控向量
    public void resetPoint(){
        isChangeVector=false;
        points.clear();
    }
 
}

AndroidManifest.xml為使用權限:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      android:versionCode="1"
      package="ccc.GameSnake" android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name" >
        <activity android:name="GameSnake"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
 
    </application>
    <uses-sdk android:minSdkVersion="4" />
 
<!-- 取得電源控制權限  防止待命使用 -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
</manifest>

程式影片解說

程式手機執行影片