多人衛星定位遊戲-殭屍大戰

遊戲玩法:一開始玩家皆是人類,待遊戲等待時間結束後,會隨機跳選一名玩家為殭屍,只要被殭屍抓到的人類,也會被感染成為殭屍,並且一起感染所剩的人類倖存者,殭屍於100秒限時內感染全部人類,即可獲勝,反之,於時間結束仍有人類倖存者,則為人類獲勝。
此程式為前面範例所使用的技術結合製作的多人GPS遊戲,下圖為遊戲開始畫面、遊戲進行畫面、開發流程圖、程式流程圖、伺服器程式流程圖。
%E5%A4%9A%E4%BA%BA%E9%81%8A%E6%88%B2%E6%A8%99%E9%A1%8C.PNG
圖一-遊戲開始畫面

%E5%A4%9A%E4%BA%BA%E9%81%8A%E6%88%B2%E7%95%AB%E9%9D%A2.PNG
圖二-遊戲進行畫面
%E6%AE%AD%E5%B1%8D%E5%A4%A7%E6%88%B0%E9%96%8B%E7%99%BC%E6%B5%81%E7%A8%8B.png
圖三-開發流程
%E6%AE%AD%E5%B1%8D%E5%A4%A7%E6%88%B0%E7%A8%8B%E5%BC%8F%E6%B5%81%E7%A8%8B%E5%9C%96.png
圖四-程式流程圖
%E6%AE%AD%E5%B1%8D%E5%A4%A7%E6%88%B0%E4%BC%BA%E6%9C%8D%E5%99%A8%E7%A8%8B%E5%BC%8F%E6%B5%81%E7%A8%8B%E5%9C%96.png
圖五-伺服器程式流程圖

以下為殭屍大戰程式碼:
專案下載:ZombieWars(內含殭屍大戰、客戶測試、伺服器).rar
主程式:ZombieWars.java

package ccc.ZombieWars;
 
import java.security.KeyStore.LoadStoreParameter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
 
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.Projection;
import com.google.android.maps.MapView.LayoutParams;
 
import ccc.ZombieWars.R;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Display;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.KeyEvent;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.Toast;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
 
public class ZombieWars extends MapActivity {
 
    Thread mainThread;
    Boolean isMainThreadStop = true;
    int updateFPS = 30;
    KeyHandler keyHandler = new KeyHandler();
    PowerManager.WakeLock wakeLock;
 
    MapView mapView;
    MapController mapController;
    Rect screenRect;
    PlayerObj player;
    GpsSensorObj gpsSendor;
 
    Handler handler = new Handler();
    List<Runnable> updateSet = new ArrayList<Runnable>();
    SingleGameCore singleGameCore = null;
    MultiplayerGameCore multiplayerGameCore = null;
 
    @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,
                "Zombie PowerControl");
 
        WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
        Display display = wm.getDefaultDisplay();
        screenRect = new Rect(0, 0, display.getWidth(), display.getHeight());
 
        String ApiKey = "03rGqE_CW0JtuagXBUsB8Zxijve-Y131pYz1jqA";
        mapView = new MapView(this, ApiKey);
        // 設定衛星圖
        mapView.setSatellite(true);
        mapController = mapView.getController();
 
        GeoPoint point = new GeoPoint(24448541, 118322206);
        mapController.setCenter(point);
        mapController.setZoom(20);
        mapView.setDrawingCacheEnabled(true);
 
        setContentView(mapView);
 
        player = new PlayerObj(this, mapView);
        gpsSendor = new GpsSensorObj(this, mapView);
        mainThreadStart();
 
    }
 
    /**
     * 電源控制 防止進入休眠狀態切換
     */
    protected void powerControl(boolean needWake) {
        if (needWake && !wakeLock.isHeld()) {
            wakeLock.acquire();
        } else if (!needWake && wakeLock.isHeld()) {
            wakeLock.release();
        }
 
    }
 
    @Override
    public boolean onTouchEvent(android.view.MotionEvent event) {
 
        // player.setGeoPoint(mapView.getProjection().fromPixels((int)event.getX(),
        // (int)event.getY()));
        gpsSendor.geoPoint = mapView.getProjection().fromPixels(
                (int) event.getX(), (int) event.getY());
 
        return true;
    };
 
    @Override
    protected void onPause() {
 
        super.onPause();
    };
 
    protected static final int MENU_ZoomIn = Menu.FIRST;
    protected static final int MENU_ZoomOut = Menu.FIRST + 1;
    protected static final int MENU_Exit = Menu.FIRST + 2;
    protected static final int MENU_SingleGameStart = Menu.FIRST + 3;
    protected static final int MENU_MultiplayerGameStart = Menu.FIRST + 4;
    protected static final int MENU_MultiplayerSendGameStart = Menu.FIRST + 5;
    protected static final int MENU_CancelGame = Menu.FIRST + 6;
 
    @Override
    public boolean onMenuOpened(int featureId, Menu menu) {
        menu.removeItem(MENU_MultiplayerSendGameStart);
        if (singleGameCore == null && multiplayerGameCore == null) {
            menu.removeItem(MENU_CancelGame);
            if (menu.findItem(MENU_SingleGameStart) == null)
                menu.add(1, MENU_SingleGameStart, 0, "單人遊戲");
            if (menu.findItem(MENU_MultiplayerGameStart) == null){
                menu.add(1, MENU_MultiplayerGameStart, 0, "多人遊戲");
            }
        } else {
            menu.removeItem(MENU_SingleGameStart);
            menu.removeItem(MENU_MultiplayerGameStart);            
            if (menu.findItem(MENU_CancelGame) == null)
                menu.add(1, MENU_CancelGame, 0, "取消遊戲");
            if(multiplayerGameCore!=null)
                if(multiplayerGameCore.canSendStartGame()&&menu.findItem(MENU_MultiplayerSendGameStart) == null)
                        menu.add(1,MENU_MultiplayerSendGameStart, 0, "遊戲開始");
        }
        return super.onMenuOpened(featureId, menu);
    };
 
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        menu.add(0, MENU_ZoomIn, 0, "放大");
        menu.add(0, MENU_ZoomOut, 0, "縮小");
        menu.add(1, MENU_Exit, 0, "離開");
        return super.onCreateOptionsMenu(menu);
    };
 
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
 
        switch (item.getItemId()) {
        case MENU_ZoomIn:
            mapController.zoomIn();
 
            break;
        case MENU_ZoomOut:
            mapController.zoomOut();
            break;
        case MENU_Exit:
            exit();
            break;
        case MENU_SingleGameStart:
            if (singleGameCore == null) {
                singleGameCore = new SingleGameCore(this);
            }
            break;
        case MENU_MultiplayerGameStart:
            if (multiplayerGameCore == null) {
                multiplayerGameCore = new MultiplayerGameCore(this);
            }
            break;
        case MENU_MultiplayerSendGameStart:
            if (multiplayerGameCore != null) {
                multiplayerGameCore.sendStartGame();
            }
            break;
        case MENU_CancelGame:
            if (singleGameCore != null) {
                singleGameCore.remove();
            }
            if (multiplayerGameCore != null) {
                multiplayerGameCore.remove();
            }
            break;
        }
 
        return super.onOptionsItemSelected(item);
    }
 
    void exit() {
        if (singleGameCore != null) {
            singleGameCore.remove();
        }
        if (multiplayerGameCore != null) {
            multiplayerGameCore.remove();
        }
        mainThreadStop();
        if (mainThread != null) {
            try {
                mainThread.join();
            } catch (InterruptedException e) {
 
            }
        }
        finish();// 結束遊戲
    }
 
    @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 mainThreadStart() {
        isMainThreadStop = false;
        powerControl(true);
        if (mainThread == null) {
            mainThread = new Thread(threadRun);
            mainThread.start();
        } else if (!mainThread.isAlive()) {
            mainThread = new Thread(threadRun);
            mainThread.start();
        }
    }
 
    public void mainThreadStop() {
        isMainThreadStop = true;
        powerControl(false);
    }
 
    Runnable threadRun = new Runnable() {
        public void run() {
            long delayTime = 1000 / updateFPS;
            while (!isMainThreadStop) {
                long startTime = System.currentTimeMillis();
                handler.post(mainUpdate);
                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);
    }
 
    Runnable mainUpdate = new Runnable() {
        public void run() {
            if (isKeyDown(KeyEvent.KEYCODE_DPAD_RIGHT)) {
                mapController.scrollBy(10, 0);
            }
            if (isKeyDown(KeyEvent.KEYCODE_DPAD_LEFT)) {
                mapController.scrollBy(-10, 0);
 
            }
            if (isKeyDown(KeyEvent.KEYCODE_DPAD_UP)) {
                mapController.scrollBy(0, -10);
 
            }
            if (isKeyDown(KeyEvent.KEYCODE_DPAD_DOWN)) {
                mapController.scrollBy(0, 10);
            }
 
            updateSetRun();
 
            Point gpsPoint = new Point();
            Point playPoint = new Point();
            Projection projection = mapView.getProjection();
            projection.toPixels(gpsSendor.geoPoint, gpsPoint);
            projection.toPixels(player.drawGeoPoint, playPoint);
 
            int dx = gpsPoint.x - playPoint.x;
            int dy = gpsPoint.y - playPoint.y;
 
            if (dx * dx + dy * dy > player.getWidth() * player.getHeight()
                    * 0.2) {
                player.animateTo(gpsSendor.geoPoint);
            }
 
            dx = screenRect.centerX() - gpsPoint.x;
            dy = screenRect.centerY() - gpsPoint.y;
            if (dx * dx + dy * dy > 60 * 60) {
                gpsSendor.autoUpdateMap(true);
            } else {
                gpsSendor.autoUpdateMap(false);
            }
 
            mapView.invalidate();
 
        }
    };
 
    void updateSetRun() {
        if (updateSet.size() > 0) {
            Runnable[] updateArray = new Runnable[updateSet.size()];
            updateSet.toArray(updateArray);
            for (Runnable updateItem : updateArray) {
                updateItem.run();
            }
        }
    }
 
    @Override
    protected boolean isRouteDisplayed() {
 
        return false;
    }
 
}

程式:GpsSensorObj.java

package ccc.ZombieWars;
 
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.util.Log;
 
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
 
public class GpsSensorObj extends Overlay {
 
    GeoPoint geoPoint;
    LocationManager locationManager;
 
    MapView mapView;
    public boolean enableAutoUpdateMap = false;
 
    public GpsSensorObj(Context context, MapView mapView) {
 
        locationManager = (LocationManager) context
                .getSystemService(Context.LOCATION_SERVICE);
        this.mapView = mapView;
        geoPoint = new GeoPoint(mapView.getMapCenter().getLatitudeE6(), mapView
                .getMapCenter().getLongitudeE6());
        mapView.getOverlays().add(this);
        locationManager.requestLocationUpdates(locationManager.GPS_PROVIDER, 0,
                0, locationListener);
 
    }
 
    LocationListener locationListener = new LocationListener() {
 
        public void onStatusChanged(String provider, int status, Bundle extras) {
 
        }
 
        public void onProviderEnabled(String provider) {
 
        }
 
        public void onProviderDisabled(String provider) {
 
        }
 
        public void onLocationChanged(Location location) {
            updateGeoPoint(location);
        }
    };
 
    void autoUpdateMap(boolean enable) {
        enableAutoUpdateMap = enable;
    }
 
    void updateGeoPoint(Location location) {
        if (location != null) {
            geoPoint = new GeoPoint((int) (location.getLatitude() * 1e6),
                    (int) (location.getLongitude() * 1e6));
            if (enableAutoUpdateMap) {
                mapView.getController().animateTo(geoPoint);
            }
        }
 
    }
 
    void updateGeoPoint() {
        updateGeoPoint(getLocation());
    }
 
    Location getLocation() {
        return locationManager
                .getLastKnownLocation(LocationManager.GPS_PROVIDER);
    }
 
    @Override
    public void draw(Canvas canvas, MapView mapView, boolean shadow) {
 
        Point point = new Point();
        mapView.getProjection().toPixels(geoPoint, point);
        Paint paint = new Paint();
        paint.setARGB(150, 255, 255, 255);
        paint.setStrokeWidth(3);
        canvas.drawLine(point.x - 10, point.y, point.x + 10, point.y, paint);
        canvas.drawLine(point.x, point.y - 10, point.x, point.y + 10, paint);
        canvas.drawText("GpsSensor", 20, 30, paint);
        canvas.drawText("緯度:" + geoPoint.getLatitudeE6() / 1e6, 20, 40, paint);
        canvas.drawText("經度:" + geoPoint.getLongitudeE6() / 1e6, 20, 50, paint);
        super.draw(canvas, mapView, shadow);
    }
}

程式:KeyHandler.java

package ccc.ZombieWars;
 
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;
    }
}

程式:MultiplayerGameCore.java

package ccc.ZombieWars;
 
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
 
import ccc.ZombieWars.GameStat.StatType;
import ccc.ZombieWars.net.PacketListener;
import ccc.ZombieWars.net.PacketModel;
import ccc.ZombieWars.net.PacketModel.PacketType;
import ccc.ZombieWars.net.client.ClientSocketHandler;
 
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
 
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Paint.Align;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.View;
 
public class MultiplayerGameCore extends View implements Runnable {
    int clientId = 0;
    GameStat gameStat;
    ZombieWars main;
    PlayerObj player;
    Map<Integer, PlayerObj> players = new HashMap<Integer, PlayerObj>();
    Map<Integer, PlayerObj> human = new HashMap<Integer, PlayerObj>();
    Map<Integer, PlayerObj> zombie = new HashMap<Integer, PlayerObj>();
    Drawable readyImg;
    Drawable humanWinImg;
    Drawable zombieWinImg;
    ClientSocketHandler socket = new ClientSocketHandler();
    long sendPacketTime = 0;
    boolean isGameStart = false;
    int sendLat = 0;
    int sendLng = 0;
    //String serverHost = "10.0.2.2";
    //String serverHost="192.168.60.8";
    String serverHost = "192.168.0.1";
    int serverPort = 443;
 
    public MultiplayerGameCore(ZombieWars context) {
        super(context);
        this.main = context;
        this.player = main.player;
        readyImg = main.getResources().getDrawable(R.drawable.ready);
        humanWinImg = main.getResources().getDrawable(R.drawable.humanwin);
        zombieWinImg = main.getResources().getDrawable(R.drawable.zombiewin);
 
        MapView.LayoutParams layout = new MapView.LayoutParams(
                MapView.LayoutParams.FILL_PARENT,
                MapView.LayoutParams.FILL_PARENT, 0, 0,
                MapView.LayoutParams.TOP_LEFT);
        gameStat = new GameStat(System.currentTimeMillis() + 3000);
        main.mapView.addView(this, layout);
        main.updateSet.add(this);
        socket.setPacketListener(packetListener);
        Thread connectThread = new Thread(connectServer);
        connectThread.start();
 
    }
 
    Runnable connectServer = new Runnable() {
        public void run() {
            gameStat.resetTime(System.currentTimeMillis() + 2000);
 
            socket.Connect(serverHost, serverPort);
            if (socket.isConnectError) {
                gameStat.resetTime(System.currentTimeMillis() + 3000);
            }
        }
    };
    PacketListener packetListener = new PacketListener() {
        public void receivePacket(PacketModel packetModel) {
            ReceivePacket receivePacket = new ReceivePacket(packetModel);
            main.handler.post(receivePacket);
        }
 
        public void closeCallback() {
            main.handler.post(closeCallback);
        }
    };
 
    class ReceivePacket implements Runnable {
        PacketModel packetModel;
        int id, lat, lng;
 
        public ReceivePacket(PacketModel packetModel) {
            this.packetModel = packetModel;
        }
 
        public void run() {
            switch (packetModel.getType()) {
            case getClientId:
                clientId = Integer.parseInt(packetModel.get("id"));
                addPlayer(clientId, player);
                break;
            case startGame:
                isGameStart = true;
                break;
            case countdownTimeMillis:
                if (gameStat.nowStatType == StatType.game) {
                    int countdownTimeMillis = Integer.parseInt(packetModel
                            .get("value"));
                    gameStat.resetTime(System.currentTimeMillis()
                            + countdownTimeMillis);
                }
                break;
            case overGame:
                gameStat.resetTime(System.currentTimeMillis() + 3000);
                String win = packetModel.get("win");
                if (win.equals("human"))
                    gameStat.nowStatType = StatType.humanWin;
                else
                    gameStat.nowStatType = StatType.zombieWin;
                printfPacket(packetModel);
                break;
            case updateGps:
                id = Integer.parseInt(packetModel.get("id"));
                lat = Integer.parseInt(packetModel.get("lat"));
                lng = Integer.parseInt(packetModel.get("lng"));
                updateGps(id, lat, lng);
                break;
            case addPlayer:
                id = Integer.parseInt(packetModel.get("id"));
                lat = Integer.parseInt(packetModel.get("lat"));
                lng = Integer.parseInt(packetModel.get("lng"));
                addPlayer(id, lat, lng);
                break;
            case removePlayer:
                id = Integer.parseInt(packetModel.get("id"));
                removePlayer(id);
                break;
            case killHuman:
                id = Integer.parseInt(packetModel.get("id"));
                killHuman(id);
                break;
 
            default:
                printfPacket(packetModel);
                break;
            }
        }
    }
 
    Runnable closeCallback = new Runnable() {
        public void run() {
            socket.isConnectError = true;
        }
    };
 
    void printfPacket(PacketModel packetModel) {
        for (String key : packetModel.keySet()) {
            Log.i("s:", "" + key + "=" + packetModel.get(key));
        }
    }
 
    void updateGps(int id, int lat, int lng) {
 
        PlayerObj updatePlayer = players.get(id);
        if(updatePlayer==null)
            updatePlayer=addPlayer(id, lat,lng);
        GeoPoint newPoint = new GeoPoint(lat, lng);
        updatePlayer.animateTo(newPoint);
    }
 
    PlayerObj addPlayer(int id, int lat, int lng) {
        PlayerObj newPlayer = new PlayerObj(main, main.mapView);
        newPlayer.srcZoom = player.srcZoom;
        GeoPoint newPoint = new GeoPoint(lat, lng);
        newPlayer.setGeoPoint(newPoint);
        addPlayer(id, newPlayer);
        return newPlayer;
    }
 
    void addPlayer(int id, PlayerObj newPlayer) {
        removePlayer(id);
        players.put(id, newPlayer);
        human.put(id, newPlayer);
    }
 
    void removePlayer(int id) {
        PlayerObj removePlayer = players.get(id);
        if (removePlayer != null&&removePlayer!=player) {
            players.remove(id);
            zombie.remove(id);
            human.remove(id);
            removePlayer.remove();
        }
 
    }
 
    boolean canSendStartGame() {
        if (socket.isConnect && !socket.isConnectError && !isGameStart
                && gameStat.nowStatType == StatType.game)
            return true;
        else
            return false;
    }
 
    void sendStartGame() {
        if (canSendStartGame()) {
            PacketModel packetModel = new PacketModel(PacketType.startGame);
            socket.sendPacket(packetModel);
        }
    }
 
    PlayerObj getPlayer(int clientId) {
        return players.get(clientId);
    }
 
    void killHuman(int clientId) {
        PlayerObj dieHuman = getPlayer(clientId);
        dieHuman.setImage(PlayerObj.ImageSyle.zombie);
        human.remove(clientId);
        zombie.put(clientId, dieHuman);
    }
 
    GeoPoint getGeoPoint(int x, int y) {
        return main.mapView.getProjection().fromPixels(x, y);
    }
 
    Point getPoint(GeoPoint geoPoint) {
        Point point = new Point();
        main.mapView.getProjection().toPixels(geoPoint, point);
        return point;
    }
 
    void scaleRect(Rect rect, float scaleX, float scaleY) {
        int w = (int) (rect.width() * scaleX / 2);
        int h = (int) (rect.height() * scaleY / 2);
        rect.set(rect.centerX() - w, rect.centerY() - h, rect.centerX() + w,
                rect.centerY() + h);
    }
 
    boolean intersects(PlayerObj playerA, PlayerObj playerB) {
        Rect A = playerA.getRect();
        Rect B = playerB.getRect();
        scaleRect(A, 0.8f, 0.8f);
        scaleRect(B, 0.8f, 0.8f);
        return Rect.intersects(A, B);
    }
 
    public void remove() {
        if (socket.isConnect)
            socket.close();
        if (players.size() > 0) {
            PlayerObj[] playerArray = new PlayerObj[players.size()];
            players.values().toArray(playerArray);
            for (PlayerObj player : playerArray) {
                if (player != this.player)
                    player.remove();
            }
        }
        player.setImage(PlayerObj.ImageSyle.human);
        main.updateSet.remove(this);
        main.mapView.removeView(this);
        main.multiplayerGameCore = null;
    }
 
    public void run() {
        switch (gameStat.nowStatType) {
        case ready:
            if (socket.isConnect) {
                if (System.currentTimeMillis() > sendPacketTime) {
                    PacketModel packetModel = new PacketModel(
                            PacketType.getClientId);
                    packetModel
                            .put("lat", player.geoPoint.getLatitudeE6() + "");
                    packetModel.put("lng", player.geoPoint.getLongitudeE6()
                            + "");
                    socket.sendPacket(packetModel);
                    sendPacketTime = System.currentTimeMillis() + 1000;
                }
                if (clientId != 0)
                    gameStat.nowStatType = StatType.game;
            }
            if (socket.isConnectError && gameStat.isTimeOver())
                remove();
            break;
        case game:
            if (System.currentTimeMillis() > sendPacketTime) {
                if (sendLat != player.geoPoint.getLatitudeE6()
                        && sendLng != player.geoPoint.getLongitudeE6()) {
                    PacketModel pm = new PacketModel(PacketType.updateGps);
                    sendLat = player.geoPoint.getLatitudeE6();
                    sendLng = player.geoPoint.getLongitudeE6();
                    pm.put("id", clientId + "");
                    pm.put("lat", sendLat + "");
                    pm.put("lng", sendLng + "");
                    socket.sendPacket(pm);
                    sendPacketTime = System.currentTimeMillis() + 200;
                }
            }
            break;
        case humanWin:
        case zombieWin:
            if (gameStat.isTimeOver())
                remove();
            break;
        }
    }
 
    @Override
    protected void onDraw(Canvas canvas) {
        Paint paint = new Paint();
 
        switch (gameStat.nowStatType) {
        case ready:
            readyImg.setAlpha(100);
            readyImg.setBounds(main.screenRect);
            readyImg.draw(canvas);
            paint.setARGB(255, 255, 255, 255);
            paint.setTextSize(30);
            paint.setTextAlign(Align.CENTER);
            if (!socket.isConnectError) {
                canvas.drawText("等待連線中", main.screenRect.centerX(),
                        main.screenRect.centerY(), paint);
            }
            break;
        case game:
            paint.setARGB(150, 255, 255, 255);
            paint.setTextSize(12);
 
            if (isGameStart && gameStat.getCountdownTime() != 0) {
                canvas.drawText("剩餘時間:" + gameStat.getCountdownTime(), 20, 80,
                        paint);
                canvas.drawText("人類數量:" + human.size(), 20, 90, paint);
                canvas.drawText("殭屍數量:" + zombie.size(), 20, 100, paint);
            } else {
                canvas.drawText("玩家數量:" + players.size(), 20, 80, paint);
            }
            break;
        case humanWin:
            if (gameStat.getCountdownTimeMillis() < 1000)
                humanWinImg.setAlpha(gameStat.getCountdownTimeMillis() / 5);
            else
                humanWinImg.setAlpha(200);
            humanWinImg.setBounds(main.screenRect);
            humanWinImg.draw(canvas);
            break;
        case zombieWin:
            if (gameStat.getCountdownTimeMillis() < 1000)
                zombieWinImg.setAlpha(gameStat.getCountdownTimeMillis() / 5);
            else
                zombieWinImg.setAlpha(200);
            zombieWinImg.setBounds(main.screenRect);
            zombieWinImg.draw(canvas);
            break;
        }
        if (socket.isConnectError) {
            paint.setARGB(255, 255, 255, 255);
            paint.setTextSize(30);
            paint.setTextAlign(Align.CENTER);
            canvas.drawText("連線中斷", main.screenRect.centerX(), main.screenRect
                    .centerY(), paint);
        }
        super.onDraw(canvas);
    }
}

程式:PlayerObj.java

package ccc.ZombieWars;
 
import java.util.Random;
 
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.Projection;
 
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.View;
 
public class PlayerObj extends Overlay {
    Context context;
    MapView mapView;
    GeoPoint geoPoint;
    GeoPoint drawGeoPoint;
    public boolean isZombie = false;
    ImageSyle imageSyle = ImageSyle.human;
    int srcZoom;
    int width, height;
 
    public PlayerObj(Context context, MapView mapView) {
 
        this.context = context;
 
        this.mapView = mapView;
        mapView.getOverlays().add(this);
        setGeoPoint(new GeoPoint(mapView.getMapCenter().getLatitudeE6(),
                mapView.getMapCenter().getLongitudeE6()));
        srcZoom = mapView.getZoomLevel();
 
    }
 
    public void remove() {
        mapView.getOverlays().remove(this);
    }
 
    enum ImageSyle {
        human, zombie
    }
 
    public void setImage(ImageSyle style) {
        if(style==ImageSyle.zombie){
            isZombie=true;
        }else{
            isZombie=false;
        }
        imageSyle = style;
    }
 
    public void animateTo(GeoPoint objPoint) {
        this.geoPoint = objPoint;
        Projection projection = mapView.getProjection();
        Point dstPoint = new Point();
        Point drawPoint = new Point();
        projection.toPixels(geoPoint, dstPoint);
        projection.toPixels(drawGeoPoint, drawPoint);
        int dx = dstPoint.x - drawPoint.x;
        int dy = dstPoint.y - drawPoint.y;
        if (dx * dx + dy * dy > 500 * 500) {
            this.drawGeoPoint = geoPoint;
        }
 
    }
 
    public void setGeoPoint(GeoPoint objPoint) {
        this.drawGeoPoint = objPoint;
        this.geoPoint = objPoint;
    }
 
    Rect getRect() {
        Point point = new Point();
        mapView.getProjection().toPixels(drawGeoPoint, point);
        return getRect(point.x, point.y);
    }
 
    private Rect getRect(int x, int y) {
        int width2 = width / 2;
        int height2 = height / 2;
        return new Rect(x - width2, y - height2, x + width2, y + height2);
    }
 
    int getWidth(){
        return width;
    }
    int getHeight(){
        return height;
    }
    @Override
    public void draw(Canvas canvas, MapView mapView, boolean shadow) {
 
        Drawable img = null;
        switch (imageSyle) {
        case human:
            img = context.getResources().getDrawable(R.drawable.human);
            break;
        case zombie:
            img = context.getResources().getDrawable(R.drawable.zombie);
            break;
        }
        if (img != null) {
            Projection projection = mapView.getProjection();
            Point drawPoint = new Point();
            projection.toPixels(drawGeoPoint, drawPoint);
 
            Point dstPoint = new Point();
            projection.toPixels(geoPoint, dstPoint);
 
            int dx = dstPoint.x - drawPoint.x;
            int dy = dstPoint.y - drawPoint.y;
            if (dx * dx + dy * dy < 4 * 4) {
                this.drawGeoPoint = geoPoint;
                drawPoint = dstPoint;
            } else {
                Double radian = Math.atan2(dy, dx);
                int fixX = drawPoint.x + (int) (Math.cos(radian) * 3);
                int fixY = drawPoint.y + (int) (Math.sin(radian) * 3);
                drawGeoPoint = projection.fromPixels(fixX, fixY);
            }
 
            int offsetScale = mapView.getZoomLevel() - srcZoom;
 
            width = img.getIntrinsicWidth() ;
            height = img.getIntrinsicHeight() ;
            if (offsetScale < 0) {
                width >>= -offsetScale;
                height >>= -offsetScale;
            } else {
                width <<= offsetScale;
                height <<= offsetScale;
            }
            int width2 = width / 2;
            int height2 = height / 2;
            if(width2<5)
                width2=5;
            if(height2<5)
                height2=5;
            img.setBounds(drawPoint.x - width2, drawPoint.y - height2,
                    drawPoint.x + width2, drawPoint.y + height2);
            img.draw(canvas);
        }
 
        super.draw(canvas, mapView, shadow);
    }
 
}

程式:SingleGameCore.java

package ccc.ZombieWars;
 
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
 
import ccc.ZombieWars.GameStat.StatType;
 
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
 
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Paint.Align;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.View;
 
public class SingleGameCore extends View implements Runnable {
    Random random = new Random();
    GameStat gameStat;
    ZombieWars main;
    PlayerObj player;
    List<PlayerObj> bots = new ArrayList<PlayerObj>();
    List<PlayerObj> human = new ArrayList<PlayerObj>();
    List<PlayerObj> zombie = new ArrayList<PlayerObj>();
    Drawable readyImg;
    Drawable humanWinImg;
    Drawable zombieWinImg;
    int botCount = 10;
 
    public SingleGameCore(ZombieWars context) {
        super(context);
        this.main = context;
        this.player = main.player;
        readyImg = main.getResources().getDrawable(R.drawable.ready);
        humanWinImg = main.getResources().getDrawable(R.drawable.humanwin);
        zombieWinImg = main.getResources().getDrawable(R.drawable.zombiewin);
 
        MapView.LayoutParams layout = new MapView.LayoutParams(
                MapView.LayoutParams.FILL_PARENT,
                MapView.LayoutParams.FILL_PARENT, 0, 0,
                MapView.LayoutParams.TOP_LEFT);
        gameStat = new GameStat(System.currentTimeMillis() + 3000);
        main.mapView.addView(this, layout);
        main.updateSet.add(this);
    }
 
    void killHuman(PlayerObj dieHuman) {        
        dieHuman.setImage(PlayerObj.ImageSyle.zombie);
        human.remove(dieHuman);
        zombie.add(dieHuman);
    }
 
    GeoPoint getGeoPoint(int lat, int lng) {
        return new GeoPoint(lat, lng);
    }
 
    Point getPoint(GeoPoint geoPoint) {
        Point point = new Point();
        main.mapView.getProjection().toPixels(geoPoint, point);
        return point;
    }
 
    void scaleRect(Rect rect, float scaleX, float scaleY) {
        int w = (int) (rect.width() * scaleX / 2);
        int h = (int) (rect.height() * scaleY / 2);
        rect.set(rect.centerX() - w, rect.centerY() - h, rect.centerX() + w,
                rect.centerY() + h);
    }
 
    boolean intersects(PlayerObj playerA, PlayerObj playerB) {
        Rect A = playerA.getRect();
        Rect B = playerB.getRect();
        scaleRect(A, 0.8f, 0.8f);
        scaleRect(B, 0.8f, 0.8f);
        return Rect.intersects(A, B);
    }
 
    public void randomBot() {
        int r = random.nextInt(100);
        if (r < botCount) {
            int plat,plng,blat,blng;
            PlayerObj bot = bots.get(r);
             plat=player.geoPoint.getLatitudeE6();
             plng=player.geoPoint.getLongitudeE6();
             blat=bot.geoPoint.getLatitudeE6();
             blng=bot.geoPoint.getLongitudeE6();
            Rect limRect=new Rect(plng-450,plat-450,plng+450,plat+450);
            if(limRect.contains(blng,blat)){
             blat += random.nextInt(400) - 200;
             blng += random.nextInt(400) - 200;
            }else{
                blat=plat+random.nextInt(900) - 450;
                blng=plng+random.nextInt(900) - 450;
            }
            bot.animateTo(getGeoPoint(blat, blng));
        }
 
    }
 
    void gameInit() {
        for (int i = 0; i < botCount; i++) {
            PlayerObj bot = new PlayerObj(main, main.mapView);
            bot.srcZoom = player.srcZoom;
            int rlat, rlng;
            Rect playerRect = player.getRect();
            do {
                rlat = player.geoPoint.getLatitudeE6() + random.nextInt(900) - 450;
                rlng = player.geoPoint.getLongitudeE6() + random.nextInt(900) - 450;
                GeoPoint newPoint =getGeoPoint(rlat, rlng);
                bot.setGeoPoint(newPoint);
            } while (Rect.intersects(playerRect, bot.getRect()));
 
            bots.add(bot);
            human.add(bot);
        }
        human.add(player);
        killHuman(human.get(random.nextInt(human.size())));
 
    }
 
    public void gameUpdate() {
        randomBot();
 
        if (zombie.size() > 0) {
            PlayerObj[] zombieArray = new PlayerObj[zombie.size()];
            zombie.toArray(zombieArray);
            for (PlayerObj z : zombieArray) {
                if (human.size() > 0) {
                    PlayerObj[] humanArray = new PlayerObj[human.size()];
                    human.toArray(humanArray);
                    for (PlayerObj h : humanArray) {
                        if (intersects(z, h)) {
                            killHuman(h);
                        }
                    }
                }
            }
        }
 
    }
 
    public void remove() {
        if (bots.size() > 0) {
            PlayerObj[] botArray = new PlayerObj[bots.size()];
            bots.toArray(botArray);
            for (PlayerObj bot : botArray) {
                bot.remove();
            }
        }
        player.setImage(PlayerObj.ImageSyle.human);
        main.updateSet.remove(this);
        main.mapView.removeView(this);
        main.singleGameCore=null;
    }
 
    public void run() {
        switch (gameStat.nowStatType) {
        case ready:
 
            if (gameStat.isTimeOver()) {
                gameInit();
                gameStat.nowStatType = StatType.game;
                gameStat.resetTime(System.currentTimeMillis() + 100000);
            }
            break;
        case game:
            gameUpdate();
            if (gameStat.isTimeOver() || human.size() == 0) {        
                if (player.isZombie&&human.size() == 0)
                    gameStat.nowStatType = StatType.zombieWin;
                else
                    gameStat.nowStatType = StatType.humanWin;
                gameStat.resetTime(System.currentTimeMillis() + 3000);
            }
            break;
        case humanWin:
        case zombieWin:
            if (gameStat.isTimeOver())
                remove();
            break;
        }
    }
 
    @Override
    protected void onDraw(Canvas canvas) {
        Paint paint = new Paint();
 
        switch (gameStat.nowStatType) {
        case ready:
            if (gameStat.getCountdownTimeMillis() < 1000)
                readyImg.setAlpha(gameStat.getCountdownTimeMillis() / 10);
            else
                readyImg.setAlpha(100);
            readyImg.setBounds(main.screenRect);
            readyImg.draw(canvas);
            paint.setARGB(255, 255, 255, 255);
            paint.setTextSize(30);
            paint.setTextAlign(Align.CENTER);
            canvas
                    .drawText(gameStat.getCountdownTime() + "秒後開始",
                            main.screenRect.centerX(), main.screenRect.centerY()+50, paint);
 
            break;
        case game:
            paint.setARGB(150, 255, 255, 255);
            paint.setTextSize(12);
 
            canvas.drawText("剩餘時間:" + gameStat.getCountdownTime(), 20, 80,
                    paint);
            canvas.drawText("人類數量:" + human.size(), 20, 90, paint);
            canvas.drawText("殭屍數量:" + zombie.size(), 20, 100, paint);
            break;
        case humanWin:
            if (gameStat.getCountdownTimeMillis() < 1000)
                humanWinImg.setAlpha(gameStat.getCountdownTimeMillis() / 5);
            else
                humanWinImg.setAlpha(200);
            humanWinImg.setBounds(main.screenRect);
            humanWinImg.draw(canvas);
            break;
        case zombieWin:
            if (gameStat.getCountdownTimeMillis() < 1000)
                zombieWinImg.setAlpha(gameStat.getCountdownTimeMillis() / 5);
            else
                zombieWinImg.setAlpha(200);
            zombieWinImg.setBounds(main.screenRect);
            zombieWinImg.draw(canvas);
            break;
        }
 
        super.onDraw(canvas);
    }
}

程式:GameStat.java

package ccc.ZombieWars;
 
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
 
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Paint.Align;
import android.util.Log;
import android.view.View;
 
public class GameStat {
    private Long startTime=0l;
    private Long overTime=0l;
    private Long pauseTime=0l;
    private boolean isTimePause=false;
    StatType nowStatType=StatType.ready;
    enum StatType{ready,game,humanWin,zombieWin};
 
    public GameStat(long overTime){
        resetTime(overTime);
    }
 
    public void resetTime(long overTime){
        this.startTime=System.currentTimeMillis();
        this.overTime=overTime;
    }
    /**
     * 得到距離結束時間秒數
     */
    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 int getCountdownTimeMillis(){
        if(!isTimeOver()){
            if(isTimePause)
                return (int)((this.overTime-pauseTime));
            else
                return (int)((this.overTime-System.currentTimeMillis()));
        }
        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;
    }
}

程式:PacketHandler.java

package ccc.ZombieWars.net;
 
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
 
import ccc.ZombieWars.net.PacketListener;
import ccc.ZombieWars.net.PacketModel;
import ccc.ZombieWars.net.PacketSender;
 
public class PacketHandler implements PacketSender {
    public boolean isConnect = false;
    protected Socket socket;
 
    public PacketHandler() {
 
    }
 
    public void listenSocket(Socket socket) {
        this.socket = socket;
        listenPacket();
 
    }
 
    public void setPacketListener(PacketListener packetListener) {
        this.packetListener = packetListener;
    }
 
    public PacketListener getPacketListener() {
        return this.packetListener;
    }
 
    public void close() {
        isConnect = false;
        try {
            socket.shutdownInput();
        } catch (IOException e) {
        }
        try {
            socket.shutdownOutput();
        } catch (IOException e) {
        }
        try {
            socket.close();
        } catch (IOException e) {
        }
    }
 
    private void listenPacket() {
        Thread listenThread = new Thread(new Runnable() {
            public void run() {
                while (socket.isConnected()) {
                    isConnect = true;
                    String dataConfig;
                    try {
                            BufferedReader br = new BufferedReader(
                                    new InputStreamReader(socket.getInputStream()));
                            dataConfig = br.readLine();
                            packetListener.receivePacket(new PacketModel(
                                    dataConfig));
                            sleep(30);
                    } catch (Exception e) {
                        // System.out.println("Connect Error");
                        break;
                    }
 
                }
                isConnect = false;
                packetListener.closeCallback();
            }
        });
        listenThread.start();
    }
 
    public void sendPacket(PacketModel packetModel) {
        BufferedWriter bw;
        if (isConnect) {
            try {
 
                    bw = new BufferedWriter(
                            new OutputStreamWriter( socket.getOutputStream()));
                    bw.write(packetModel.toString());
                    bw.newLine();
                    bw.flush();
                    sleep(30);
            } catch (IOException e) {
                isConnect = false;
                packetListener.closeCallback();
            }
        }
    }
    private void sleep(long millis){
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    private PacketListener packetListener = new PacketListener() {
        public void receivePacket(PacketModel packetModel) {
 
        }
 
        public void closeCallback() {
 
        }
    };
 
}

程式:PacketListener.java

package ccc.ZombieWars.net;
 
public interface PacketListener {
    public void receivePacket(PacketModel packetModel);
    public void closeCallback();
}

程式:PacketModel.java

package ccc.ZombieWars.net;
 
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class PacketModel {
    public enum PacketType{debug,getClientId,updateGps,startGame,addPlayer,removePlayer,countdownTimeMillis,killHuman,overGame};
    private PacketType packetType=null;
    private Map<String, String> data=new HashMap<String, String>();
    public PacketModel(PacketType packetType){
        setType(packetType);
    }
    public void setType(PacketType packetType){
        this.packetType=packetType;
        data.put("packetType",packetType.toString());
    }
    public PacketType getType(){
        return packetType;
    }
    public Set<String> keySet(){
        return data.keySet();
    }
    public PacketModel(String dataConfig){
        parseString(dataConfig);
    }
    public void put(String key,String value){
        data.put(key, value);
    }
    public String get(String key){
        return data.get(key);
    }
    public void parseString(String dataConfig){
        data.clear();
        //find "XXX=XXX"
        //matcher.group(1)="XXX" matcher.group(2)="XXX"
        Pattern p = Pattern.compile("([\\w]*)[\\s]*=[\\s]*([\\w]*)");
        Matcher matcher = p.matcher(dataConfig);
        while (matcher.find()) {
                //EX:"NAME=XXX"
                //matcher.group(1)="NAME" matcher.group(2)="XXX"
                data.put(matcher.group(1), matcher.group(2));            
        }
        packetType=PacketType.valueOf(data.get("packetType"));
    }
    @Override 
    public String toString() {
        StringBuilder stringBuilder=new StringBuilder();
        data.put("packetType",packetType.toString());    
        for (String key : data.keySet()) {
            stringBuilder.append(key);
            stringBuilder.append("=");
            stringBuilder.append(data.get(key));
            stringBuilder.append(" ");
        }
        return stringBuilder.toString();
    }
 
}

程式:PacketSender.java

package ccc.ZombieWars.net;
 
public interface PacketSender {
    public void sendPacket(PacketModel packetModel);
}

程式:ClientSocketHandler.java

package ccc.ZombieWars.net.client;
 
import java.io.IOException;
import java.net.Socket;
 
import ccc.ZombieWars.net.PacketHandler;
import ccc.ZombieWars.net.PacketModel;
import ccc.ZombieWars.net.PacketModel.PacketType;
 
public class ClientSocketHandler extends PacketHandler {
    public boolean isConnectError=false;
    public ClientSocketHandler() {
 
    }
    public void Connect(String host, int port) {
        try {
            socket = new Socket(host, port);
            socket.getInputStream();
            socket.getOutputStream();
            listenSocket(socket);
        } catch (IOException e) {
            isConnectError=true;
        }
    }        
}

AndroidManifest.xml為使用權限:

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

以下為殭屍大戰的客戶測試程式碼:
主程式:ZombieWarsClientTest.java

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.rmi.activation.ActivationGroupDesc.CommandEnvironment;
 
import ccc.ZombieWars.net.PacketListener;
import ccc.ZombieWars.net.PacketModel;
import ccc.ZombieWars.net.PacketModel.PacketType;
import ccc.ZombieWars.net.client.ClientSocketHandler;
 
public class ZombieWarsClientTest {
    static int clientId=0;
    static int latE6=0;
    static int lngE6=0;
    static String serverHost="127.0.0.1";
    //static String serverHost="192.168.60.8";
    static int serverPort=443;
    static ClientSocketHandler client=new ClientSocketHandler();
    public static void main(String[] args){
        client.setPacketListener(new PacketListener() {
            @Override
            public void receivePacket(PacketModel packetModel) {
 
                switch(packetModel.getType()){
                    case getClientId:
                        clientId=Integer.parseInt(packetModel.get("id"));
                        break;
                    default:
                        printfPacket(packetModel);
                        break;
                }
            }
 
            @Override
            public void closeCallback() {
 
            }
        });
        client.Connect(serverHost, serverPort);
        if(!client.isConnectError){
        keyLatLng();
        PacketModel packetModel=new PacketModel(PacketType.getClientId);
        packetModel.put("lat", latE6+"");
        packetModel.put("lng", lngE6+"");
        client.sendPacket(packetModel);
 
        while(clientId==0);
        while(client.isConnect){
            listenCommand();    
        }
        }else{
            System.out.println("連線失敗");
        }
    }
    static void printfPacket(PacketModel packetModel){
        for (String key : packetModel.keySet()) {
            System.out.println("s:"+key+"="+packetModel.get(key));
        }
    }
    enum CmdType {debug,exit,updateGps,startGame}
    static void command(CmdType cmdType){
        PacketModel pm;
        switch (cmdType) {
        case debug:
            printf("debugValue:");
            pm=new PacketModel(PacketModel.PacketType.debug);
            pm.put("value", readLine());
            client.sendPacket(pm);
            break;
        case exit:
            client.close();
            break;
        case startGame:
            pm=new PacketModel(PacketModel.PacketType.startGame);
            client.sendPacket(pm);
            break;
        case updateGps:
            keyLatLng();
            pm=new PacketModel(PacketModel.PacketType.updateGps);
            pm.put("id", clientId+"");
            pm.put("lat", latE6+"");
            pm.put("lng", lngE6+"");
            client.sendPacket(pm);
            break;
        default:
            printfln("No Cmd");
            break;
        }
 
    }
    static void keyLatLng(){
        printf("lat:");
        String latstr=readLine();
        printf("lng:");
        String lngstr=readLine();
        try {
            latE6=(int) (Double.parseDouble(latstr)*1e6);    
        } catch (Exception e) {
 
        }
        try {
            lngE6=(int) (Double.parseDouble(lngstr)*1e6);    
        } catch (Exception e) {
 
        }
    }
    static void command(String cmd){
        CmdType cmdType = null;
        try {
            cmdType = CmdType.valueOf(cmd);
        } catch (Exception e) {
            if(!cmd.equals(""))
                printfln("\""+cmd+"\" is error command");
            return;
        }
        command(cmdType);
    }
 
    static void printf(String str) {
        System.out.print(str);
    }
    static void printfln(String str) {
        System.out.println(str);
    }
 
    static void listenCommand(){
        System.out.print("Client"+clientId+"#");
        command(readLine());            
    }
    static String readLine(){
        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
        try {
            return br.readLine();
        } catch (IOException e) {
        }
        return null;
    }
}

程式:PacketHandler.java

package ccc.ZombieWars.net;
 
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
 
import ccc.ZombieWars.net.PacketListener;
import ccc.ZombieWars.net.PacketModel;
import ccc.ZombieWars.net.PacketSender;
 
public class PacketHandler implements PacketSender {
    public boolean isConnect = false;
    protected Socket socket;
 
    public PacketHandler() {
 
    }
 
    public void listenSocket(Socket socket) {
        this.socket = socket;
        listenPacket();
 
    }
 
    public void setPacketListener(PacketListener packetListener) {
        this.packetListener = packetListener;
    }
 
    public PacketListener getPacketListener() {
        return this.packetListener;
    }
 
    public void close() {
        isConnect = false;
        try {
            socket.shutdownInput();
        } catch (IOException e) {
        }
        try {
            socket.shutdownOutput();
        } catch (IOException e) {
        }
        try {
            socket.close();
        } catch (IOException e) {
        }
    }
 
    private void listenPacket() {
        Thread listenThread = new Thread(new Runnable() {
            public void run() {
                while (socket.isConnected()) {
                    isConnect = true;
                    String dataConfig;
                    try {
                            BufferedReader br = new BufferedReader(
                                    new InputStreamReader(socket.getInputStream()));
                            dataConfig = br.readLine();
                            packetListener.receivePacket(new PacketModel(
                                    dataConfig));
                            sleep(30);
                    } catch (Exception e) {
                        // System.out.println("Connect Error");
                        break;
                    }
 
                }
                isConnect = false;
                packetListener.closeCallback();
            }
        });
        listenThread.start();
    }
 
    public void sendPacket(PacketModel packetModel) {
        BufferedWriter bw;
        if (isConnect) {
            try {
 
                    bw = new BufferedWriter(
                            new OutputStreamWriter( socket.getOutputStream()));
                    bw.write(packetModel.toString());
                    bw.newLine();
                    bw.flush();
                    sleep(30);
            } catch (IOException e) {
                isConnect = false;
                packetListener.closeCallback();
            }
        }
    }
    private void sleep(long millis){
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    private PacketListener packetListener = new PacketListener() {
        public void receivePacket(PacketModel packetModel) {
 
        }
 
        public void closeCallback() {
 
        }
    };
 
}

程式:PacketListener.java

package ccc.ZombieWars.net;
 
public interface PacketListener {
    public void receivePacket(PacketModel packetModel);
    public void closeCallback();
}

程式:PacketModel.java

package ccc.ZombieWars.net;
 
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class PacketModel {
    public enum PacketType{debug,getClientId,updateGps,startGame,addPlayer,removePlayer,countdownTimeMillis,killHuman,overGame};
    private PacketType packetType=null;
    private Map<String, String> data=new HashMap<String, String>();
    public PacketModel(PacketType packetType){
        setType(packetType);
    }
    public void setType(PacketType packetType){
        this.packetType=packetType;
        data.put("packetType",packetType.toString());
    }
    public PacketType getType(){
        return packetType;
    }
    public Set<String> keySet(){
        return data.keySet();
    }
    public PacketModel(String dataConfig){
        parseString(dataConfig);
    }
    public void put(String key,String value){
        data.put(key, value);
    }
    public String get(String key){
        return data.get(key);
    }
    public void parseString(String dataConfig){
        data.clear();
        //find "XXX=XXX"
        //matcher.group(1)="XXX" matcher.group(2)="XXX"
        Pattern p = Pattern.compile("([\\w]*)[\\s]*=[\\s]*([\\w]*)");
        Matcher matcher = p.matcher(dataConfig);
        while (matcher.find()) {
                //EX:"NAME=XXX"
                //matcher.group(1)="NAME" matcher.group(2)="XXX"
                data.put(matcher.group(1), matcher.group(2));            
        }
        packetType=PacketType.valueOf(data.get("packetType"));
    }
    @Override 
    public String toString() {
        StringBuilder stringBuilder=new StringBuilder();
        data.put("packetType",packetType.toString());    
        for (String key : data.keySet()) {
            stringBuilder.append(key);
            stringBuilder.append("=");
            stringBuilder.append(data.get(key));
            stringBuilder.append(" ");
        }
        return stringBuilder.toString();
    }
 
}

程式:PacketSender.java

package ccc.ZombieWars.net;
 
public interface PacketSender {
    public void sendPacket(PacketModel packetModel);
}

程式:ClientSocketHandler.java

package ccc.ZombieWars.net.client;
 
import java.io.IOException;
import java.net.Socket;
 
import ccc.ZombieWars.net.PacketHandler;
import ccc.ZombieWars.net.PacketModel;
import ccc.ZombieWars.net.PacketModel.PacketType;
 
public class ClientSocketHandler extends PacketHandler {
    public boolean isConnectError=false;
    public ClientSocketHandler() {
 
    }
    public void Connect(String host, int port) {
        try {
            socket = new Socket(host, port);
            socket.getInputStream();
            socket.getOutputStream();
            listenSocket(socket);
        } catch (IOException e) {
            isConnectError=true;
        }
    }    
}

以下為殭屍大戰的伺服器程式碼:
主程式:ZombieWarsServer

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;
import java.util.ArrayList;
 
import ccc.ZombieWars.net.PacketModel;
import ccc.ZombieWars.net.PacketModel.PacketType;
 
public class ZombieWarsServer {
 
    private static int serverport = 443;
    private static ServerSocket serverSocket;
 
    public static GameCore gameRoom;
    public static int flowId = 0;
    final static int roomPlayerCountMax=10;
    public static void main(String[] args) {
 
        try {
            serverSocket = new ServerSocket(serverport);
            System.out.println("Server is start.");
            startListenCommand();
            gameRoom = new GameCore();
            while (!serverSocket.isClosed()) {
                Socket newConnect = waitNewPlayer();
                if (newConnect != null) {
                    if (gameRoom.gameThread!=null||gameRoom.players.size() >= roomPlayerCountMax) {
                        gameRoom = new GameCore();
                    }
                    gameRoom.addPlayer(++flowId, newConnect);
                }
            }
 
        } catch (IOException e) {
            System.out.println("Server Socket ERROR");
        }
 
    }
 
    public static Socket waitNewPlayer() {
        Socket socket = null;
        System.out.println("Wait new clinet connect");
        try {
            socket = serverSocket.accept();
 
        } catch (IOException e) {
            System.out.println("Socket Close");
        }
        return socket;
 
    }
 
    enum CmdType {
        size, exit, debug
    }
 
    static void command(CmdType cmdType) {
        PacketModel pm;
        switch (cmdType) {
        case debug:
            printf("debugValue:");
            pm = new PacketModel(PacketModel.PacketType.debug);
            pm.put("value", readLine());
            gameRoom.castPacket(pm);
            break;
        case exit:
            try {
                serverSocket.close();
            } catch (IOException e) {
            }
            gameRoom.closeRoom();
            break;
        case size:
            printfln("RoomPresonCount:" + gameRoom.players.size());
            break;
        default:
            printfln("No Cmd");
            break;
        }
    }
 
    static void command(String cmd) {
        CmdType cmdType = null;
        try {
            cmdType = CmdType.valueOf(cmd);
        } catch (Exception e) {
            if(!cmd.equals(""))
                printfln("\""+cmd+"\" is error command");
            return;
        }
        command(cmdType);
    }
 
    static void printf(String str) {
        System.out.print(str);
    }
 
    static void printfln(String str) {
        System.out.println(str);
    }
 
    static void startListenCommand() {
        Thread cmdThread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (!serverSocket.isClosed()) {
                    listenCommand();
                }
            }
        });
        cmdThread.start();
    }
 
    static void listenCommand() {
        System.out.print("Server#");
        command(readLine());
    }
 
    static String readLine() {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        try {
            return br.readLine();
        } catch (IOException e) {
        }
        return null;
    }
 
}

程式:GameCore.java

import java.awt.Rectangle;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
 
import ccc.ZombieWars.net.PacketHandler;
import ccc.ZombieWars.net.PacketListener;
import ccc.ZombieWars.net.PacketModel;
import ccc.ZombieWars.net.PacketModel.PacketType;
import ccc.ZombieWars.server.ServerSocketHandler;
 
public class GameCore {
 
    public Map<PacketListener, Integer> playerIds = new HashMap<PacketListener, Integer>();
    public Map<Integer, ServerSocketHandler> players = new HashMap<Integer, ServerSocketHandler>();
    public List<ServerSocketHandler> zombie = new ArrayList<ServerSocketHandler>();
    public List<ServerSocketHandler> human = new ArrayList<ServerSocketHandler>();
    public GameStat gameStat;
    public Thread gameThread;
    Random random = new Random();
 
    public GameCore() {
 
    }
 
    public void gameStart() {
        ServerSocketHandler[] tmp = new ServerSocketHandler[players.size()];
        players.values().toArray(tmp);
        for (ServerSocketHandler serverSocketHandler : tmp) {
            if (!serverSocketHandler.isGetId)
                return;
        }
        gameThread = new Thread(gameUpdate);
        gameThread.start();
 
    }
 
    public Runnable gameUpdate = new Runnable() {
        long sendPacketTime=0;
        @Override
        public void run() {
            PacketModel pm;
            pm = new PacketModel(PacketType.startGame);
            castPacket(pm);
            sleep(100);
            gameStat = new GameStat(System.currentTimeMillis() + 100000);
            int r = random.nextInt(human.size());
            killHuman(human.get(r));
            sleep(50);
            while (!gameStat.isTimeOver()
                    && !(human.size() == 0 || zombie.size() == 0)) {
 
                if(System.currentTimeMillis()>sendPacketTime){
                pm = new PacketModel(PacketType.countdownTimeMillis);
                pm.put("value", gameStat.getCountdownTimeMillis() + "");
                castPacket(pm);
                sendPacketTime=System.currentTimeMillis()+3000;
                }
                sleep(50);
                checkKillHuman();
            }
            sleep(100);
            if (human.size() == 0) {
                pm = new PacketModel(PacketType.overGame);
                pm.put("win", "zombie");
            } else {
                pm = new PacketModel(PacketType.overGame);
                pm.put("win", "human");
            } 
            castPacket(pm);
            sleep(5000);
            closeRoom();
        }
    };
 
    void checkKillHuman() {
        if (zombie.size() > 0) {
            ServerSocketHandler[] zombieArray = new ServerSocketHandler[zombie
                    .size()];
            zombie.toArray(zombieArray);
            for (ServerSocketHandler z : zombieArray) {
                if (human.size() > 0) {
                    ServerSocketHandler[] humanArray = new ServerSocketHandler[human
                            .size()];
                    human.toArray(humanArray);
                    for (ServerSocketHandler h : humanArray) {
                        Rectangle zRect = getRect(z.gpoint.X, z.gpoint.Y);
                        Rectangle hRect = getRect(h.gpoint.X, h.gpoint.Y);
                        if (zRect.intersects(hRect)) {
                            killHuman(h);
                        }
                    }
                }
            }
        }
    }
 
    Rectangle getRect(int x, int y) {
        int width = 30;
        int height = 30;
        return new Rectangle(x, y, width, height);
    }
 
    void sleep(long milis) {
        try {
            Thread.sleep(milis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
 
    void killHuman(int dieHumanId) {
        killHuman(getPlayer(dieHumanId));
    }
 
    void killHuman(ServerSocketHandler dieHuman) {
        dieHuman.isZombie = true;
        human.remove(dieHuman);
        zombie.add(dieHuman);
        PacketModel pm = new PacketModel(PacketType.killHuman);
        pm.put("id", dieHuman.clientId + "");
        castPacket(pm);
    }
 
    PacketListener getNewPacketListener() {
        return new PacketListener() {
            @Override
            public void receivePacket(PacketModel packetModel) {
                ServerSocketHandler player = getPlayer(this);
                switch (packetModel.getType()) {
                case getClientId:
                    player.updateGps(packetModel.get("lat"), packetModel
                            .get("lng"));
                    player.sendClientId();
                    PacketModel pm = new PacketModel(PacketType.addPlayer);
                    pm.put("id", player.clientId + "");
                    pm.put("lat", player.getlatE6() + "");
                    pm.put("lng", player.getlngE6() + "");
                    castPacket(player, pm);
                    sleep(100);
                    ServerSocketHandler[] allPlayer = new ServerSocketHandler[players
                            .size()];
                    players.values().toArray(allPlayer);
                    for (ServerSocketHandler otherPlayer : allPlayer) {
                        if (otherPlayer != player && otherPlayer.isGetId) {
                            sleep(100);
                            pm = new PacketModel(PacketType.addPlayer);
                            pm.put("id", otherPlayer.clientId + "");
                            pm.put("lat", otherPlayer.getlatE6() + "");
                            pm.put("lng", otherPlayer.getlngE6() + "");
                            player.sendPacket(pm);
 
                        }
                    }
 
                    player.isGetId = true;
                    break;
                case startGame:
                    if (players.size() > 1) {
                        gameStart();
                    }
                    break;
                case updateGps:
                    castPacket(player, packetModel);
                    sleep(100);
                    player.updateGps(packetModel.get("lat"), packetModel
                            .get("lng"));
                    break;
 
                default:
                    printfPacket(player.clientId, packetModel);
                    break;
                }
            }
 
            @Override
            public void closeCallback() {
                removePlayer(this);
            }
        };
    }
 
    void addPlayer(int clientId, Socket socket) {
        ServerSocketHandler player = new ServerSocketHandler(clientId);
        PacketListener packetListener = getNewPacketListener();
        player.setPacketListener(packetListener);
        players.put(clientId, player);
        playerIds.put(packetListener, clientId);
        human.add(player);
        player.listenSocket(socket);
    }
 
    void castPacket(PacketModel packetModel) {
        ServerSocketHandler[] tmp = new ServerSocketHandler[players.size()];
        players.values().toArray(tmp);
        for (ServerSocketHandler serverSocketHandler : tmp) {
            if (serverSocketHandler.isGetId)
                serverSocketHandler.sendPacket(packetModel);
        }
    }
 
    void castPacket(ServerSocketHandler sendPlayer, PacketModel packetModel) {
        ServerSocketHandler[] tmp = new ServerSocketHandler[players.size()];
        players.values().toArray(tmp);
        for (ServerSocketHandler serverSocketHandler : tmp) {
            if (serverSocketHandler != sendPlayer
                    && serverSocketHandler.isGetId)
                serverSocketHandler.sendPacket(packetModel);
        }
    }
 
    void closeRoom() {
        ServerSocketHandler[] tmp = new ServerSocketHandler[players.size()];
        players.values().toArray(tmp);
        for (ServerSocketHandler serverSocketHandler : tmp) {
            serverSocketHandler.close();
        }
    }
 
    ServerSocketHandler getPlayer(PacketListener packetListener) {
        return players.get(playerIds.get(packetListener));
    }
 
    ServerSocketHandler getPlayer(int playerId) {
        return players.get(playerId);
    }
 
    void removePlayer(PacketListener packetListener) {
        int clientId = playerIds.get(packetListener);
        removePlayer(clientId);
    }
 
    void removePlayer(int clientId) {
        ServerSocketHandler player = players.get(clientId);
        PacketModel pm = new PacketModel(PacketType.removePlayer);
        pm.put("id", clientId + "");
        castPacket(player, pm);
        if (player != null) {
            playerIds.remove(player.getPacketListener());
            players.remove(player.clientId);
            zombie.remove(player);
            human.remove(player);
            player.close();
        }
    }
 
    void printfPacket(int ClientId, PacketModel packetModel) {
        for (String key : packetModel.keySet()) {
            System.out.println("c" + ClientId + ":" + key + "="
                    + packetModel.get(key));
        }
    }
 
}

程式:GameStat.java

public class GameStat {
    private Long startTime=0l;
    private Long overTime=0l;
    private Long pauseTime=0l;
    private boolean isTimePause=false;
    StatType nowStatType=StatType.ready;
    enum StatType{ready,game,win,lose};
 
    public GameStat(long overTime){
        resetTime(overTime);
    }
 
    public void resetTime(long overTime){
        this.startTime=System.currentTimeMillis();
        this.overTime=overTime;
    }
    /**
     * 得到距離結束時間秒數
     */
    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 int getCountdownTimeMillis(){
        if(!isTimeOver()){
            if(isTimePause)
                return (int)((this.overTime-pauseTime));
            else
                return (int)((this.overTime-System.currentTimeMillis()));
        }
        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;
    }
 
}

程式:PacketHandler.java

package ccc.ZombieWars.net;
 
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
 
import ccc.ZombieWars.net.PacketListener;
import ccc.ZombieWars.net.PacketModel;
import ccc.ZombieWars.net.PacketSender;
 
public class PacketHandler implements PacketSender {
    public boolean isConnect = false;
    protected Socket socket;
 
    public PacketHandler() {
 
    }
 
    public void listenSocket(Socket socket) {
        this.socket = socket;
        listenPacket();
 
    }
 
    public void setPacketListener(PacketListener packetListener) {
        this.packetListener = packetListener;
    }
 
    public PacketListener getPacketListener() {
        return this.packetListener;
    }
 
    public void close() {
        isConnect = false;
        try {
            socket.shutdownInput();
        } catch (IOException e) {
        }
        try {
            socket.shutdownOutput();
        } catch (IOException e) {
        }
        try {
            socket.close();
        } catch (IOException e) {
        }
    }
 
    private void listenPacket() {
        Thread listenThread = new Thread(new Runnable() {
            public void run() {
                while (socket.isConnected()) {
                    isConnect = true;
                    String dataConfig;
                    try {
                            BufferedReader br = new BufferedReader(
                                    new InputStreamReader(socket.getInputStream()));
                            dataConfig = br.readLine();
                            packetListener.receivePacket(new PacketModel(
                                    dataConfig));
                            sleep(30);
                    } catch (Exception e) {
                        // System.out.println("Connect Error");
                        break;
                    }
 
                }
                isConnect = false;
                packetListener.closeCallback();
            }
        });
        listenThread.start();
    }
 
    public void sendPacket(PacketModel packetModel) {
        BufferedWriter bw;
        if (isConnect) {
            try {
 
                    bw = new BufferedWriter(
                            new OutputStreamWriter( socket.getOutputStream()));
                    bw.write(packetModel.toString());
                    bw.newLine();
                    bw.flush();
                    sleep(30);
            } catch (IOException e) {
                isConnect = false;
                packetListener.closeCallback();
            }
        }
    }
    private void sleep(long millis){
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    private PacketListener packetListener = new PacketListener() {
        public void receivePacket(PacketModel packetModel) {
 
        }
 
        public void closeCallback() {
 
        }
    };
 
}

程式:PacketListener.java

package ccc.ZombieWars.net;
 
public interface PacketListener {
    public void receivePacket(PacketModel packetModel);
    public void closeCallback();
}

程式:PacketModel.java

package ccc.ZombieWars.net;
 
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class PacketModel {
    public enum PacketType{debug,getClientId,updateGps,startGame,addPlayer,removePlayer,countdownTimeMillis,killHuman,overGame};
    private PacketType packetType=null;
    private Map<String, String> data=new HashMap<String, String>();
    public PacketModel(PacketType packetType){
        setType(packetType);
    }
    public void setType(PacketType packetType){
        this.packetType=packetType;
        data.put("packetType",packetType.toString());
    }
    public PacketType getType(){
        return packetType;
    }
    public Set<String> keySet(){
        return data.keySet();
    }
    public PacketModel(String dataConfig){
        parseString(dataConfig);
    }
    public void put(String key,String value){
        data.put(key, value);
    }
    public String get(String key){
        return data.get(key);
    }
    public void parseString(String dataConfig){
        data.clear();
        //find "XXX=XXX"
        //matcher.group(1)="XXX" matcher.group(2)="XXX"
        Pattern p = Pattern.compile("([\\w]*)[\\s]*=[\\s]*([\\w]*)");
        Matcher matcher = p.matcher(dataConfig);
        while (matcher.find()) {
                //EX:"NAME=XXX"
                //matcher.group(1)="NAME" matcher.group(2)="XXX"
                data.put(matcher.group(1), matcher.group(2));            
        }
        packetType=PacketType.valueOf(data.get("packetType"));
    }
    @Override 
    public String toString() {
        StringBuilder stringBuilder=new StringBuilder();
        data.put("packetType",packetType.toString());    
        for (String key : data.keySet()) {
            stringBuilder.append(key);
            stringBuilder.append("=");
            stringBuilder.append(data.get(key));
            stringBuilder.append(" ");
        }
        return stringBuilder.toString();
    }
 
}

程式:PacketSender.java

package ccc.ZombieWars.net;
 
public interface PacketSender {
    public void sendPacket(PacketModel packetModel);
}

程式:ServerSocketHandler.java

package ccc.ZombieWars.server;
 
import gmap.GLatLng;
import gmap.GPoint;
import ccc.ZombieWars.net.PacketHandler;
import ccc.ZombieWars.net.PacketListener;
import ccc.ZombieWars.net.PacketModel;
import ccc.ZombieWars.net.PacketModel.PacketType;
 
public class ServerSocketHandler extends PacketHandler {
    public int clientId = 0;
    private int latE6 = 0;
    private int lngE6 = 0;
    public gmap.GPoint gpoint = new GPoint(0, 0, 19);
    public boolean isGetId = false;
    public boolean isZombie = false;
 
    public ServerSocketHandler(int clientId) {
        this.clientId = clientId;
    }
 
    public void sendClientId() {
        // System.out.print("sendClientId"+clientId);
        PacketModel packetModel = new PacketModel(PacketType.getClientId);
        packetModel.put("id", String.valueOf(clientId));
        super.sendPacket(packetModel);
    }
 
    public int getlatE6() {
        return latE6;
    }
 
    public int getlngE6() {
        return lngE6;
    }
 
    public void updateGps(String strLatE6, String strLngE6) {
        try {
            this.latE6 = Integer.parseInt(strLatE6);
        } catch (Exception e) {
 
        }
        try {
            this.lngE6 = Integer.parseInt(strLngE6);
        } catch (Exception e) {
 
        }
        double lat = latE6 / 1e6;
        double lng = lngE6 / 1e6;
        gpoint = new GPoint(lat, lng,19);
    }
}

程式:GLatLng.java

package gmap;
 
public class GLatLng {
    public double lat;
    public double lng;
    public GLatLng(double lat,double lng){
        this.lat=lat;
        this.lng=lng;
    }
    public GLatLng(int x,int y,int zoom){
        this.lng= MercatorProjection.XToLng(x, zoom);
        this.lat= MercatorProjection.YToLat(y, zoom);
    }
    public GLatLng(GPoint point){
        this.lng= MercatorProjection.XToLng(point.X,point.Zoom);
        this.lat= MercatorProjection.YToLat(point.Y,point.Zoom);
    }
    public GLatLng(GLatLng latlng){
        this.lng= latlng.lng;
        this.lat= latlng.lat;
    }
    public GPoint getGPoint(int zoom){
        return new GPoint(lat, lng, zoom);
    }
 
}

程式:GPoint.java

package gmap;
 
public class GPoint {
    public int X;
    public int Y;
    public int Zoom;
 
        public GPoint(double lat,double lng,int zoom){
            this.X=MercatorProjection.LngToX(lng, zoom);
            this.Y=MercatorProjection.LatToY(lat, zoom);
            this.Zoom=zoom;
        }
 
        public GPoint(int x,int y,int zoom){
            this.X = x;
            this.Y = y;
            this.Zoom = zoom;
        }
 
        public GPoint(GLatLng latlng, int zoom) {
            this.X=MercatorProjection.LngToX(latlng.lng, zoom);
            this.Y=MercatorProjection.LatToY(latlng.lat, zoom);
            this.Zoom=zoom;
        }
 
        public GPoint(GPoint point){
            this.X = point.X;
            this.Y = point.Y;
            this.Zoom = point.Zoom;
        }
 
        // 位移
        public void offset(int dx,int dy){
            this.X += dx;
            this.Y += dy;
        }
 
        // 取得經緯度
        public GLatLng getGLatLng(){
            return new GLatLng(X, Y, Zoom);
        }
 
        // 轉字串
        public String toString(){
            return String.format("GX=%1$s GY=%2$s GZ=%3$s", X, Y, Zoom);
        }
 
        // 設定縮放
        public void setZoom(int zoom) {
            GLatLng latlng = new GLatLng(this);
            this.X = MercatorProjection.LngToX(latlng.lng, zoom);
            this.Y = MercatorProjection.LatToY(latlng.lat, zoom);
            this.Zoom = zoom;
        }
}

程式:MercatorProjection.java

package gmap;
 
public class MercatorProjection {
       // 經度轉成點(point)
    public static int LngToX(double Longitude,int zoom)
    {
        int numberOfTiles = 1 << zoom << 8;
        return (int)((Longitude + 180.0) * numberOfTiles / 360.0);
    }
 
    // 緯度轉成點(point)
    public static int LatToY(double Latitude,int zoom)
    {
        int numberOfTiles = 1 << zoom << 8;
        double projection = Math.log(Math.tan(Math.PI / 4 + ((Latitude / 180 * Math.PI) / 2)));
        double y = (projection / Math.PI);
        y = 1.0 - y;
        y = y / 2.0 * numberOfTiles;
 
        return (int)y;
    }
 
    // 點(X)轉成經度
    public static double XToLng(int pointX,int zoom)
    {
        int numberOfTiles = 1 << zoom << 8;
        return (pointX * (360.0 / numberOfTiles)) - 180.0;
    }
 
    // 點(Y)轉成緯度
    public static double YToLat(int pointY,int zoom)
    {
        int numberOfTiles = 1 << zoom << 8;
        double Latitude = pointY * (2.0 / numberOfTiles);
        Latitude = 1 - Latitude;
        Latitude = Latitude * Math.PI;
        Latitude = Math.atan(Math.sinh(Latitude)) * 180.0 / Math.PI;
        return Latitude;
    }
}

程式手機操作影片

程式手機測試影片

程式影片解說