「3Dっぽい縦シューティングゲームをつくる」は前回で終わりにしたのですが、プログラミング講座 第34回【シューティングゲーム作成(最終回)/JavaScript】で宿題を出されていたのでできそうなもので面白そうなものをやっておきます。
BGMや効果音がないのはさみしいので付け加えます。C#で音を鳴らすにはどうすればいいかはMIDI、MP3などの音楽ファイルを再生する – .NET Tips (VB.NET,C#…)が参考になります。このなかの「Windows Media Playerをフォームに配置せずに再生する」方法を採用します。
MCIを使用する方法だと停止しないと再生できません。しかしWindows Media Player Controlを使用する方法であればWindows Media Player Controlを複数生成することで同時に音を鳴らすことができます。BGMと効果音を同時に鳴らすことができるのです。
BGMは以下を使います。
戦闘曲30 無料ダウンロード/魔王魂 フリーのゲーム音楽素材
戦闘曲33 無料ダウンロード/魔王魂 フリーのゲーム音楽素材
途中でBGMが終了して途切れてしまわないようにBGMが終了する時刻にボス戦の前なのか最中なのかを調べて適切なBGMを再生します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
public partial class Form1 : Form { // BGMが終了する時刻 DateTime bgm1EndTime = DateTime.Now; DateTime bgm2EndTime = DateTime.Now; WMPLib.WindowsMediaPlayer mediaPlayerBGM = new WMPLib.WindowsMediaPlayer(); void PlayBGM1() { // ひとつめのBGMの再生時間は33秒である。BGMが終了する時刻を記憶する。 bgm1EndTime = DateTime.Now + new TimeSpan(0, 0, 33); string path = Application.StartupPath + "\\battle.mp3"; if(System.IO.File.Exists(path)) { mediaPlayerBGM.settings.autoStart = true; mediaPlayerBGM.URL = path; } } void PlayBGM2() { // ふたつめのBGMの再生時間は58秒である。BGMが終了する時刻を記憶する。 bgm2EndTime = DateTime.Now + new TimeSpan(0, 0, 58); string path = Application.StartupPath + "\\boss.mp3"; if(System.IO.File.Exists(path)) { mediaPlayerBGM.settings.autoStart = true; mediaPlayerBGM.URL = path; } } void InitGame() { Enemies.Clear(); EnemyBurrets.Clear(); JikiBurrets.Clear(); Explosions.Clear(); IsBattleAntiBoss = false; BaseY = 0; Jiki = new Jiki(0.5f, Speed); Score = 0; PlayBGM1(); } // 対ボス戦がはじまったらBGMを変える void BeginBattleAntiBoss() { IsBattleAntiBoss = true; Enemies.Add(new Boss(0, 10 + BaseY, 3, 500, 1000)); PlayBGM2(); } // 対ボス戦に勝利したらBGMを元に戻す void AfterBossDead() { IsBattleAntiBoss = false; BaseY = 0; Jiki.Y = 0; PlayBGM1(); } // BGMを止める private void StopBGM() { mediaPlayerBGM.controls.stop(); } private void Timer_Tick(object sender, EventArgs e) { Update(); // ボス戦前であるにもかかわらずボス戦前のBGMが終了した場合、 // ボス戦の最中であるにもかかわらずボス戦時のBGMが終了した場合に // 同じBGMを再生しなおす。 if(Jiki != null && !Jiki.IsDead) { if(!IsBattleAntiBoss && bgm1EndTime < DateTime.Now) PlayBGM1(); if(IsBattleAntiBoss && bgm2EndTime < DateTime.Now) PlayBGM2(); } glControl.Refresh(); } } |
また効果音も入れます。効果音はここからいただきました。
まずは敵弾を食らったときの音です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
public partial class Form1 : Form { WMPLib.WindowsMediaPlayer mediaPlayerDamage = new WMPLib.WindowsMediaPlayer(); void PlaySeDamage() { string path = Application.StartupPath + "\\damage.mp3"; if(System.IO.File.Exists(path)) { mediaPlayerDamage.settings.autoStart = true; mediaPlayerDamage.URL = path; } } void OnJikiDamage(Character enemyChar) { Jiki.Life--; enemyChar.IsDead = true; Explosions.Add(new Explosion(Jiki.X, Jiki.Y, 0, 0, 0.5f)); PlaySeDamage(); Jiki.IsBackColorRed = true; if(Jiki.IsDead) { BigExplosion(Jiki.X, Jiki.Y); // ゲームオーバーなのでBGMを止める StopBGM(); } else Jiki.IsMuteki = true; } } |
次に自機が弾丸を発射したときや敵に命中したときの音を鳴らそうとするとちょっとした問題が発生。
短い間隔で連続して音を鳴らそうとすると実際に音がなりはじめる前に次の音を鳴らそうとして前の音がキャンセルされてしまうのか、音が出なくなってしまうのです。
そこで3つのWMPLib.WindowsMediaPlayerオブジェクトを作成して交互に使うことにします。それから自機が弾丸を発射したときと敵に命中したときの両方で音をならそうとしてもうまく聞こえないので、命中時のみ音を鳴らすことにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
public partial class Form1 : Form { int playSeHitCount = 0; WMPLib.WindowsMediaPlayer mediaPlayerHit1 = new WMPLib.WindowsMediaPlayer(); WMPLib.WindowsMediaPlayer mediaPlayerHit2 = new WMPLib.WindowsMediaPlayer(); WMPLib.WindowsMediaPlayer mediaPlayerHit3 = new WMPLib.WindowsMediaPlayer(); void PlaySeHit() { string path = Application.StartupPath + "\\hit.mp3"; if(System.IO.File.Exists(path)) { playSeHitCount++; WMPLib.WindowsMediaPlayer mediaPlayerHit = mediaPlayerHit1; if(playSeHitCount % 2 == 1) mediaPlayerHit = mediaPlayerHit2; if(playSeHitCount % 2 == 2) mediaPlayerHit = mediaPlayerHit3; mediaPlayerHit.settings.autoStart = true; mediaPlayerHit.URL = path; } } void OnBurretHitEnemy(EnemyBase enemy, JikiBurret burret) { enemy.Life--; burret.IsDead = true; if(!enemy.IsDead) Explosions.Add(new Explosion(burret.X, burret.Y, 0, 0, 0.5f)); else { BigExplosion(enemy.X, enemy.Y); // 音は敵が死んだときだけならす。 PlaySeHit(); } } } |
ボスを倒したときは長めの爆発音にします。このときザコ敵がいるときはいっしょに爆発させていますが、単にenemy.IsDead = trueでは点数が加算されないので、Lifeプロパティを0にしています。これだと点数が加算されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
public partial class Form1 : Form { // ボスを倒したときは長めの爆発音 WMPLib.WindowsMediaPlayer mediaPlayerBossDead = new WMPLib.WindowsMediaPlayer(); void PlaySeBossDead() { string path = Application.StartupPath + "\\big-explosion.mp3"; if(System.IO.File.Exists(path)) { mediaPlayerBossDead.settings.autoStart = true; mediaPlayerBossDead.URL = path; } } void OnBossDead(Boss boss) { // ボスを倒した場合はボスを大爆発させる BigExplosionWhenBossDead(boss.X, boss.Y); // 対ボス戦時のBGMを停止して長めの爆発音を入れる StopBGM(); PlaySeBossDead(); // ザコ敵もいっしょに爆発 ExplosionAllEnemies(); // 飛ぶまえのボスの弾丸も消す ClearBossBurrets(); // 少し時間をおいてから // IsBattleAntiBoss = false; // BaseY = 0; // Jiki.Y = 0; を実行する Timer timer = new Timer(); timer.Interval = 10000; // 爆発音を長めにしたので10秒間待ってから次の処理をする timer.Tick += Timer_Tick1; timer.Start(); void Timer_Tick1(object sender, EventArgs e) { Timer t = (Timer)sender; t.Stop(); t.Dispose(); AfterBossDead(); } } void ExplosionAllEnemies() { foreach(EnemyBase enemy in Enemies) { if(!enemy.IsDead) { //enemy.IsDead = true; // これでは点数が加算されない enemy.Life = 0; BigExplosion(enemy.X, enemy.Y); } } } void ClearBossBurrets() { foreach(EnemyBurret enemyBurret in EnemyBurrets) { if(enemyBurret is BossEnemyBurret) { BossEnemyBurret burret = (BossEnemyBurret)enemyBurret; if(!burret.IsMove) burret.IsDead = true; } } } } |