顯示具有 Silverlight 標籤的文章。 顯示所有文章
顯示具有 Silverlight 標籤的文章。 顯示所有文章

2010年4月15日 星期四

Game Loop Compare- DispacterTimer, Storyboard, Composition.Rendering

Game Loop 對寫遊戲來說是一個很重要的步驟, 通常我們會將一些不需長時間且不用大量計算的事情, 放在Game Loop裡面去作, 例如:碰撞偵測, 動畫撥放, 更新繪圖座標, 檢查滑鼠或鍵盤的狀態..等.

WPF/Silverlight中, 可以透過下列方式來實作Game Loop,分別是:

  • DispatcherTimer - 可以在指定的時間間隔內, 處理佇列中的執行緒.
  • Storyboard - 以時間軸控制動畫, 和DispatcherTimer的作法類似, 透過Duration屬性決定時間長短, 然後在Completed事件中再次呼叫StoryboardBegin方法, 就可以達到類似計時器的迴圈.
  • CompositionTarget.Rendering - 每當程式要顯示一個畫面(frame)時, 就會引發Rendering事件

下列程式碼將示範如何使用上述的方法, 我們會讓一個圓球在視窗內作移動, 當碰到邊界時會作反彈, 而圓球的座標是在Game Loop中作更新, 並顯示對應的FPS值, 如下圖所示:

[圖 1]利用Game Loop作動畫

xmal檔如下:

<Window x:Class="GameLoopTesting.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" SizeToContent="WidthAndHeight">
<Canvas x:Name="LayoutRoot" Background="Black" Width="800" Height="600">
<TextBlock x:Name="txtFPS"
Foreground="White"
FontFamily="Arial"
FontSize="24"
Text="FPS:"
Panel.ZIndex="99"/>
<Ellipse x:Name="ball"
Width="50"
Height="50"
Fill="LightBlue"
Stroke="Gray"
Canvas.Left="100"
Canvas.Top="100">
</Ellipse>
</Canvas>
</Window>



cs檔如下:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Threading;
using System.Windows.Media.Animation;

namespace GameLoopTesting
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public enum GameLoopType
{
DISPATCHERTIMER,
STORYBOARD,
RENDERING
}

/// <summary>
/// For object parameters
/// </summary>
private float radius;
private double dx = 1;
private double dy = 1;
private double x = 0;
private double y = 0;


/// <summary>
/// For FPS property
/// </summary>
private int _frameCounter = 0;
private double _timeSinceLastUpdate = 0.0;
private DateTime _lastUpdateTime = DateTime.MinValue;
private double _fpsValue = 0.0;

private Storyboard _sbTrigger;


public Window1()
{
InitializeComponent();

radius = (float)ball.Width/2;
_lastUpdateTime = DateTime.Now;


///Switch game loop type
SwitchGameLoop(GameLoopType.DISPATCHERTIMER);
}


void SwitchGameLoop(GameLoopType gameLoopType)
{
switch (gameLoopType)
{
case GameLoopType.DISPATCHERTIMER:
{
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(10);
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}
break;

case GameLoopType.STORYBOARD:
{
_sbTrigger = new Storyboard();
_sbTrigger.Duration = TimeSpan.FromMilliseconds(10);
_sbTrigger.Completed += new EventHandler(sbTrigger_Completed);
_sbTrigger.Begin();

}
break;


case GameLoopType.RENDERING:
{
CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);

}
break;
}
}

void timer_Tick(object sender, EventArgs e)
{
/// Calculate FPS value
/// -----------------------------------------------------------
_frameCounter++;

if ((DateTime.Now - _lastUpdateTime).Seconds >= 1)
{
txtFPS.Text = "FPS : " + _frameCounter;
_frameCounter = 0;
_lastUpdateTime = DateTime.Now;
}
//-----------------------------------------------------------

UpdateObjectPosition();
}

void sbTrigger_Completed(object sender, EventArgs e)
{
UpdateObjectPosition();
_sbTrigger.Begin();
}

void CompositionTarget_Rendering(object sender, EventArgs e)
{
/// Calculate FPS value
/// -----------------------------------------------------------
TimeSpan elapsedTime = DateTime.Now - _lastUpdateTime;

_frameCounter++;
_timeSinceLastUpdate += elapsedTime.TotalSeconds;

if (_frameCounter >= 100)
{
_fpsValue = (int)(_frameCounter / _timeSinceLastUpdate);
txtFPS.Text = "FPS : " + _fpsValue;

_frameCounter = 0;
_timeSinceLastUpdate = 0;
}
_lastUpdateTime = DateTime.Now;
//----------------------------------------------------------------

UpdateObjectPosition();
}


/// <summary>
/// Update the position of object
/// </summary>
private void UpdateObjectPosition()
{
x = ((double)Canvas.GetLeft(ball));
y = ((double)Canvas.GetTop(ball));

x += dx;
y += dy;

Canvas.SetLeft(ball, x);
Canvas.SetTop(ball, y);

if (x > LayoutRoot.Width - 2 * radius)
{
dx = -Math.Abs(dx);
}
if (x < 0)
{
dx = Math.Abs(dx);
}
if (y > LayoutRoot.Height - 2 * radius)
{
dy = -Math.Abs(dy);
}
if (y < 0)
{
dy = Math.Abs(dy);
}
}
}
}


總 結:
* 在WPF中使用Storyboard作Game Loop時, 會發生畫面更新到一半時, 卻停止更新,但Silverlight上卻不會, 原因目前還不知道.
* 透過上述的3種方式, 都可以作為遊戲的迴圈, 不過穩定度和解析度上有所差別.可參考這篇Game Loop部落格的詳細比較.
就穩定度來說 :
CompositionTarget.Rendering > Storyboard > DispatcherTimer


參考文章:
Creating a Game Loop
Application (Game) Loop, FPS and Sprite like Animation
Game Loop

2010年4月13日 星期二

如何在Silverlight的應用程式上作Debug

在開發Silverlight的應用程式時, 你可能要用到Debug功能, 來找出程式的錯誤, 但在Silverlight專案執行Debug功能, 可能會出現下面訊息, 而無法除錯.



解決的方法如下 :
Visual Studio 2008 SP1 的 Silverlight 3 Tools 包括:
* Visual Basic 和 C# 專案範本
* Intellisense 和 XAML 程式碼產生器
* Silverlight 應用程式偵錯
* 適用於 Mac 的 Silverlight 應用程式遠端偵錯
* Web 參考支援
* WCF 範本
* Team Build 和命令列建置支援
* 支援快取的透明平台擴充程式
* 支援 Silverlight 3 瀏覽器外用應用程式

安裝上述工具後, 即可解決上述問題.
另外若預設瀏覽器可能不是IE, 可能會發生執行Silverlight的程式沒有畫面, 此時就必須將IE設定為預設瀏覽器, 作法如下:
  1. 開啟 Internet Explorer 瀏覽器視窗。
  2. 選取 [工具] > [網際網路選項]。
  3. 選取 [程式集] 標籤。
  4. 在 [預設網頁瀏覽器] 區段中按一下 [設成預設值]。
  5. 按一下 [確定]

2010年4月11日 星期日

Touch Game Framework

最近要開發一些Touch的遊戲, 所以構思了整個Game Framework.

下面的表格列出之前survey的一些game engine, 不過對我來說還是有一些技術性的問題,例如: touch功能的支援, UI的建立, 軟體收費, 支援平台等等.


[圖] XNA, Unity, Blender的比較表

所以最後構思的遊戲架構如下:


應用程式 - 選用Silverlight平台, 主要優點如下:
  • 豐富的圖形及使用者互動功能: Silverlight 包含了 WPF技術的子集,可大幅擴充瀏覽器中用來建立 UI 的項目. 另外, 它具有向量式的繪圖能力, 3D繪圖和製作動畫等等.
  • 影音播放 - 它會以資料流方式處理視訊和音訊. 它可以調整視訊品質, 以配合從行動裝置到桌面瀏覽器, 甚至是到 720p HDTV 視訊模式等任何裝置.
  • 支援Windows 7Touch功能 - Silverlight 在3.0版本後就支援Windows 7的觸控功能, 可參考這篇文章的簡介 - Silverlight 3.0 + Multi-touch
  • 跨瀏覽器、跨平台的支援 - 它可以在所有常見的 Web 瀏覽器 (包括 Microsoft Internet ExplorerMozilla Firefox Apple Safari) 以及 Microsoft Windows 和 Apple Mac OS 上執行Silverlight, 可參考 =>支援的平台和瀏覽器
物理引擎 - 選用Farseer Physics Engine. 它是一個在.NET平台上使用的物理引擎(它是open source), 主要用來開發Silverlight和XNA2D遊戲. 它提供很多物理相關的特性, 例如: 重力, 作用力, 力矩,碰撞,彈簧和關節..等等.


參考文章 :
Farseer Physics Engine 3.0

msdn - Silverlight

2010年2月24日 星期三

Silverlight 3.0 + Multi-touch

Silverlight是微軟最新的Web用戶介面技術,它能夠跨流覽器、跨平臺運行.
Silverlight 3.0 上也支援多點觸控功能 , 但目前功能還是相當有限, 只能取得觸控點的資料,
若要想要有手勢的功能的話, 需要透過觸控點另外作計算.


Silverlight的多點觸控架構如下:



從上圖可知, 要寫一個Silverlight的觸控程式, 必要元素要有: Touch Device + Windows 7 + IE 8

Windows Touch API 不一樣的地方, Silverlight 不需要註冊RegisterTouchWindow, 是以整個應用程式為範圍來註冊多點觸控事件, 而不是在UIElement 加入觸控事件(WPF).

撰寫Silverlight multi-touch application的流程如下:

(1)註冊多點觸控的處理常式 - TouchFrameReported(它是一個static function)
Touch.FrameReported += new TouchFrameEventHandler(Touch_FrameReported);

針對多點觸控所發生的事件,Silverlight預設將多點觸控事件提升為滑鼠事件, 若不希望升級成滑鼠事件, 可以呼叫 SuspendMousePromotionUntilTouchUp

(2)取得觸控點的資料
TouchPoint GetPrimaryTouchPoint(UIElement relativeTo) --> 取得主要觸控點資料
TouchPointCollection GetTouchPoints(UIElement relativeTo) --> 取得所有觸控點資料

傳入一個UIElement將回傳相對偏移的 Position 產生包含相對座標的 TouchPoint.
如果想要獲得觸控點的的絕對座標, 請將 relativeTo 指定為 null.

觸控點的其他資訊如下所示;

example code:
foreach (TouchPoint tp in e.GetTouchPoints(_canvas))
{
// 取得作業系統針對特定觸控裝置所提供的唯一 ID
tp.TouchDevice.Id;

// 取得當觸控點產生時,滑鼠指標所停留的最上層 UIElement 物件
tp.TouchDevice.DirectlyOver

// 取得觸控點的 X, Y 座標位置(相當於觸控區域的中心點位置)
tp.Position;

// 取得觸控點接觸區域(其區域為一個矩形)
tp.Size;
}


(3) 識別觸控的動作 -TouchAction
觸控點的動作可分成下列3種:


TouchAction.Down: 來自 TOUCHEVENTF_DOWN 訊息的觸控動作
TouchAction.Move: 來自 TOUCHEVENTF_MOVE 訊息的觸控動作

TouchAction.Up:來自 TOUCHEVENTF_UP 訊息的觸控動作


下圖為利用Silverlight開發的繪圖板. 因此在網頁上也可利用觸控作繪圖.




P.S: 若要使用Silverlight 來作為開發工具. 如果您沒有 Visual Web Developer,請使用 Web Platform Installer,如果您已有 Visual Studio, 請直接下載這個工具 ( Silverlight 3 SDK )

參考文章:
Silverlight 3 Multi-touch: The Basics
Silverlight 3 中的multi-Touch程式開發
Silverlight 3 Multi-Touch with Windows 7 and HP Touch Smart