摘要
上一回我們爲大家介紹了更多的 XNA Framework 支持遊戲開發的類別,包括支持輸入控制,音效播放,以及背景音樂播放控制的類別等等,讓讀者能夠爲所製作的遊戲程序加入更豐富的遊戲效果。這一回我們將要爲大家介紹進階的輸入控制技巧,讓使用者可以利用 Windows Phone 7 智能型手機支持多點觸控的觸控屏幕控制遊戲程序的執行。
認識手勢操作
在上一回的介紹中,我們學會如何利用 TouchPanel 類別的 GetState 方法查詢智能型手機的觸摸屏的狀態,並依據使用者觸碰在觸控屏幕的位置移動遊戲程序顯示的圖形的位置。
呼叫 TouchPanel 類別的 GetState 方法查詢觸控屏幕的狀態並判斷用戶觸碰觸控屏幕的位置只是最簡單的觸控屏幕控制技巧,除了支持取得觸控屏幕的狀態以外,觸控屏幕還支持用戶進行多種不同的控制,包括觸碰、觸碰不放、水平拖曳、垂直拖曳、自由拖曳、以及輕拂等操作。表1 所示即爲觸控屏幕的各種狀態的說明:
操作動作 | 說明 |
Tap | 觸碰。觸碰觸控屏幕後放開,沒有移動的動作。 |
DoubleTap | 連續觸碰。連續觸碰同一個位置兩次。 |
Hold | 點住不放。觸碰後不放達一段時間。 |
VerticalDrag | 垂直拖曳。觸碰屏幕後上下移動。 |
HorizontalDrag | 水平拖曳。觸碰屏幕後左右移動。 |
FreeDrag | 自由拖曳。觸碰屏幕後往任意方向移動 |
DragComplete | 拖曳結束。 |
Flick | 輕拂。觸碰屏幕後往任意方向拂動後離開屏幕。 |
Pinch | 同時便用兩個手指頭觸碰觸控屏幕後移動。 |
PinchComplete | Pinch 操作結束。 |
以 XNA 爲基礎的遊戲程序可以利用表1所列的各種觸控屏幕狀態判斷用戶執行的觸控操作種類,以反應使用者的觸控操作。
[注意]
以 XNA 爲基礎的遊戲程序必須啓用觸控功能才能夠讓遊戲的使用者進行觸控操作,如果已啓用 Pinch 操作功能,則當用戶利用兩個手指頭同時觸碰觸控屏幕並進行移動時,就會產生 Pinch 操作,而不是兩個不同的拖曳操作,如果未啓用 Pinch 操作功能,則所產生的就不是 Pinch 操作,而是依據兩個觸碰位置的平均爲準的單一拖曳操作。
啓用手勢操作支持
以 XNA 爲基礎的遊戲程序必須設定 TouchPanel 類別的 EnabledGestures 屬性,才能夠啓用手勢操作功能,以支持用戶以手勢操作遊戲程序。
程序設計師可以在 Game1 類別的 Initialize 方法執行設定 TouchPanel 類別的 EnabledGestures 屬性的動作,以啓用手勢操作支持,做法如下:
TouchPanel.EnabledGestures =
GestureType.Hold |
GestureType.Tap |
GestureType.DoubleTap |
GestureType.FreeDrag |
GestureType.Flick |
GestureType.Pinch;
[說明]
請注意在上述的程序中,GestureType.FreeDrag 設定表示要支持使用者以自由拖曳的方式操作遊戲程序,設定了 GestureType.FreeDrag 就已經涵蓋 GestureType.VerticalDrag 設定和 GestureType.HorizontalDrag 設定。而 DragComplete 狀態和 PinchComplete 狀態代表觸控動作結束的狀態,不需要啓用。
[注意]
以 XNA 爲基礎的遊戲程序可以視需要啓用需要使用的觸控功能,例如只支持用戶利用觸控的方式選取菜單的遊戲程序,就可以僅啓用 Tap 和 VerticalDrag 兩種觸控功能,讓用戶以垂直拖曳的方式捲動遊戲程序提供的菜單,再觸碰欲選擇的菜單,其他不需要用到的觸控控制功能就不需要啓用,避免啓用多種觸控操作功能,造成判斷觸控操作動作的邏輯複雜,進而影響到觸控的精確度和遊戲程序執行的效能。
處理使用者的手勢操作
啓用了手勢操作功能之後,以 XNA 爲基礎的應用程序可以在 Game1 類別的 Update 方法中呼叫 TouchPanel 類別的 ReadGesture 方法取得用戶的手勢操作信息。請注意讀取使用者的手勢操作的做法和呼叫 TouchPanel 類別的 GetState 方法讀取觸控面板的狀態的做法不同,因爲用戶對遊戲程序的觸控操作會產生多個手勢信息,來不及被遊戲處理的手勢信息會被存放到隊列中等待處理,讓遊戲程序利用循環取出並加以處理。
以下的 Update 方法便會利用 while 循環,搭配 TouchPanel 類別的 IsGestureAvailable 屬性判斷是否還有用戶觸控操作產生的手勢信息尚未被處理,如果尚有使用者觸控操作產生的手勢信息尚未被處理,則呼叫 TouchPanel 類別的 ReadGesture 方法讀取手勢信息,並加以處理:
protected override void Update(GameTime gameTime)
{
…
while (TouchPanel.IsGestureAvailable) //判斷是否尚有手勢資訊尚未被處理
{
GestureSample gesture = TouchPanel.ReadGesture(); //讀取尚未處理的手勢資訊
switch (gesture.GestureType) //判斷手勢操作的種類
{
case GestureType.Tap: //如果手勢操作的種類是Tap
//處理 Tap 操作
break;
…
}
}
}
因爲 GestureType.FreeDrag 自由拖曳操作已經包括 GestureType.VerticalDrag 垂直拖曳操作和 GestureType.HorizontalDrag 水平拖曳操作,所以遊戲程序在判斷用戶的觸控操作的動作時,不需要既判斷動作是否爲 GestureType.FreeDrag,又判斷動作是否爲 GestureType.VerticalDrag 或 GestureType.HorizontalDrag,兩者擇一處理即可。
[特別注意]
使用 TouchPanel 類別進行觸控控制的遊戲程序可以呼叫 TouchPanel 類別的 GetState 方法取得使用者對觸控面板的觸控狀態,或是呼叫 TouchPanel 類別的 ReadGesture 方法取得使用者的手勢操作狀態,不要兩者混用,否則將會無法得到正確的結果。例如先利用 TouchPanel 類別的 ReadGesture 方法取得使用者的手勢操作狀態,再利用 TouchPanel 類別的 GetState 方法取得用戶觸碰屏幕的位置,當做手勢操作觸碰屏幕的位置來使用就是錯誤的做法。
請注意呼叫 TouchPanel 類別的 ReadGesture 方法讀取手勢信息時,讀取到的手勢信息會以 GestureSample 結構的型式傳回給呼叫者。表2 所示爲 GestureSample 結構常用的屬性:
屬性名稱 | 說明 |
Delta | 存放與第一個碰觸點的偏差量。 |
Delta2 | 存放與第二個碰觸點的偏差量。 |
GestureType | 存放觸控操作的種類。 |
Position | 存放第一個碰觸點的位置。 |
Position2 | 存放第二個碰觸點的位置。 |
Timestamp | 存放觸控操作發生的時間。 |
因爲呼叫 TouchPanel 類別的 ReadGesture 方法傳回的 GestureSample 結構只能存放第一個和第二觸碰點的位置:Position 和 Postition2,以及存放兩個觸碰點的偏差量,所以只能支持到最多兩個手指頭的操作,事實上 Windows Phone 7 配備的觸控屏幕最多可以支持到多達四個觸碰點的觸控操作,程序可以經由呼叫 TouchPanel 類別的 GetCapabilities 方法查詢觸控屏幕的基本功能,取得 TouchPanelCapabilities 結構型態的傳回值之後,就可以利用 TouchPanelCapabilities 結構的 MaximumTouchCount 成員得知觸控屏幕最大支持的觸碰點數,做法如下:
TouchPanelCapabilities tcs = TouchPanel.GetCapabilities(); //查詢觸控螢幕的基本功能
有關 TouchPanelCapabilities 結構常用的屬性請參考表3 的說明:
屬性名稱 | 說明 |
IsConnected | 查詢觸控屏幕的可用狀態。 |
MaximumTouchCount | 取得支持使用者同時觸碰觸控屏幕的觸碰點數量。 |
如果需要取得用戶同時觸碰屏幕的所有座標點,則可以呼叫 TouchPanel 類別的 GetState 方法,取得所有觸碰點的集合,再利用循環取出集合中的所有觸碰點並加以處理,做法如下:
TouchCollection tc = TouchPanel.GetState(); //取得觸控螢幕的狀態
foreach (TouchLocation tl in tc) //取得所有的觸碰點
{
//利用 tl.Position 取得觸碰點座標
}
[說明]
請注意 GestureSample 結構的屬性的型態爲 TimeSpan 結構而不是 GameTime 類別,用來代表手勢操作與手勢操作之間的時間間隔。
各種手勢操作需要用到的 GestureSample 結構的屬性可以參考表4 的詳細說明:
觸控操作 | 說明 |
Tap | Position 屬性 |
DoubleTap | Position 屬性 |
Hold | Position 屬性 |
VerticalDrag | Position 屬性和 Delta 屬性 |
HorizontalDrag | Position 屬性和 Delta 屬性 |
FreeDrag | Position 屬性和 Delta 屬性 |
DragComplete | 無 |
Flick | Delta 屬性 |
Pinch | Position、Position2、Delta、和 Delta 2 四個屬性 |
PinchComplete | 無 |
處理手勢操作的要訣
在處理使用者的觸控操作方面,Flick 觸控操作可以利用 GestureSample 結構的 Delta 屬性的內容值當做用戶輕拂的快慢速度,以控制捲動遊戲內容的速度。VerticalDrag 和 HorizontalDrag 觸控操作可以利用 GestureSample 結構的 Delta 屬性的內容值判斷垂直和水平移動的距離,而且 VerticalDrag 觸控操作的 Delta 屬性的 X 成員的內容值必爲 0,而 HorizontalDrag 觸控操作的 Delta 屬性的 Y 成員的內容值必爲 0,不需要另外透過程序代碼進行設定。Pinch 觸控操作是兩個手指頭並用的觸控操作,常常用來執行旋轉對象、放大縮小對象、或是旋轉相機鏡頭的動作。遊戲程序可以利用 GestureSample 結構的 Position 屬性和 Delta 屬性,取得第一個手指頭的觸碰點和偏差量,利用 Position2 屬性和 Delta2 屬性取得第二個手指頭的觸碰點和偏差量,再據以執行變更遊戲程序顯示的內容的動作。
設計支持手勢操作的 XNA 遊戲
瞭解 XNA Framework 支持觸控操作的基本功能之後,接下來我們就要設計一個能夠允許用戶利用觸控屏幕操作的簡單遊戲。
首先請啓動 Microsoft Visual Studio 2010 Express,建立型態爲 [XNA Game Studio(4.0)] 型態的項目,然後於 Content Pipeline 項目中加入讓使用者進行觸控操作的圖形的圓形檔案,以及當圓形碰撞到遊戲程序的窗口時欲發出的聲響的聲音文件。例如本範例準備了兩個圓形的圖案,名稱爲 Ball.png 的圖形檔案是一個圓形,名稱爲 HoldBall.png 的圖形檔案是一箇中心有一個紅點的圓形,用來表示被點選的圖形,而 Hitwall.wav 則是當圓形的圖案碰撞到遊戲程序的窗口時欲發出的聲音的聲音文件。
將遊戲程序需要使用的資源加入到 Content Pipeline 項目之後,請開啓遊戲項目中的 Game1.cs 檔案,於類別中加入以下的變量和屬性宣告:
Texture2D Ball; //管理圓形圖案的變數
Texture2D HoldBall; //管理中心有紅點的圓形圖案的變數
Vector2 BallPosition=Vector2.Zero; //記錄圓形圖案位置的變數
bool isHold = false; //記錄使用者是否執行Hold式的觸控操作的變數
SoundEffect HitWall; //管理圖案碰撞遊戲程式視窗的音效的變數
SoundEffectInstance HitWallEffect; //管理欲播放的音效的變數
Vector2 Velocity = Vector2.Zero; //記錄Flick操作速度的變數
public const float Friction = 0.9f; //記錄摩擦力的變數
public const float BounceMagnitude = .5f; //記錄反彈速度的變數
public const float MinScale = .5f; //記載最小縮小比例的變數
public const float MaxScale = 2f; //記載最大放大比例的變數
private float scale = 1f; //記載目前比例的變數
public float Scale //記載目前比例的屬性
{
get { return scale; }
set
{
scale=MathHelper.Clamp(value, MinScale, MaxScale);//控制縮放的比例的最大/最小值
}
}
準備好遊戲程序需要的變量之後,請修改 Game1 類別的建構函式,在建立 GraphicsDeviceManager 類別的對象之後設定 GraphicsDeviceManager 類別的對象的 PreferredBackBufferHeight 屬性和 graphics.PreferredBackBufferWidth 屬性,將遊戲程序的窗口設定成寬 480 x 高 800 的大小。編輯妥的 Game1 類別建構函式如下:
public Game1()
{
graphics = new GraphicsDeviceManager(this);//建立GraphicsDeviceManager類別的物件
Content.RootDirectory = "Content"; //設定載入遊戲資源的根路徑
graphics.PreferredBackBufferHeight = 800; //設定遊戲程式視窗的高度
graphics.PreferredBackBufferWidth = 480; //設定遊戲程式視窗的寬度
// Frame rate is 30 fps by default for Windows Phone.
TargetElapsedTime = TimeSpan.FromTicks(333333); //設定 Update 方法被呼叫的頻率
}
設定妥遊戲程序窗口的大小之後請編輯 Game1 類別的 Initialize 方法,負責啓用手勢操作的功能,編輯妥的 Initialize 方法如下:
protected override void Initialize()
{
// TODO: Add your initialization logic here
TouchPanel.EnabledGestures =
GestureType.Hold |
GestureType.Tap |
GestureType.DoubleTap |
GestureType.FreeDrag |
GestureType.HorizontalDrag |
GestureType.Flick |
GestureType.Pinch; //啟用手勢操作功能
base.Initialize();
}
啓用手勢操作功能之後請於 Game1 類別的 LoadContent 方法加入加載遊戲資源的程序代碼,編輯好的 LoadContent 方法如下:
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
// TODO: use this.Content to load your game content here
Ball = Content.Load<Texture2D>("Ball"); //載入圓形圖案
HoldBall = Content.Load<Texture2D>("HoldBall"); //載入中心有紅點的圓形圖案
BallPosition = new Vector2((Window.ClientBounds.Width - Ball.Width) / 2,
(Window.ClientBounds.Height-Ball.Height)/2);//設定圓形圖案要顯示在視窗的正中央
// TODO: use this.Content to load your game content here
HitWall =
Content.Load<SoundEffect>("HitWall");//載入圓形圖案撞擊視窗邊界要發出的音效檔案
HitWallEffect =
HitWall.CreateInstance(); //利用 SoundEffect 類別的物
//件建立 SoundEffectInstance 類別的物件
HitWallEffect.Apply3D(new AudioListener(),
new AudioEmitter()); //呼叫SoundEffectInstance類別的 Apply3D 方法套用 3D 音效
}
您可以視需要修改上述的程序代碼加載的資源名稱,以加載遊戲程序執行時需要的資源。
加載妥遊戲程序需要使用的資源之後,請修改名稱爲 Update 的方法,以反應使用者的手勢操作動作。加入手勢控制功能的 Update 方法如下:
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
// TODO: Add your update logic here
while (TouchPanel.IsGestureAvailable) //判斷使用者是否執行了手勢操作
{
GestureSample gesture = TouchPanel.ReadGesture(); //讀取使用者的手勢操作
switch (gesture.GestureType) //判斷使用者的手勢操作型態
{
case GestureType.Tap: //Tap 操作
case GestureType.DoubleTap: //DoubleTap 操作
BallPosition.X=gesture.Position.X-Ball.Width/2; //設定圖案顯示在使用
BallPosition.Y=gesture.Position.Y-Ball.Height/2; //者觸碰的座標點
break;
case GestureType.Hold: //Hold 操作
Rectangle BallRect = new Rectangle(0, 0, (int)(Ball.Width * Scale),
(int)(Ball.Height * Scale)); //計算圖形的矩形大小
if (BallRect.Contains((int)(gesture.Position.X - BallPosition.X),
(int)(gesture.Position.Y - BallPosition.Y)))//判斷觸碰點是否在矩形中
{
isHold = true; //將 IsHold 變數的內容值設定為 true
}
break;
case GestureType.FreeDrag: //FreeDrag 操作
BallPosition += gesture.Delta; //循著使用者拖曳的軌跡移動圖形
break;
case GestureType.Flick: //Flick 操作
Velocity = gesture.Delta; //設定移動圖形的速度
break;
case GestureType.Pinch: //Pinch 操作
Vector2 a = gesture.Position; //取得第一個觸碰點
Vector2 aOld =
gesture.Position - gesture.Delta;//取得第一個觸碰點的起始位置
Vector2 b = gesture.Position2; //取得第二個觸碰點
Vector2 bOld =
gesture.Position2 - gesture.Delta2;//取得第二個觸碰點的起始位置
float d = Vector2.Distance(a, b); //計算兩個觸碰點之間的距離
float dOld = Vector2.Distance(aOld, bOld);//計算兩個原始座標之間的距離
float scaleChange = (d - dOld) * .01f; //計算距離的變化量
Scale += scaleChange; //將距離變化量的 1/10 當做縮放的比例
break;
}
}
BallPosition += Velocity *
(float)gameTime.ElapsedGameTime.TotalSeconds; //依據Flick操作的速度移動圖形
Velocity *= 1f - (Friction *
(float)gameTime.ElapsedGameTime.TotalSeconds);//利用磨擦係數減緩圖形的移動速度
float HalfWidth = (Ball.Width * Scale) / 2f; //取得圖形寬度的 1/2
float HalfHeight = (Ball.Height * Scale) / 2f; //取得圖形高度的 1/2
if (BallPosition.X < Window.ClientBounds.Left) //判斷圖形是否觸及視窗的左邊界
{
BallPosition.X=Window.ClientBounds.Left;//設定圖形左上角點的X座標等於視窗左邊界
Velocity.X *= -BounceMagnitude; //設定反彈的速度為目前速度的一半
HitWallEffect.Play(); //播放碰撞視窗邊界的音效
}
if (BallPosition.X > Window.ClientBounds.Right –
Ball.Width * Scale) //判斷圖形是否觸及視窗的右邊界
{
BallPosition.X = Window.ClientBounds.Right –
Ball.Width * Scale; //設定圖形右上角點的X座標等於視窗右邊界
Velocity.X *= -BounceMagnitude; //設定反彈的速度為目前速度的一半
HitWallEffect.Play(); //播放碰撞視窗邊界的音效
}
if (BallPosition.Y < Window.ClientBounds.Top) //判斷圖形是否觸及視窗的上邊界
{
BallPosition.Y = Window.ClientBounds.Top;//設定圖形左上角點的Y座標等於視窗上邊界
Velocity.Y *= -BounceMagnitude; //設定反彈的速度為目前速度的一半
HitWallEffect.Play(); //播放碰撞視窗邊界的音效
}
if (BallPosition.Y > Window.ClientBounds.Bottom –
Ball.Height * Scale) //判斷圖形是否觸及視窗的下邊界
{
BallPosition.Y = Window.ClientBounds.Bottom –
Ball.Height * Scale; //設定圖形右下角點的Y座標等於視窗下邊界
Velocity.Y *= -BounceMagnitude; //設定反彈的速度為目前速度的一半
HitWallEffect.Play(); //播放碰撞視窗邊界的音效
}
base.Update(gameTime);
}
最後我們還要修改名稱爲 Draw 的方法,將遊戲程序顯示的圖形顯示在反應用戶手勢操作的位置上,做法如下:
protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); // TODO: Add your drawing code here spriteBatch.Begin(); //開始繪製遊戲內容 if (isHold) //如果圖形被使用點中 { spriteBatch.Draw(HoldBall, BallPosition, null, Color.White, 0, Vector2.Zero, Scale, SpriteEffects.None, 0); //在指定的位置顯示中心有紅點的圓形 } else { spriteBatch.Draw(Ball, BallPosition, null, Color.White, 0, Vector2.Zero, Scale, SpriteEffects.None, 0); //在指定的位置顯示圓形 } spriteBatch.End(); //結束繪製遊戲內容 base.Draw(gameTime); }
做好之後請執行 [建置] 項目的動作。
將設計妥的遊戲程序部署到 Windows Phone 7 智能型手機
因爲 Windows Phone 7 仿真器不支持用戶觸控操作,所以允許使用者利用手勢進行遊戲操作的遊戲程序必須部署到真實的 Windows Phone 7 智能型手機才能測試和手勢操作有關的功能是否正確,無法透過 Windows Phone 7 仿真器進行測試,因爲在一般計算機上執行的 Windows Phone 7 仿真器無法接受用戶透過手勢動作進行操作。
[提示]
如果用戶所使用的計算機配備的是觸控屏幕,則在配備觸控屏幕的計算機執行的 Windows Phone 7 仿真器仍然可以允許用戶透過觸控屏幕進行操作。
欲將開發妥的遊戲程序部署到實際的 Windows Phone 7 智能型手機,而不是 Windows Phone 7 仿真器,程序設計師使用的計算機必須先安裝Zune 軟件。安裝完成後請重新啓動計算機,並將 Windows Phone 7 智能型手機連上開發計算機的 USB 端口,所安裝的 Zune 軟件將會在 Windows Phone 7 智能型手機連上計算機時自動啓動。圖1 所示即爲 Zune 軟件激活後的執行畫面:
圖1:Zune 執行的畫面
Zune 自動啓動之後,請執行 [所有程序 | Windows Phone Developer Tools] 羣組中的 [Application Deployment] 程序,並在 [Application Deployment] 程序啓動之後於 [Target] 下拉式選項中選擇:Windows Phone 7 Device,再按下 [Browse] 鍵瀏覽到建置項目成功得到的 XAP 檔案,如圖2 所示:
圖2:使用 [Application Deployment] 程序部署 Windows Phone 7 遊戲程序的畫面
做好之後按下 [Deploy] 鍵將所選擇的 Windows Phone 7 遊戲程序部署到 Windows Phone 7 智能型手機。請注意 Windows Phone 7 智能型手機不可處於屏幕鎖定的狀態,否則將無法部署成功。
[提示]
除了可以利用 [Application Deployment] 程序部署 Windows Phone 7 遊戲程序到 Windows Phone 7 智能型手機以外,程序設計師也可以利用 Visual Studio 2010 Express for Windows Phone 直接將所開發的程序部署到 Windows Phone 7 智能型手機,不過程序設計師所使用的計算機仍然必須事先安裝好 Zune 軟件。程序設計師可以利用 [XNA Game Studio Device Management] 工具欄提供的下拉選項 [ ] 選擇到 Windows Phone 7 Device,再按下 CTRL+F5 組合鍵將開發好的程序部署到 Windows Phone 7 智能型手機。同樣地,Windows Phone 7 智能型手機不可以處於屏幕鎖定的狀態,否則將無法部署成功。
請執行部署到 Windows Phone 7 智能型手機的遊戲程序,並利用 XNA Framework 支持的各種手勢操作技巧點選、自由移動、輕拂、或放大縮小遊戲程序顯示的餅圖案,體驗利用手勢操作程序的高度方便性。圖3所示即爲允許用戶利用手勢操作的程序執行的情形:
圖3:允許用戶利用手勢操作的程序執行的情形
請注意當餅圖案被輕拂至碰撞到程序的窗口時會發出聲響併產生反彈。
程序下載:GestureControl.zip
PS:如果是Windows Phone8的真機解鎖:
WP8的解鎖與WP7的不同。(主要是不用再理會Zune了)
我們還是用“Windows Phone Developer Registration”進行解鎖,但是可能在註冊的時候會遇見“make sure the IpOverUsbSvc is running”的提示,我們只需打開“任務管理器”->“服務”->找到“IpOverUsbSvc”服務,先“停止”然後“開始”即可解決該問題。
當然了,以後在調試的時候再也不用開啓Zune神馬的了,只要保證IpOverUsbSvc在運行即可。
最後,解鎖後的手機,可以通過 XapDeploy 工具直接部署Xap應用,在 X:\Program Files (x86)\Microsoft SDKs\Windows Phone\v8.0\Tools\XAP Deployment文件夾中即可找到。