どうも、Namazuです。
今回は扱う内容は以下の通りです。
- 音楽の再生
前回、前々回の記事の内容を使いますのでまずはそちらをご覧ください。
音楽の再生をする方法は大きく分けて二つあります。
- Song クラスを用いる方法
- SoundEffect クラスを用いる方法
BGMなどの再生ならばSongの方が適しているかもしれませんが、今回はSoundEffectクラスの扱い方を紹介します。
Songを扱わない理由は後述します。
今回扱うクラスは以下の通りです。
- SoundEffect Class
- SoundEffectInstance Class
流れとしては、SoundEffectクラスで再生する音楽を読み込んでオブジェクトを作成します。
その後SoundEffectInstanceクラスの変数にそのオブジェクトのインスタンスを作成して代入し、扱います。
具体的には、以下のように記述します。
※本当はse,bgmはグローバル変数であるべきですが扱い方を分かっていただくためにこのように記述しています。
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
SoundEffect se = Content.Load<SoundEffect>("stage");
SoundEffectInstance bgm = se.CreateInstance();
bgm.IsLooped = true;
// TODO: use this.Content to load your game content here
}
- Content.Load
画像と同様に、音声データを読み込みます。 - se.CreateInstance
返り値の型はSoundEffectInstanceです。これによって作成した音声データのオブジェクトのインスタンスを取得します。
インスタンスを用いているのは、"SoundEffect"ならではでしょう。
同時に同じ音声を再生することも考慮されています。
ただし、一度に保持できるインスタンスの数には限りがあるので、不要になったものは削除しましょう。
音声データもContent.mgcbからビルドしなければいけないのですが、一つ注意点があります。
画像をご覧ください。
もうお馴染みの画面ですが、この左下のボックスの中のProcessorをSound Effect - MonoGameに設定してください。
Songクラスを使う場合はこれをSong - MonoGameに設定してください。
そのあとビルドすれば、音声データの設定は完了です。
それでは早速、ボタンによって音楽の再生・停止・音量の変更ができるゲーム(?)を作りましょう。
デフォルトのソースファイルのActivity1.csはそのまま、Game1.csを以下のように書き換えてください。
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using System;
namespace TouchAndPlayMusic
{
/// <summary>
/// This is the main type for your game.
/// </summary>
public class Game1 : Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D ayaya, yoko, alice, karen;
SoundEffect se;
SoundEffectInstance bgm;
Vector2 touchPosition;
TouchCollection touchCollection;
Rectangle ayayarect = new Rectangle(300, 200, 200, 200);
Rectangle alicerect = new Rectangle(700, 200, 200, 200);
Rectangle yokorect = new Rectangle(300, 500, 200, 200);
Rectangle karenrect = new Rectangle(700, 500, 200, 200);
string[] text;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.IsFullScreen = true;
graphics.PreferredBackBufferWidth = 800;
graphics.PreferredBackBufferHeight = 480;
graphics.SupportedOrientations = DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight;
}
/// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
// TODO: Add your initialization logic here
base.Initialize();
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
ayaya = Content.Load<Texture2D>("ayaya2");
alice = Content.Load<Texture2D>("alice");
karen = Content.Load<Texture2D>("karen");
yoko = Content.Load<Texture2D>("yoko");
se = Content.Load<SoundEffect>("stage");
bgm = se.CreateInstance();
bgm.IsLooped = true;
// TODO: use this.Content to load your game content here
}
/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// game-specific content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
Exit();
// TODO: Add your update logic here
touchCollection = TouchPanel.GetState();
base.Update(gameTime);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
text = new string[touchCollection.Count];
for (var i = 0; i < touchCollection.Count; i++)
{
TouchLocation touchLocation = touchCollection[i];
touchPosition = touchLocation.Position;
Point point = new Point((int)touchPosition.X, (int)touchPosition.Y);
if (ayayarect.Contains(point))
{
bgm.Play();
}
else if (alicerect.Contains(point))
{
bgm.Stop();
}
else if (yokorect.Contains(point))
{
if (bgm.Volume >= 0.9f) bgm.Volume = 1.0f;
else bgm.Volume += 0.01f;
}
else if (karenrect.Contains(point))
{
if (bgm.Volume <= 0.1f) bgm.Volume = 0.0f;
else bgm.Volume -= 0.01f;
}
text[i] = string.Format("id={0} X={1} Y={2} state={3} touch count={4}", touchLocation.Id, touchLocation.Position.X, touchLocation.Position.Y, touchLocation.State, touchCollection.Count);
Console.WriteLine(text[i]);
}
spriteBatch.Begin();
spriteBatch.Draw(ayaya, ayayarect, Color.White);
spriteBatch.Draw(alice, alicerect, Color.White);
spriteBatch.Draw(yoko, yokorect, Color.White);
spriteBatch.Draw(karen, karenrect, Color.White);
spriteBatch.End();
// TODO: Add your drawing code here
base.Draw(gameTime);
}
}
}
SoundEffectInstanceの扱いについて軽く説明します。
- bgm.Play()
音声を再生します。 - bgm.Stop()
音声を一時停止します。 - bgm.Volume
0.0F~1.0Fまでの値を指定します。
マスターボリューム(端末の最大音量)*bgm.Volumeで音量調整ができます。
この値の範囲を超えてしまうとエラーを吐くので気を付けてください。
さて、これでSoundEffectの扱い方の説明は終了としますがなぜSongを使わなかったのか、その理由を説明します。
例えば、以下のように記述すれば先ほどと同じアプリが作成できます。
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using System;
namespace TouchAndPlayMusic
{
/// <summary>
/// This is the main type for your game.
/// </summary>
public class Game1 : Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D ayaya, yoko, alice, karen;
Song song;
Vector2 touchPosition;
TouchCollection touchCollection;
Rectangle ayayarect = new Rectangle(300, 200, 200, 200);
Rectangle alicerect = new Rectangle(700, 200, 200, 200);
Rectangle yokorect = new Rectangle(300, 500, 200, 200);
Rectangle karenrect = new Rectangle(700, 500, 200, 200);
string[] text;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.IsFullScreen = true;
graphics.PreferredBackBufferWidth = 800;
graphics.PreferredBackBufferHeight = 480;
graphics.SupportedOrientations = DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight;
}
/// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
// TODO: Add your initialization logic here
base.Initialize();
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
ayaya = Content.Load<Texture2D>("ayaya2");
alice = Content.Load<Texture2D>("alice");
karen = Content.Load<Texture2D>("karen");
yoko = Content.Load<Texture2D>("yoko");
song = Content.Load<Song>("stage");
MediaPlayer.IsRepeating = true;
// TODO: use this.Content to load your game content here
}
/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// game-specific content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
Exit();
// TODO: Add your update logic here
touchCollection = TouchPanel.GetState();
base.Update(gameTime);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
text = new string[touchCollection.Count];
for (var i = 0; i < touchCollection.Count; i++)
{
TouchLocation touchLocation = touchCollection[i];
touchPosition = touchLocation.Position;
Point point = new Point((int)touchPosition.X, (int)touchPosition.Y);
if (ayayarect.Contains(point))
{
MediaPlayer.Play(song);
}
else if (alicerect.Contains(point))
{
MediaPlayer.Stop();
}
else if (yokorect.Contains(point))
{
if (MediaPlayer.Volume >= 0.99F) MediaPlayer.Volume = 1.0F;
else MediaPlayer.Volume += 0.01F;
}
else if (karenrect.Contains(point))
{
if (MediaPlayer.Volume <= 0.01F) MediaPlayer.Volume = 0.0F;
else MediaPlayer.Volume -= 0.01F;
}
text[i] = string.Format("id={0} X={1} Y={2} state={3} touch count={4}", touchLocation.Id, touchLocation.Position.X, touchLocation.Position.Y, touchLocation.State, touchCollection.Count);
Console.WriteLine(text[i]);
}
spriteBatch.Begin();
spriteBatch.Draw(ayaya, ayayarect, Color.White);
spriteBatch.Draw(alice, alicerect, Color.White);
spriteBatch.Draw(yoko, yokorect, Color.White);
spriteBatch.Draw(karen, karenrect, Color.White);
spriteBatch.End();
// TODO: Add your drawing code here
base.Draw(gameTime);
}
}
}
音声ファイルのビルドに注意して実行してみてください。おそらくうまく動くはずです。
Songクラスは音楽プレイヤーのように一つの音楽を再生すること、いくつかの音楽を再生することを前提として設計されています。
よって、端末内にある音楽ファイルを再生することにも使えるのですが自分でやってみるとイマイチ出来ない…
そして極めつけは、songCollectionというsongクラスをまとめて扱えるクラスがあるのですが、何故かそのクラスにはコンストラクタが存在しません。
このへんどうにかなりませんかねぇ…
とりあえず、きんいろモザイクはいいぞということだけ伝われば十分です。