Silverlightで画面上にキャラクタ(スプライト)を表示する方法は、DirectDrawやDirect3Dのようにバックバッファに対して自力で描画するのとは全く違った考え方で、表示したいオブジェクトをビジュアルツリーに登録するだけで、後はSilverlightが勝手に描画してくれます。一度登録したら明示的に削除しなければ、ずっと表示されたままになります。昔のハードウェアスプライトを知っている人には、馴染み深いと思います。
まず準備として、Page.xamlにCanvasを設置します。Silverlight2にはレイアウトコントロールとして、Canvas, Grid, StackPanelがありますが、GridやStackPanelは適当に子要素を登録すれば勝手に座標をいい感じに調節して表示してくれるのに対して、Canvasは自分で細かく座標を指定して使います。スプライト表示にはCanvasを使います。ウィザードが自動生成した初期状態のPage.xamlには空っぽのGridコントロールが配置されていますが、これは必要ないのでそのままCanvasに置き換えてしまいます。(Gridの子要素としてCanvasを設置してもいい)
<UserControl x:Class="SpriteTest.Page" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="400" Height="300"> <Canvas x:Name="LayoutRoot" Background="White"> </Canvas> </UserControl>
スプライトのパターンデータは、プログラム上でハードコーディングすることも可能ですが、通常はXAMLファイルとして用意します。XAMLは主にベクトル画像を表現するファイルですが、ImageオブジェクトとしてPNGを貼り付ければ、ラスター画像も簡単に扱えます。
XAMLファイルをSilverlightアプリケーションに組み込むには、SilverlightユーザーコントロールとしてC#のクラスにしてしまう方法と、リソースに登録する方法があります。
ユーザーコントロールにするには、[プロジェクト]-[新しい項目の追加]-[Silverlightユーザーコントロール]からプロジェクトに追加します。ここでMario.xamlを追加すると、Marioというクラスが自動的に定義されて、コード上でMario型のオブジェクトを扱うことができます。新しいMario型のインスタンスの作成も普通に Mario mario = new Mario() で作ることができます。XAML中に書かれている子要素もクラスのメンバとして定義されるので、簡単にアクセスできます。
リソースとして登録する場合は、外部のエディタ等でXAMLを予め用意しておいて、[プロジェクト]-[既存項目の追加]で加えます。また、加えたXAMLファイルのプロパティを開き、ビルドアクションを「埋め込まれたリソース」に変更します。
リソースの場合はC#のクラスが定義されないので、インスタンスの作成がちょっと面倒で、XamlReader.Load()にXAMLを読み込ませて作成してもらうことになります。
Assembly asm = this.GetType().Assembly;
Stream s = asm.GetManifestResourceStream("SpriteTest.mario.xaml");
StreamReader reader = new StreamReader(s);
UIElement mario = XamlReader.Load(reader.ReadToEnd()) as UIElement;
スプライトのインスタンスが作成できたら、CanvasのChildrenコレクションに追加します。Page.xamlを上記のように書いた場合、CanvasにLayoutRootという名前が付いているので、そのままLayoutRootでアクセスできます。
LayoutRoot.Children.Add(mario);
表示順(Zオーダー)はChildrenコレクション内の順番に依存していて、後から追加したものほど手前に表示されます。Canvas.ZIndex添付プロパティで制御でき、ZIndexが大きいほど手前に表示されます。ZIndexが等しい場合は、Childrenコレクション内の順番に依存し、後から追加したものほど手前に表示されます。(2008/11/05訂正)
スプライトを表示する座標はCanvasの添付プロパティを使って設定します。
Canvas.SetLeft(mario, 100); // mario.SetValue(Canvas.LeftProperty, 100)でも可
Canvas.SetTop(mario, 80); // mario.SetValue(Canvas.TopProperty, 80)でも可
普通のオブジェクト指向的な発想では mario.X = 100; mario.Y = 80; としたいところですが、Silverlight(WPF)では添付プロパティという方法を使います。添付プロパティのメリットはまだちゃんと理解していませんが、Canvasの子要素になったときにしか使わないLeft, Topを子に持たせるのは無駄だから、親が管理するといった感じでしょうか。(Gridの子要素になったときは、Gridの行と列を表すRow, Columnが添付プロパティになります)
スプライトを消去するときは、Childrenコレクションから削除します。
LayoutRoot.Children.Remove(mario);
同じスプライトを複数同時に表示する場合は、表示したい数だけMarioのインスタンスを作成します。ただし、既にあるオブジェクトからクローンを作成する機能がないようなので、必要な数だけXAMLを読み込む必要があります。毎回XAMLの解析が入って無駄な感じがしますが、我慢するしか無さそうです。
なお、表示する数が決まっているもの(自機、スコア、ゲームオーバーの文字…etc)は、Page.xamlに最初から登録しておいて、VisibilityプロパティやOpacityプロパティで表示のON/OFFを切り替えるのが一般的なようです。

