feature image

2016年9月14日 | ブログ記事

MonoGameを使ってAndroidのゲームを作ろう その3

どうも、Namazuです。
今回は扱う内容は以下の通りです。

前回前々回の記事の内容を使いますのでまずはそちらをご覧ください。

音楽の再生をする方法は大きく分けて二つあります。

BGMなどの再生ならばSongの方が適しているかもしれませんが、今回はSoundEffectクラスの扱い方を紹介します。
Songを扱わない理由は後述します。

今回扱うクラスは以下の通りです。

流れとしては、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
}

インスタンスを用いているのは、"SoundEffect"ならではでしょう。
同時に同じ音声を再生することも考慮されています。
ただし、一度に保持できるインスタンスの数には限りがあるので、不要になったものは削除しましょう。

音声データもContent.mgcbからビルドしなければいけないのですが、一つ注意点があります。
画像をご覧ください。

%e8%aa%ac%e6%98%8e%e7%94%a8

もうお馴染みの画面ですが、この左下のボックスの中の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の扱いについて軽く説明します。

さて、これで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クラスをまとめて扱えるクラスがあるのですが、何故かそのクラスにはコンストラクタが存在しません。
このへんどうにかなりませんかねぇ…

とりあえず、きんいろモザイクはいいぞということだけ伝われば十分です。

Namazu icon
この記事を書いた人
Namazu

Namazuです。 今まで制作に携わったゲームはTypingWar、EccentricEscape、進捗どうですか?、NinjaFlicker等です。 MonoGameにまつわる記事も書いています。

この記事をシェア

このエントリーをはてなブックマークに追加
共有

関連する記事

2023年11月21日
School Breakin' Tag -新感覚おにごっこ-
s9 icon s9
2023年9月3日
タイピング&アクション『TypeTheCode』作りました
wal icon wal
2023年4月25日
【驚愕】作曲4年目だった男が大学3年間ゲームサウンドに関わった末路...【ゲームサウンドのお仕事について】
tenya icon tenya
ERC20トークンを用いた宝探しゲーム(真)の提案【アドベントカレンダー2018 10日目】 feature image
2018年11月3日
ERC20トークンを用いた宝探しゲーム(真)の提案【アドベントカレンダー2018 10日目】
Azon icon Azon
2021年5月19日
CPCTF2021を実現させたスコアサーバー
xxpoxx icon xxpoxx
2023年4月27日
Vulkanのデバイスドライバを自作してみた
kegra icon kegra
記事一覧 タグ一覧 Google アナリティクスについて 特定商取引法に基づく表記