[Teaching] [Silverlight] 用 Silverlight 一起開發Kuso小遊戲《捏氣泡》- 程式人員篇

 

楔子

接下來,我們會帶領大家一起製作一款如下圖的《捏氣泡》遊戲:

我們採用的技術平台及工具如下:

1. Silverlight 4

2. Visual Studio 2010

3. Blend 4

clip_image004clip_image006clip_image008

圖說:捏氣泡最終完成圖

 

本文是針對程式開發人員所撰寫。

 

我和Bear合作的方式,是由我們先共同討論出設計的大方向,接著由我先透過Visualstudio製作出骨架,再交給Bear使用Blend套上介面。

在本文中,程式人員會學到下列遊戲開發上的相關技巧

  • 迴圈的建立&用法
  • 圖型顯示的方法
  • 播放音效的方法
  • 相依屬性的建立
  • 封裝屬性的建立
  • 外部儲存區使用
  • OOB的使用

 

事前準備

整款《捏氣泡》遊戲架構如下,共有4頁以及一個氣泡的Usercontrol,另外需要準備3款氣泡的圖,以及一個氣泡破裂的音效。

StartPage.xaml:遊戲開始頁。按一下會開始遊戲。

PlayPage.xaml:遊戲進行頁。

InstallPage.xaml:遊戲安裝頁。可安裝《捏氣泡》到本機桌面執行。

EndPage.xaml:遊戲結束頁。可統計分數,返回首頁或重新遊戲。

BubbleControl.xaml:氣泡的Usercontrol。

 

Step 1. 設定換頁

由於整款遊戲有4頁,會依照不同狀態進行切換,所以我們先在app.xaml加入一些程式碼,可以提供方便的換頁功能,這並不是SL3.0的導覽機制,只是單純將頁面當成Usercontrol加入,以達到換頁的效果。

app.xam.cs

//換頁

Grid navigationUI = new Grid();

public static void NavigateToPage(UserControl page)

{

App application = (App)Application.Current;

application.navigationUI.Children.Clear();

application.navigationUI.Children.Add(page);

}

private void Application_Startup(object sender, StartupEventArgs e)

{

//this.RootVisual = new PlayPage();

this.RootVisual = navigationUI;

navigationUI.Children.Add(new InstallPage());

}

這樣未來在page裡,就可以用下面非常簡單的code進行換頁的動作

app.xam.cs

App.NavigateToPage(new PlayPage());
 

另外我們建立兩個變數,一個拿來存放每一局的消耗時間,另一個拿來存放目前已經累積捏破的氣泡

app.xam.cs

//計時

public static TimeSpan UseTime { set; get; }

//計數量

public static int TotalBroken { set; get; }

 

Step 2. 設定外部儲存區

由於我們希望氣泡數量可以持續累積,而不是在遊戲關閉後,紀錄就消失,因此我們會用到Silverlight中的IsolatedStorage。讓應用程式在執行前,讀出先前紀錄,關閉前,再將記錄存回去。

app.xam.cs

private void Application_Startup(object sender, StartupEventArgs e)

{

//this.RootVisual = new PlayPage();

this.RootVisual = navigationUI;

navigationUI.Children.Add(new InstallPage());

//讀取LuIsoSeetings

ReadIsoSeetings("TotalBroken");

}

private void Application_Exit(object sender, EventArgs e)

{

//儲x存sIsoSeetings

SaveIsoSeetings("TotalBroken");

}


private void ReadIsoSeetings(String Key)

{

//讀取LuIsolatedStorageSettings

IsolatedStorageSettings IsoStorageSetting = IsolatedStorageSettings.ApplicationSettings;

string stringIsoStorageSet = null;

bool IsIsoStorageSetError = false;

try

{

stringIsoStorageSet = IsoStorageSetting[Key].ToString();

}

catch (System.Collections.Generic.KeyNotFoundException ex)

{

IsIsoStorageSetError = true;

}

if (IsIsoStorageSetError)

{

IsoStorageSetting.Add(Key, TotalBroken);

IsoStorageSetting.Save();

}

else

{

TotalBroken = int.Parse(stringIsoStorageSet);

}

}

private static void SaveIsoSeetings(String Key)

{

//存入LuIsolatedStorageSettings

IsolatedStorageSettings IsoStorageSetting = IsolatedStorageSettings.ApplicationSettings;

IsoStorageSetting[Key] = TotalBroken.ToString();

IsoStorageSetting.Save();

}

 

製作氣泡的UserControl

 

《捏氣泡》遊戲中,我們會製做一個氣泡的UserControl,並且將封裝的屬性和相依屬性寫在這個UserControl內。

 

氣泡的功能如下:

1. 本身有一個持續放大縮小的視覺特效,可以讓設計人員自行決定是否要開啟

2. 可以讓設計人員自行設定氣泡顏色

3. 按下氣泡會發出聲音

4. 氣泡破掉有兩種效果

 

Step 1. 設定xaml

3段Storyboard,一段是播放特效,2段是2種破裂的狀態。

效果之後會交給設計人員處理。

<UserControl.Resources>

<Storyboard x:Name="sb_Effect" />

<Storyboard x:Name="sb_BrokenOne"/>

<Storyboard x:Name="sb_BrokenTwo">

</UserControl.Resources>

3個控制項,一張氣泡正常的圖片,

2張氣泡破裂的圖片,一個撥放音效的控制項。

控制項內容之後會交給設計人員處理。

<Grid x:Name="LayoutRoot" >

<Image x:Name="Image_Normal" />

<Image x:Name="Image_BrokenTwo" />

<Image x:Name="Image_BrokenOne" />

<MediaElement x:Name="mediaElement" />

</Grid>

 

Step 2. 設定變數:(從2種破裂效果中,隨機抽出1種)

BubbleControl.xaml.cs

private Random _rnd = new Random((int)DateTime.Now.Ticks);
 

Step 3. 設定屬性:

BubbleControl.xaml.cs

private bool _isEffect; //是否要開啟放大縮小特效

private bool _isBroken; //氣泡是否已經破裂

public int EffectDelayTime { set; get; } //放大縮小特效的延遲時間

public bool IsBroken { set { if (!IsBroken) { _isBroken = value; SetBroken(value); } } get { return _isBroken; } }

public bool IsEffect { set { _isEffect = value; SetEffect(value); } get { return _isEffect; } }

 

 

Step 4. 設定氣泡破掉:

這段程式碼將會從2段Storyboard中,抽出一段來撥放。

也就是一段Storyboard是其中一種破裂特效,另一段則是另一種破裂特效。

在這裡,其實是透過Storyboard切換3張圖片的Visibility

 

BubbleControl.xaml.cs

private void SetBroken(bool value)

{

if (value)

{

if (_rnd.Next(2) == 0)

{

sb_BrokenOne.Begin();

}

else

{

sb_BrokenTwo.Begin();

}

SetEffect(false);

PlaySound();

AddTotalBroken();

}

else

{

sb_BrokenOne.Stop();

sb_BrokenTwo.Stop();

}

}

 

Step 5. 設定特效:

為了讓特效看起來更自然,每顆氣泡有閃動的效果,也避免特效的觸發時間都一樣,所以加入了延遲時間,可以讓設計人員自行設定。

BubbleControl.xaml.cs

private void SetEffect(bool value)

{

if (value)

{

sb_Effect.BeginTime = TimeSpan.FromSeconds(EffectDelayTime);

sb_Effect.Begin();

}

else

{

sb_Effect.Stop();

}

}

 

Step 6. 氣泡破掉時播放音效

BubbleControl.xaml.cs

public void PlaySound()

{

mediaElement.Stop();

mediaElement.Play();

}

 

Step 7. 增加破裂氣泡數

只要氣泡破裂後,就做一次記錄,將氣泡加入累積總數。

BubbleControl.xaml.cs

private void AddTotalBroken()

{

App.TotalBroken++;

}

 

Step 8. 設定相依屬性

設定相依屬性的主要目的是讓UserControl的屬性,未來在PlayPage中也可以進行設定。

因此,我們將設定ImageSource相依屬性,好讓設計人員未來可以自行更換圖片。

因為有3款圖片(正常、破裂1、破裂2),因此我們將設定3款相依屬性,方便設計人員未來自行使用。

BubbleControl.xaml.cs

//相依屬性

public static readonly DependencyProperty ImageSourceNormalProperty = DependencyProperty.Register("ImageSourceNormal", typeof(ImageSource), typeof(BubbleControl), null);

public static readonly DependencyProperty ImageSourceBrokenOneProperty = DependencyProperty.Register("ImageSourceBrokenOne", typeof(ImageSource), typeof(BubbleControl), null);

public static readonly DependencyProperty ImageSourceBrokenTwoProperty = DependencyProperty.Register("ImageSourceBrokenTwo", typeof(ImageSource), typeof(BubbleControl), null);

public ImageSource ImageSourceNormal

{

get { return (ImageSource)this.GetValue(ImageSourceNormalProperty); }

set { this.SetValue(ImageSourceNormalProperty, value); }

}

public ImageSource ImageSourceBrokenOne

{

get { return (ImageSource)this.GetValue(ImageSourceBrokenOneProperty); }

set { this.SetValue(ImageSourceBrokenOneProperty, value); }

}

public ImageSource ImageSourceBrokenTwo

{

get { return (ImageSource)this.GetValue(ImageSourceBrokenTwoProperty); }

set { this.SetValue(ImageSourceBrokenTwoProperty, value); }

}

 

製作InstallPage

 

我們希望這款遊戲,是從瀏覽器安裝到桌面後,才可以運作,因此我們會製作一個InstallPage,作為起始頁。當判斷玩家是運作在桌面後,才導到StartPage。

 

Step 1. 設定OOB

clip_image010

圖說:在專案上按右鍵,選擇屬性》

 

clip_image012

圖說:開啟OOB並進行相關設定》

 

Step 2. 建立判斷

InstallPage.xaml

<TextBlock x:Name="tb_Install"></TextBlock>

InstallPage.xaml.cs

public InstallPage()

{

InitializeComponent();

this.Loaded += new RoutedEventHandler(InstallPage_Loaded);

Application.Current.InstallStateChanged += new EventHandler(Current_InstallStateChanged);

}

void Current_InstallStateChanged(object sender, EventArgs e)

{

//throw new NotImplementedException();

if (Application.Current.InstallState == InstallState.Installed)

{

tb_Install.Text = "Installed";

}

}

void InstallPage_Loaded(object sender, RoutedEventArgs e)

{

//throw new NotImplementedException();

if (Application.Current.IsRunningOutOfBrowser)

{

App.NavigateToPage(new StartPage());

}

if (Application.Current.InstallState == InstallState.Installed)

{

tb_Install.Text = "Installed";

}

}

private void tb_Install_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)

{

try

{

Application.Current.Install();

}

catch

{

MessageBox.Show("Game has been installed!");

}

}

 

 

製作StartPage

clip_image004[1]

 

Step 1. 建立觸發事件

 

StartPage非常簡單,只是一個引導頁,當按下開始的文字後,就會導到PlayPage

StartPage.xaml

<TextBlock x:Name="tb_Start" MouseLeftButtonDown="tb_Start_MouseLeftButtonDown" Text="Start" />

StartPage.xaml.cs

private void tb_Start_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)

{

App.NavigateToPage(new PlayPage());

}

 

製作PlayPage

clip_image013

Step 1. 建立迴圈

建立迴圈的方式可參考下列文章。

 

30快速建立遊戲迴圈

 

建立迴圈的目的,是可以方便我們進行一些偵測,在《捏氣泡》遊戲中,迴圈有兩個目的。

1. 更新遊戲時間

2. 偵測氣泡是不是已經全部被捏破

 

所以遊戲的迴圈,是會建立在PlayPage.xaml這一個頁面

 

使用StoryBoard可以建立一個每0.033秒會偵測一次的迴圈,如果以遊戲開發的術語來說,就是每秒30格的遊戲。

 

PlayPage.xaml

<UserControl.Resources>

<Storyboard x:Name="sb_Timer" Duration="0:0:0.033" Completed="sb_Timer_Completed"/>

</UserControl.Resources>

PlayPage.xaml.cs

bool IsGameLoopRunning = true;

private void sb_Timer_Completed(object sender, EventArgs e)

{

//GameLoop

ShowUseTime(); //更新遊戲時間

CheckUcState(); //偵測氣泡是不是已經全部被捏破

if (IsGameLoopRunning)

{

sb_Timer.Begin();

}

}

 

有時候在CheckUcState()後,遊戲迴圈就可以停止了。因此,另外會宣告一個變數IsGameLoopRunning,用來判斷遊戲迴圈是不是必須持續運作。

 

Step 2. 迴圈的工作-更新遊戲時間

《捏氣泡》會判斷玩家捏完一輪氣泡的時間,而此時間,也會同步在畫面上顯示。

 

因此,我將這項工作放在迴圈裡,在迴圈運作時,一併處理。

 

PlayPage.xaml.cs

<TextBlock x:Name="tb_UseTime" Text="00:00:00"/>

PlayPage.xaml.cs

//宣告變數-計時

DateTime time_start = DateTime.Now;

DateTime time_end;

TimeSpan ts;

private void ShowUseTime()

{

time_end = DateTime.Now;

ts = (TimeSpan)(time_end - time_start);

DateTime dt = new DateTime(ts.Ticks);

//顯示時間

tb_UseTime.Text = dt.ToString("mm:ss:ff");

}

 

Step 3. 迴圈的工作-偵測氣泡是不是已經全部被捏破

 

《捏氣泡》還會判斷玩家是否已經捏完所有氣泡,因此,我也將這項工作放在迴圈裡,在迴圈運作時,一併處理。

private void CheckUcState()

{

int j = 0;

for (int i = 1; i <= 24; i++)

{

BubbleControl bubble = this.LayoutRoot.FindName("BubbleControl_" + i) as BubbleControl;

if (bubble.IsBroken)

{

j++;

}

}

if (j >= 24)

{

//全部捏破後,紀錄目前的消耗時間

App.UseTime = ts;

IsGameLoopRunning = false;

NavigateToEndPage();

}

}

 

Step 4. 迴圈初始化

 

在每一次跳到PlayPage的同時,要進行一次初始化動作,進行3件事情。

 

1. 設定迴圈變數。(設定迴圈正在運作)

2. 將遊戲時間歸零

3. 將氣泡的狀態恢復成初始的狀態

public PlayPage()

{

InitializeComponent();

GameStart();

}

public void GameStart()

{

//初始化

IsGameLoopRunning = true;

//時間歸零

App.UseTime = new TimeSpan();

//氣泡狀態賦歸

for (int i = 1; i <= 24; i++)

{

BubbleControl bubble = this.LayoutRoot.FindName("BubbleControl_" + i) as BubbleControl;

bubble.IsBroken = false;

}

//循環開始

sb_Timer.Begin();

}

 

 

Step 5. 遊戲結束後

遊戲結束後,將會跳到EndPage,計算分數,以及消耗的時間。

//換頁

private void NavigateToEndPage()

{

App.NavigateToPage(new EndPage());

}

 

製作EndPage

clip_image014

EndPage的目的是顯示使用的時間,以及顯示目前累積的總氣泡數。另外,在按下Again文字後,可以立刻重新開始。按下Menu會回到Start頁面。

 

Step 1. 遊戲結束後

public EndPage()

{

InitializeComponent();

//顯示時間!

ShowUseTime();

//顯示總氣泡數

ShowTotalBroken();

}

private void ShowTotalBroken()

{

//throw new NotImplementedException();

tb_TotalBroken.Text = App.TotalBroken.ToString();

}

private void ShowUseTime()

{

DateTime dt = new DateTime(App.UseTime.Ticks);

tb_UseTime.Text = dt.ToString("mm:ss:ff");

}

private void tb_Again_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)

{

App.NavigateToPage(new PlayPage());

}

private void tb_Menu_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)

{

App.NavigateToPage(new StartPage());

}
小結

到目前為止,已經將程式的骨架設定完成,程式人員的工作已經告一段落。

 

可以將專案檔,交給設計人員進行介面的設計,設計人員的工作包括以下項目。

 

  • 版面配置
  • 封裝屬性的使用
  • 相依屬性的使用
  • 動畫腳本的建立
  • 行為的使用

接下來,我們將會一步步的,帶領設計人員一起完成這款遊戲。

 

B+ StudioCopyright © 2010 B+ Studio.

posted on 2010-10-05 20:18  B+  阅读(393)  评论(0编辑  收藏  举报

导航