Microsoft Visual Web Developer 2008 Express Edition Silverlight 3 |
■Silverlightでもうちょっとマシなゲーム作成 | Prev Top Next |
下記の3点を修正します。
1.キャラクターを画像に変更。
2.弾を発射するときと、キャラクタに弾がヒットしたときに効果音を鳴らす。
3.爆発エフェクトをアルファブレンドを使用して実装。
---Page2.xaml---
<navigation:Page x:Class="SilverlightTutrial.Page2" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation" d:DesignWidth="640" d:DesignHeight="480" Title="Page2 Page"> <navigation:Frame x:Name="frmPage2" JournalOwnership="OwnsJournal"> <!-- タイマーイベントを開始 --> <Canvas x:Name="canControl" Loaded="StartTimer" xmlns="http://schemas.microsoft.com/client/2007" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" > <!-- 自機 --> <!-- コントロールを変更 --> <!--<Rectangle x:Name="playerControl" Width="30" Height="30" Stroke="#333366" StrokeThickness="2" Fill="#f0f0f0" />--> <Image Source="Player.png" Stretch="None" x:Name="playerControl" Width="30" Height="30" /> <!-- 爆発エフェクトようのコントロールを追加 --> <Image Source="Ex.png" Stretch="None" x:Name="exControl" Width="60" Height="60" /> <!-- 弾を発射する効果音用のコントロールを追加 --> <MediaElement x:Name="ShootSE" AutoPlay="False" Source="ShootSE.mp3" /> <!-- 敵の爆発音用のコントロールを追加 --> <MediaElement x:Name="ExSE" AutoPlay="False" Source="ExSE.mp3" /> <!-- キーボード入力を有効にするためのダミーコントロール --> <Button x:Name="button" FontSize="30" Content="START" Width="0" Height="0" Canvas.Left="220" Canvas.Top="220" KeyDown="move_KeyDown" KeyUp="move_KeyUp" /> <Button x:Name="nextButton" FontSize="30" Content="START" Width="200" Height="60" Canvas.Left="220" Canvas.Top="280" Click="start_Click" /> <Button x:Name="endButton" FontSize="30" Content="EXIT" Width="200" Height="60" Canvas.Left="220" Canvas.Top="350" Click="end_Click"/> <!-- メッセージ用コントロール --> <TextBlock x:Name="Message" /> <!-- デバック用コントロール --> <TextBlock x:Name="Debug" /> </Canvas> </navigation:Frame> </navigation:Page>今回は前回のソースを修正します。修正部分はフォントカラーを青くしてます。
ゲームで使用している画像を一応のせときます。しょーもない画像だけど。
自機
敵
エフェクト
次に効果音です。これには <MediaElement>コントロール を使用します。ファイルパスの指定方法は画像と同じで相対パスになります。 AutoPlay属性 はページが表示されたときに自動的にメディアを再生するかの設定を行います。 ここでは自動再生しないようにします。
---Page2.xaml.cs---
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using System.Windows.Navigation; using System.Windows.Browser; using System.Windows.Media.Imaging; namespace SilverlightTutrial { public partial class Page2 : Page { // 処理モード int Mode; int enemyMoveMode; int enemyMoveCnt; // 自機の速度 double PlayerSpeed; // キー入力判定 bool RightKey; bool LeftKey; // 敵 // Rectangle[] enemyArray = null; Image[] enemyArray = null; // 自機弾 Ellipse pShoot = new Ellipse(); // 敵弾(10発まで同時表示) Ellipse[] eShootArray = new Ellipse[10]; // コンストラクタ public Page2() { InitializeComponent(); Initialize(); this.canControl.Children.Add(pShoot); for (int i = 0; i < eShootArray.Length; i++) this.canControl.Children.Add(eShootArray[i]); for( int i=0; i<enemyArray.Length; i++ ) this.canControl.Children.Add(enemyArray[i]); } private void Initialize() { Mode = 0; enemyMoveMode = 0; enemyMoveCnt = 0; PlayerSpeed = 0.0; RightKey = false; LeftKey = false; Point p; // メッセージ非表示 Message.Visibility = Visibility.Collapsed; Canvas.SetZIndex(Message, 999); // 大きいほど前面に表示される可能性が高くなる(微妙な言い回しだがMSDNにそう書いてある) nextButton.Visibility = Visibility.Collapsed; Canvas.SetZIndex(nextButton, 999); endButton.Visibility = Visibility.Collapsed; Canvas.SetZIndex(endButton, 999); // 弾を初期化する shootInitialize(); // 敵を初期化する enemyArrayInitialize(); // App1.Current.Host.Content.ActualWidth -> Silverlightコントロールの横幅 p = new Point(App1.Current.Host.Content.ActualWidth * 0.5 - playerControl.Width / 2, App1.Current.Host.Content.ActualHeight - playerControl.Height - 10); // 自機の座標を初期位置に設定する Canvas.SetLeft(playerControl, p.X); Canvas.SetTop(playerControl, p.Y); //エフェクトを非表示 exControl.Visibility = Visibility.Collapsed; Canvas.SetZIndex(exControl, 996); exControl.Opacity = 0.0; // アルファ値の初期値0 // サウンド停止 ExSE.Stop(); ShootSE.Stop(); } // ユーザーがこのページに移動したときに実行されます。 protected override void OnNavigatedTo(NavigationEventArgs e) { } private void shootInitialize() { pShoot.Visibility = Visibility.Collapsed; pShoot.Width = 10.0; pShoot.Height = 10.0; pShoot.Stroke = new SolidColorBrush(Color.FromArgb(0xff, 0x0f, 0x0f, 0x0f)); pShoot.StrokeThickness = 2; pShoot.Fill = new SolidColorBrush(Color.FromArgb(0xff, 0xff, 0x0f, 0x0f)); Canvas.SetZIndex(pShoot, 997); for (int i = 0; i < eShootArray.Length; i++) { if( eShootArray[i] == null ) eShootArray[i] = new Ellipse(); eShootArray[i].Visibility = Visibility.Collapsed; eShootArray[i].Width = 10.0; eShootArray[i].Height = 10.0; eShootArray[i].Stroke = new SolidColorBrush(Color.FromArgb(0xff, 0x0f, 0x0f, 0x0f)); eShootArray[i].StrokeThickness = 2; eShootArray[i].Fill = new SolidColorBrush(Color.FromArgb(0xff, 0xff, 0xff, 0xf0)); Canvas.SetZIndex(eShootArray[i], 998); } } private void enemyArrayInitialize() { const double offsetw = 80.0; const double offseth = 50.0; const double sleft = 20; const double stop = 30; int cnt = 0; double left = sleft; double top = stop; // 敵を適当に作成する if( enemyArray == null ) { // enemyArray = new Rectangle[21]; enemyArray = new Image[21]; } for (int i = 0; i < enemyArray.Length; i++) { // if( enemyArray[i] == null ) // enemyArray[i] = new Rectangle(); if( enemyArray[i] == null ) enemyArray[i] = new Image(); enemyArray[i].Visibility = Visibility.Visible; enemyArray[i].Width = 30; enemyArray[i].Height = 30; // enemyArray[i].Stroke = new SolidColorBrush(Color.FromArgb(0xff, 0x0f, 0x0f, 0x0f)); // enemyArray[i].StrokeThickness = 4; // enemyArray[i].Fill = new SolidColorBrush(Color.FromArgb(0xff, 0x0f, 0x0f, 0xf0)); enemyArray[i].Source = new BitmapImage(new Uri("enemy.png", UriKind.Relative)); Canvas.SetLeft(enemyArray[i], left); Canvas.SetTop(enemyArray[i], top); if (cnt < 6) { left += offsetw; cnt++; } else { left = sleft; top += offseth; cnt = 0; } } } // タイマーの実装 public void StartTimer(object o, RoutedEventArgs sender) { System.Windows.Threading.DispatcherTimer myDispatcherTimer = new System.Windows.Threading.DispatcherTimer(); // myDispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 33); // 33 ミリ秒ごとにイベントを発生させるようにする myDispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 1000 / 60); // 1000 / 60 ミリ秒ごとにイベントを発生させるようにする myDispatcherTimer.Tick += new EventHandler(Each_Tick); // タイマーのイベントハンドラーを設定 myDispatcherTimer.Start(); // タイマー開始 } // タイマーイベント処理 public void Each_Tick(object o, EventArgs sender) { // キーボード入力を有効にするためになにがなんでもフォーカスを当てる。 // ただし常にフォーカスを当てると、ボタンが表示されている画面でマウスクリックが反応しなくなるので注意 if( Mode == 0 ) button.Focus(); switch (Mode) { case 0: // 戦い中 playerUpdate(); // 自機の更新 enemyUpdate(); // 敵の更新 shootUpdate(); // 弾の更新 CollisionAll(); // 当たり判定 ExUpdate(); // エフェクト更新 break; case 1: // ゲームオーバー break; case 2: // クリア break; } } // 自機の移動 private void playerUpdate() { // キーボードの入力状態をチェックし、速度を計算する if (RightKey) PlayerSpeed += 2.0; if (LeftKey) PlayerSpeed -= 2.0; // 自機の位置を計算 double left = Canvas.GetLeft(playerControl) + PlayerSpeed; // 自機がスクリーンからはみ出たら戻す if (left < 0.0) left = 0.0; if (left > App1.Current.Host.Content.ActualWidth - playerControl.Width) left = App1.Current.Host.Content.ActualWidth - playerControl.Width; // 座標を設定する Canvas.SetLeft(playerControl, left); // 減速 PlayerSpeed *= 0.9; } // 敵の移動 private void enemyUpdate() { switch (enemyMoveMode) { // 右に移動 case 0: enemyMoveCnt++; if (enemyMoveCnt > 80) { enemyMoveCnt = 0; enemyMoveMode++; } MoveEnemy(1.0, 0.0); break; // 下に移動 case 1: enemyMoveCnt++; if (enemyMoveCnt > 30) { enemyMoveCnt = 0; enemyMoveMode++; } MoveEnemy(0.0, 1.0); break; // 左に移動 case 2: enemyMoveCnt++; if (enemyMoveCnt > 80) { enemyMoveCnt = 0; enemyMoveMode++; } MoveEnemy(-1.0, 0.0); break; // 下に移動 case 3: enemyMoveCnt++; if (enemyMoveCnt > 30) { enemyMoveCnt = 0; enemyMoveMode = 0; } MoveEnemy(0.0, 1.0); break; } } private void MoveEnemy(double addX, double addY) { double x; double y; Random rnd = new Random(); int j = 0; for (int i = 0; i < enemyArray.Length; i++) { if (enemyArray[i].Visibility == Visibility.Visible) { x = Canvas.GetLeft(enemyArray[i]); y = Canvas.GetTop(enemyArray[i]); Canvas.SetLeft(enemyArray[i], x + addX ); Canvas.SetTop(enemyArray[i], y + addY); if (rnd.Next(500) == 0) { // 弾発射 while( j < eShootArray.Length ) { if (eShootArray[j].Visibility == Visibility.Collapsed) { Canvas.SetLeft(eShootArray[j], Canvas.GetLeft(enemyArray[i]) + enemyArray[i].Width / 2 - eShootArray[j].Width / 2); Canvas.SetTop(eShootArray[j], Canvas.GetTop(enemyArray[i]) + enemyArray[i].Height / 2 - eShootArray[j].Height / 2); eShootArray[j].Visibility = Visibility.Visible; // 効果音再生 ShootSE.Position = TimeSpan.FromSeconds(0); ShootSE.Play(); break; } j++; } } } } } // 弾の更新 private void shootUpdate() { double left; double top; // 自機の弾の更新 if (pShoot.Visibility == Visibility.Visible) { left = Canvas.GetLeft(pShoot); top = Canvas.GetTop(pShoot); top -= 10.0; if (top > 0) { Canvas.SetLeft(pShoot, left); Canvas.SetTop(pShoot, top); } else pShoot.Visibility = Visibility.Collapsed; } // 敵の弾の更新 for (int i = 0; i < eShootArray.Length; i++) { if (eShootArray[i].Visibility == Visibility.Visible) { top = Canvas.GetTop(eShootArray[i]); if (top <= App1.Current.Host.Content.ActualHeight) { Canvas.SetTop( eShootArray[i], top += 5.0 ); } else eShootArray[i].Visibility = Visibility.Collapsed; } } } // エフェクトの更新 private void ExUpdate() { if (exControl.Visibility == Visibility.Visible) { exControl.Opacity -= 0.05; if (exControl.Opacity <= 0.0f) exControl.Visibility = Visibility.Collapsed; } } // キーボード入力したときに発生するイベント private void move_KeyDown(object sender, KeyEventArgs e) { switch (e.Key) { // 右に移動 case Key.Right: RightKey = true; break; // 左に移動 case Key.Left: LeftKey = true; break; // 攻撃 case Key.Up: // 弾を非表示のときのみショットを撃てる if (Mode == 0 && pShoot.Visibility == Visibility.Collapsed) { // 弾の座標を自機の座標から計算し、設定する Canvas.SetLeft(pShoot, Canvas.GetLeft(playerControl) + playerControl.Width / 2); Canvas.SetTop(pShoot, Canvas.GetTop(playerControl) - pShoot.Height); pShoot.Visibility = Visibility.Visible; // 効果音再生 ShootSE.Position = TimeSpan.FromSeconds(0); ShootSE.Play(); } break; } } // キーボード入力をやめたときに発生するイベント private void move_KeyUp(object sender, KeyEventArgs e) { switch (e.Key) { case Key.Right: RightKey = false; break; case Key.Left: LeftKey = false; break; } } private void CollisionAll() { // 自機の弾と敵との当たり判定 double shootL = Canvas.GetLeft(pShoot); double shootR = Canvas.GetLeft(pShoot) + pShoot.Width; double shootT = Canvas.GetTop(pShoot); double shootB = Canvas.GetTop(pShoot) + pShoot.Height; double bodyL; double bodyR; double bodyT; double bodyB; for (int i = 0; i < enemyArray.Length; i++) { if (enemyArray[i].Visibility == Visibility.Visible) { bodyL = Canvas.GetLeft(enemyArray[i]); bodyR = Canvas.GetLeft(enemyArray[i]) + enemyArray[i].Width; bodyT = Canvas.GetTop(enemyArray[i]); bodyB = Canvas.GetTop(enemyArray[i]) + enemyArray[i].Height; // 当たり判定チェック if (!(shootR < bodyL || bodyR < shootL || shootB < bodyT || bodyB < shootT)) { // ヒットしたので非表示にする。 enemyArray[i].Visibility = Visibility.Collapsed; pShoot.Visibility = Visibility.Collapsed; // 効果音再生 ExSE.Position = TimeSpan.FromSeconds(0); ExSE.Play(); // エフェクトを表示 exControl.Visibility = Visibility.Visible; exControl.Opacity = 1.0; Canvas.SetTop(exControl, Canvas.GetTop(enemyArray[i]) + enemyArray[i].Height / 2 - exControl.Height / 2); Canvas.SetLeft(exControl, Canvas.GetLeft(enemyArray[i]) + enemyArray[i].Width / 2 - exControl.Width / 2); break; } } } bool flg = false; for (int i = 0; i < enemyArray.Length; i++) { if (enemyArray[i].Visibility == Visibility.Visible) { flg = true; break; } } if (flg == false) { // クリア ChangeMode(2); return; } // 自機と敵の弾との当たり判定 shootL = Canvas.GetLeft(playerControl); shootR = Canvas.GetLeft(playerControl) + playerControl.Width; shootT = Canvas.GetTop(playerControl); shootB = Canvas.GetTop(playerControl) + playerControl.Height; // 敵の弾の座標 for (int i = 0; i < eShootArray.Length; i++) { if (eShootArray[i].Visibility == Visibility.Visible) { bodyL = Canvas.GetLeft(eShootArray[i]); bodyR = Canvas.GetLeft(eShootArray[i]) + eShootArray[i].Width; bodyT = Canvas.GetTop(eShootArray[i]); bodyB = Canvas.GetTop(eShootArray[i]) + eShootArray[i].Height; // 当たり判定チェック if (!(shootR < bodyL || bodyR < shootL || shootB < bodyT || bodyB < shootT)) { // ヒットしたのでゲームオーバー ChangeMode(1); // 効果音再生 ExSE.Position = TimeSpan.FromSeconds(0); ExSE.Play(); return; } } } // 自機と敵との当たり判定 for (int i = 0; i < enemyArray.Length; i++) { if (enemyArray[i].Visibility == Visibility.Visible) { bodyL = Canvas.GetLeft(enemyArray[i]); bodyR = Canvas.GetLeft(enemyArray[i]) + enemyArray[i].Width; bodyT = Canvas.GetTop(enemyArray[i]); bodyB = Canvas.GetTop(enemyArray[i]) + enemyArray[i].Height; // 当たり判定チェック if (!(shootR < bodyL || bodyR < shootL || shootB < bodyT || bodyB < shootT)) { // ヒットしたのでゲームオーバー ChangeMode(1); return; } } } } private void ChangeMode(int pMode) { Mode = pMode; switch (Mode) { // ゲームオーバー case 1: Message.Text = "ゲームオーバー"; Message.FontSize = 40; Message.FontWeight = FontWeights.Bold; Message.Width = App1.Current.Host.Content.ActualWidth; Message.Height = 40; Canvas.SetLeft(Message, 0); Canvas.SetTop(Message, ( App1.Current.Host.Content.ActualHeight - Message.Height ) / 2 - 100); Message.TextAlignment = TextAlignment.Center; // 中央寄せ Message.Foreground = new SolidColorBrush(Color.FromArgb(0xff, 0xff, 0, 0)); Message.Visibility = Visibility.Visible; // ボタン表示 nextButton.Visibility = Visibility.Visible; endButton.Visibility = Visibility.Visible; break; // クリア case 2: Message.Text = "クリア!!"; Message.FontSize = 40; Message.FontWeight = FontWeights.Bold; Message.Width = App1.Current.Host.Content.ActualWidth; Message.Height = 40; Canvas.SetLeft(Message, 0); Canvas.SetTop(Message, (App1.Current.Host.Content.ActualHeight - Message.Height) / 2 - 100); Message.TextAlignment = TextAlignment.Center; // 中央寄せ Message.Foreground = new SolidColorBrush(Color.FromArgb(0xff, 0x00, 0, 0xff)); Message.Visibility = Visibility.Visible; // ボタン表示 nextButton.Visibility = Visibility.Visible; endButton.Visibility = Visibility.Visible; break; } } // つまらないのでやめる private void end_Click(object sender, RoutedEventArgs e) { // javascript によりウィンドウを閉じる HtmlPage.Window.Eval("var w=window.open('','_top'); w.close()"); } // 仕方がないので再挑戦 private void start_Click(object sender, RoutedEventArgs e) { Initialize(); } } }自機の弾が敵にヒットしたとき爆発エフェクトが発生します。このエフェクトは徐々に薄くなるアニメーションを行います。 アルファ値は UIElement::Opacityメンバ で設定します。[ 0.0 〜 1.0 ] の範囲が有効値となります。0.0のとき透明になります。 MSDN
次に効果音。これは解説しなくてもいいのでしょう(笑)。解説ページだけのせときます。MSDN