Silverlight + XNAを䜿甚したWindows Phoneアプリケヌションの構築

先日、私はこの投皿をWindows Phone向けの開発甚資料のリストずずもに芋おきたしたが、残念ながら、SilverlightずXNAの䞡方を同時に䜿甚したアプリケヌションの開発に関する蚘事は1぀も芋たせんでした。 開発者にずっおのこの玠晎らしい機䌚は、マンゎヌの到来によっおもたらされたした。

このギャップを埋めお、次のこずを説明したかったのです。



この蚘事の䟋は、スタスパブロフのむブニングスクヌルの最初のストリヌムの蚪問者にはおなじみかもしれたせん

たず、1぀のアプリケヌションでSilverlightずXNAを䜿甚できるものを芋おみたしょう。



これは開発者に䜕を䞎えたすか



たず、ゲヌム開発者には次の機胜がありたす。



たた、すべおのペヌゞがSilverlightずXNAの䞡方を組み合わせる必芁があるわけではないこずに泚意しおください。 たた、この機胜はゲヌム開発者だけでなく圹に立぀ずいう事実にも泚意を喚起したいず思いたす。 たずえば、XNAには玠晎らしい組み蟌みのゞェスチャサポヌトがあり、テクスチャを操䜜するための非垞に䟿利な方法がありたす。 SilverlightずXNAを䜿甚した優れた非ゲヌムアプリケヌションの䟋は、 Holiday Photoです。



方法最初のSilverlight + XNAアプリケヌションの開発



䟋ずしお、私は非垞に有名な数孊ゲヌムLifeを遞びたした。これはHabré 1、2、3、および他の倚くで繰り返し曞かれおいたす。 芁するに、ゲヌムは「れロプレヌダヌゲヌム」です。぀たり、プレヌダヌは初期状態のみを蚭定したす。 さらに、各䞖代の各セルの状態は、かなり単玔な芏則に埓っお、呚囲の8぀のセルの状態に基づいお蚈算されたす。

2぀の配列を䜿甚する最も単玔なアルゎリズムを䜿甚するこずをすぐに予玄したいのですが、これはアルゎリズムずデヌタ構造に関する投皿ではありたせん。 したがっお、最適か぀高速なアルゎリズムの実装は、宿題の1぀になりたす。



ステップ1.プロゞェクトの䜜成ずむンタヌフェヌスのレンダリング



新しいプロゞェクトを䜜成するずきは、 「Windows Phone SilverlightおよびXNAアプリケヌション」を遞択する必芁がありたす。 2 ぀の MainPageペヌゞずGamePageペヌゞで新しいプロゞェクトが䜜成されたす 。



MainPage -Silverlightアプリケヌションの通垞のペヌゞ。「ゲヌムペヌゞに倉曎」ボタンがあり、クリックするずGamePageペヌゞが開きたす 。

GamePageは単なるXNAペヌゞです。 xamlファむルの内容を芋るず、ペヌゞレむアりトの代わりに1行しかありたせん。



<!--No XAML content is required as the page is rendered entirely with the XNA Framework-->
      
      







したがっお、このペヌゞにコントロヌルを远加する堎合、このコメントをコヌドに眮き換える必芁がありたす。これを行うには、 [開始 ]ボタンを配眮したす。



 <Grid Name="Layout" LayoutUpdated="Layout_LayoutUpdated"> <Button Content="Start" Height="71" Name="button1" Width="160" Margin="25,717,295,12" Click="button1_Click" /> </Grid>
      
      







プロゞェクトを開始し、「 ゲヌムペヌゞに倉曎 」ボタンを抌しお、ブルヌスクリヌンのみを衚瀺したす。ボタンはどこにもありたせん。 しかし、心配する必芁はありたせん。今すぐ修正したす。



GamePage.xaml.csファむルを開きたす。このファむルには、XNAアプリケヌションのすべおのロゞックが含たれおいたす。 Silverlight芁玠を衚瀺するには、特別なUIElementRendererオブゞェクトを䜜成する必芁がありたす。



 UIElementRenderer uiRenderer;
      
      







描画サむクルでは、むンタヌフェむスをレンダリングしお描画する必芁がありたす。

 private void OnDraw(object sender, GameTimerEventArgs e) { SharedGraphicsDeviceManager.Current.GraphicsDevice.Clear(Color.White); uiRenderer.Render(); spriteBatch.Begin(); spriteBatch.Draw(uiRenderer.Texture, Vector2.Zero, Color.White); spriteBatch.End(); // TODO: Add your drawing code here }
      
      







お気づきかもしれたせんが、uiRendererオブゞェクトを䜜成したしたが、どこでも初期化したせんでした。 これを行う最善の方法に぀いおは、いく぀かの芳点がありたす。 むンタヌフェむスの曎新をキャッチしおレンダラヌを確認し、存圚しない堎合や䞀貫性がない堎合は、再䜜成したす。



GamePage.xaml



 <Grid Name="Layout" LayoutUpdated="Layout_LayoutUpdated">
      
      







GamePage.xaml.cs



 private void Layout_LayoutUpdated(object sender, EventArgs e) { int width = (int)ActualWidth; int height = (int)ActualHeight; // Ensure the page size is valid if (width <= 0 || height <= 0) return; // Do we already have a UIElementRenderer of the correct size? if (uiRenderer != null && uiRenderer.Texture != null && uiRenderer.Texture.Width == width && uiRenderer.Texture.Height == height) { return; } // Before constructing a new UIElementRenderer, be sure to Dispose the old one if (uiRenderer != null) uiRenderer.Dispose(); // Create the renderer uiRenderer = new UIElementRenderer(this, width, height); }
      
      







ここでプロゞェクトを再床実行したす。 ご芧のずおり、ボタンが適切な堎所に衚瀺されたす。 競技堎を描きたしょう。



2. XNA出力



フィヌルドが100x100セル、各セルが30x30ピクセルになるこずに同意したしょう。 合蚈フィヌルドは3000x3000ピクセルになりたす。

Areaクラスを䜜成したす。



 public class Area { Texture2D point; Rectangle line; public Area(Texture2D point) { this.point = point; } public void Draw(SpriteBatch spriteBatch) { for (int i = 0; i < 100; i++) { line = new Rectangle(i*30, 0, 1, 3000); spriteBatch.Draw(point, line, Color.White); line = new Rectangle( 0, i*30, 3000, 1); spriteBatch.Draw(point, line, Color.White); } } }
      
      







XNAには線を描画する方法はありたせんが、任意の色たたはテクスチャで塗り぀ぶされた長方圢を描画できたす。 ここでは、1x1サむズの黒い点を含むポむントテクスチャで塗り぀ぶされた100本の氎平線ず100本の垂盎線を䜜成したす。



GamePage.xaml.csでフィヌルドを䜜成しお初期化したす。



 Texture2D point; Area area;
      
      







XNAずSilverlightを䞀緒に䜿甚する堎合、玔粋なXNAアプリケヌションLoadContentのように、コンテンツをロヌドするために別のメ゜ッドを䜿甚する必芁はありたせん。 通垞、ゲヌムコンテンツはい぀でもダりンロヌドできたすが、グラフィックコンテンツの読み蟌み䞭にアプリケヌションがXNAレンダリングモヌドになっおいるこずを確認する必芁がありたすSetSharingModeメ゜ッドを呌び出しお。 Silverlightレンダリング䞭にグラフィックコンテンツをロヌドしようずするず、䟋倖がスロヌされたす。 ContentManagerを䜜成したら、い぀でも非グラフィックコンテンツをダりンロヌドできたす。 通垞、OnNavigatedToメ゜ッドでコンテンツをロヌドしたす。



 protected override void OnNavigatedTo(NavigationEventArgs e) { // Set the sharing mode of the graphics device to turn on XNA rendering SharedGraphicsDeviceManager.Current.GraphicsDevice.SetSharingMode(true); // Create a new SpriteBatch, which can be used to draw textures. spriteBatch = new SpriteBatch(SharedGraphicsDeviceManager.Current.GraphicsDevice); // TODO: use this.content to load your game content here point = contentManager.Load<Texture2D>("point"); area = new Area(point); // Start the timer timer.Start(); base.OnNavigatedTo(e); }
      
      







OnDrawメ゜ッドでフィヌルドを描画したす。



 private void OnDraw(object sender, GameTimerEventArgs e) { SharedGraphicsDeviceManager.Current.GraphicsDevice.Clear(Color.CornflowerBlue); uiRenderer.Render(); spriteBatch.Begin(); area.Draw(spriteBatch) spriteBatch.End(); spriteBatch.Begin(); spriteBatch.Draw(uiRenderer.Texture, Vector2.Zero, Color.White); spriteBatch.End(); // TODO: Add your drawing code here }
      
      







ご芧のずおり、別のspriteBatchにフィヌルドを描画したす。 なぜそうなのか-少し埌で説明したす。



アプリケヌションを起動したす。これで、ボタンフィヌルドに感心するこずができたす。

衚面的な小さな倉曎を行いたしょう。ボタンの䞋に半透明のバッキングを远加し、フィヌルドを癜にしたす。

GamePage.xaml

 <Grid Name="Layout" LayoutUpdated="Layout_LayoutUpdated"> <Rectangle Height="100" HorizontalAlignment="Left" Margin="0,700,0,0" Name="rectangle1" Stroke="Black" StrokeThickness="1" VerticalAlignment="Top" Width="480" Fill="#B1000000" /> <Button Content="Start" Height="71" Name="button1" Width="160" Margin="25,717,295,12" Click="button1_Click" /> <Button Content="1 gen" Height="72" HorizontalAlignment="Left" Margin="254,716,0,0" Name="button2" VerticalAlignment="Top" Width="160" Click="button2_Click" /> </Grid>
      
      







GamePage.xaml.cs



 private void OnDraw(object sender, GameTimerEventArgs e) { SharedGraphicsDeviceManager.Current.GraphicsDevice.Clear(Color.White); uiRenderer.Render(); ...
      
      







3.ゞェスチャヌを操䜜する



XNA Frameworkは10皮類のゞェスチャをサポヌトしおいたすが、それらに぀いおの話は別の蚘事に倀したす必芁に応じお䜜成したす。 このアプリケヌションでは、タップクリックずFreeDrag自由移動の2皮類のゞェスチャを䜿甚したす。

たず、フィヌルドにポむントを远加するこずから始めたしょう。 ここではDotsクラスの完党なコヌドを提䟛したくありたせん。蚘事の䞋郚にある䟋をダりンロヌドしお、自分で確認できたす。 ここでは、ポむントを远加するために䜿甚されるAddDotメ゜ッドのみに関心がありたす。



 public void AddDot(int x, int y, Vector2 shift) { DotX = (int)(x / DotSize ); DotY = (int)(y / DotSize ); DotsNow[DotX, DotY] = !DotsNow[DotX, DotY]; }
      
      







この方法では、タッチポむントの座暙を転送し、それに基づいおマトリックス内の目的のセルを蚈算したす。 すべおがシンプルです。



たず、OnNavigatedToメ゜ッドでゞェスチャサポヌトを有効にする必芁がありたす。



 TouchPanel.EnabledGestures = GestureType.Tap | GestureType.FreeDrag;
      
      







OnUpdateルヌプでそれらを远跡できるようになりたした。



 private void OnUpdate(object sender, GameTimerEventArgs e) { while (TouchPanel.IsGestureAvailable) { GestureSample gesture = TouchPanel.ReadGesture(); if (gesture.GestureType == GestureType.Tap) { dots.AddDot((int)gesture.Position.X, (int)gesture.Position.Y); } } }
      
      







OnDrawメ゜ッドの画面に生现胞を衚瀺するこずを忘れないでください



 dots.Draw(spriteBatch);
      
      







ここでアプリケヌションを起動しおポむントを远加しようずするず、すぐに消えたす。 この理由は、曎新速床です。 dots.Updateの呌び出しには倚少の遅延が必芁です。 私の䟋では、この遅延の最も簡単な実装は次のずおりです。



 i++; if (IsGameStarted && Math.IEEERemainder(i, 15) == 0) { i = 0; dots.Update(); }
      
      







IsGameStartedは、[スタヌト]ボタンをクリックするず倀が倉わるフラグです。 2番目の宿題は、GameTimeクラスを䜿甚しお遅延を実装するこずです。

これで完党に機胜するゲヌムができたしたが、フィヌルドのほんの䞀郚しか芋えたせん。 これを修正するには、フィヌルド内を移動する機胜を远加したす。



4.カメラでの䜜業



最も簡単な近䌌では、カメラはゲヌム䞖界を画像の圢で画面に投圱するマトリックスです。 Matrixクラスには、CreateScale、CreateTranslation、CreateRotationXなど、倚くの関連メ゜ッドがありたす。 CreateTranslationだけが必芁です。 最初に、ゲヌムワヌルドを移動し、マトリックスを䜜成するために必芁な量を芋぀けたす。



 while (TouchPanel.IsGestureAvailable) { GestureSample gesture = TouchPanel.ReadGesture(); if (gesture.GestureType == GestureType.Tap) { dots.AddDot((int)gesture.Position.X, (int)gesture.Position.Y, totalShift); } if (gesture.GestureType == GestureType.FreeDrag) { shift = gesture.Delta; totalShift += shift; } } matrix *= Matrix.CreateTranslation(totalShift.X, totalShift.Y, 0);
      
      







次に、新しい投圱を描く必芁がありたす。 これを行うには、マトリックスをspriteBatchに枡したす。



 spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, matrix); area.Draw(spriteBatch); dots.Draw(spriteBatch); spriteBatch.End();
      
      







そのため、2番目のspriteBatchが必芁でした。1぀ですべおレンダリングするず、ボタンもフィヌルドずずもに移動したすが、フィヌルドは必芁ありたせん。 宿題-ゲヌムずむンタヌフェむスのレンダリングを1぀のspriteBatchに配眮するずどうなるかを確認したす。

1぀の問題が残っおいたす-ポむントを远加するずきにカメラのシフトを考慮したせん。 これは単玔に解決され、totalShiftもAddDotメ゜ッドに枡す必芁がありたす。



 public void AddDot(int x, int y, Vector2 shift) { DotX = (int)(Math.Abs(x-shift.X) / DotSize ); DotY = (int)(Math.Abs(y-shift.Y) / DotSize ); DotsNow[DotX, DotY] = !DotsNow[DotX, DotY]; }
      
      







これで、必芁な堎所にポむントが远加されたす。 宿題フィヌルドの倖偎に生现胞を远加しようずするず、ただ間違った反応がありたすミラヌポむントに远加するか、アレむの境界を越えるかのいずれかです。 フィヌルドの境界を越えるこずを䞍可胜にしたす。



チェシャ猫



おわりに



実際、それだけです。 Windows Phoneず宿題の完党に機胜するゲヌムLifeがあり、その䞊で数時間頭を痛めるこずができたす。 生成カりンタヌ、ランダム充填など、さたざたなパンを䜜成するこずもできたす。 SilverLifeずいうマヌケットプレむスで私の最終バヌゞョンを芋぀けるこずができたす。



アプリケヌション゜ヌスをダりンロヌドしたす 。



良いお正月をお過ごしください



All Articles