ADVENT CALENDAR 2019
Altseedの音クラスのID管理を楽にした話
By Funny_Silkie
はじめに
Amusement Creators の Funny_Silkie です。
今回はAltseedの音周りについて,自分で使いやすくしたクラスを作った話をします。
Altseed使わない人からしたら何言ってんだこいつみたいになると思いますが…
目次
Alteedの現状の音周り
Altseedの音周りでは,主にSound
クラスとSoundSource
クラスを使用します。
SoundSource
クラスは読み込んだ音源を保管しておくクラスで,ループ地点などを設定できます。
Sound
クラスではSoundSource
クラスの音源を再生したり,音量を調整できたりします。
また,音源を読み込んでSoundSource
クラスのインスタンスを生成するメソッドもあります。
Sound
クラスのPlay
メソッドで音源を再生するときにIDが返り値で出されます。このIDを使うことで音量の設定や一時停止などをする事が出来ます。
整理するとこんな感じになります。
Sound
クラスで音源を読み込んでSoundSource
クラスのインスタンス生成Sound.Play(SoundSource)
で音源を再生を開始し,IDを取得- IDを使って音量の調整や一時停止などを実施
それではID管理がめんどくさい!ってことで自動でID管理してくれるクラス,SoundPlus
を試しに作ってみました。
名前適当とか言わない
SoundPlusクラスで何をしているか
コンストラクタでSoundSource
クラスの参照を受け取ります。
asd.Sound.Play
を実行されるときに返されるIDをフィールドに持ち,asd.Sound
クラスにおけるIDが必要なメソッドの実行を代理でしてくれます。
また,Sound
クラスのGet…やSet…メソッドをプロパティにして扱いやすくしています。
プロパティの中のSoundState
プロパティでは,音の再生状況を返します。
型はSoundState
という独自の列挙体で,以下のようになっています。
/// <summary>
/// 音の状態を表す列挙体
/// </summary>
public enum SoundState
{
/// <summary>
/// 再生中
/// </summary>
Played,
/// <summary>
/// 一時停止中
/// </summary>
Paused,
/// <summary>
/// 停止中
/// </summary>
Stopped
}
ID管理のためにどうすればよいか
IDは音量調整,再生速度変更,一時停止,再生再開,再生停止など様々なことに必要で,asd.Sound.Play
メソッドを実行するたびに発行されます。
そのため,クラスでIDを管理するとなると1インスタンスにつき1IDが妥当になり,過度な再生を抑止してIDを一意に保つ必要が出てきます。
結果,Play
メソッドを実行するときに再生中の時は例外を投げる処理にしました。
Play
メソッドとは別に,再生状況を無視して強制的に新たにIDを発行し再生するForceToPlay
メソッドも作りました。
また,Play
を何回か行っても音量や再生速度,パン位置などのSound
クラスでのみ設定できる要素が継続できるように,プロパティをフィールドとしても保持しておき,Play
した瞬間に設定しなおします(下記参照)。
//プロパティは概してこんな感じ
/// <summary>
/// 音量を取得または設定する(0.0~1.0)
/// </summary>
/// <exception cref="ArgumentOutOfRangeException">設定する値が規定範囲より外</exception>
public float Volume
{
get => _volume;
set
{
if (value < 0 || value > 1) throw new ArgumentOutOfRangeException();
if (IsPlaying) Engine.Sound.SetVolume(ID, value);
_volume = value;
}
}
private float _volume = 1;
//一時停止中かどうか
private bool isPaused = false;
//Play時に返されるID
private int ID;
/// <summary>
/// 音を再生する
/// </summary>
/// <exception cref="InvalidOperationException">音が既に再生中または一時停止中だった</exception>
public void Play()
{
if (IsPlaying || isPaused) throw new InvalidOperationException();
StartPlaying();
}
/// <summary>
/// 現在の状態に関係なく強制的に音を再生する
/// </summary>
/// <remarks>既に再生中だった音のID管理はこのインスタンスでは実行されなくなる</remarks>
public void ForceToPlay()
{
StartPlaying();
}
private void StartPlaying()
{
//プロパティのコピー
var volume = _volume;
var pan = _panningPosition;
var back = _playBackSpeed;
var enable = _isPlaybackSpeedEnabled;
//新ID発行
ID = Engine.Sound.Play(source);
//プロパティ再設定
Volume = volume;
PanningPosition = pan;
PlayBackSpeed = back;
IsPlaybackSpeedEnabled = enable;
isPaused = false;
}
プロパティで値をセットするときはArgumentOutOfRangeException
をボンボン投げるようになっています。
オーバーしていたら最大値や最小値に丸めるという方法もありますが,そこらへんは実装側の性格次第か…?(このクラスではしょっちゅう例外ぶん投げる)
実装内容
SoundPlus
クラスの実装はこんな感じです。
コンストラクタ
引数の型(変数名) | 実装内容 | 例外 |
---|---|---|
SoundSource(source) | sourceの参照を持つ | ArgumentNullException(source) |
string(path), bool(isDecompress) | pathに存在する音源を読み取りSoundSourceの参照を持つ | ArgumentException(音源読み取り失敗), ArgumentNullException(path) |
SoundSourceクラスの参照を持つため両方それに関係する実装です。
下の方のコンストラクタではasd.Engine.Sound.CreateSoundSource(string path, bool isDecompress)を使ってSoundSourceのインスタンスを生成しています。
プロパティ
プロパティ名 | 型 | アクセッサ | 説明 | 例外 |
---|---|---|---|---|
SoundState | SoundState | get | 現在の音の再生状況(再生中,一時停止中,再生されていない) | なし |
IsReleased | bool | get | フィールドの音源がメモリから解放されたかどうか | なし |
Length | float | get | 音源の長さ(秒) | なし |
LoopStartingPoint | float | get, set | ループ時の再生開始点(秒) | ArgumentOutOfRangeException(0未満またはLoopEndPoint 以下) |
LoopEndPoint | float | get, set | ループ時の再生終了点(秒) | ArgumentOutOfRange(LoopStartPoint 以下またはLength より大きい) |
IsLoopingMode | bool | get, set | ループするかどうか | なし |
Volume | float | get, set | 音量の大きさ | ArgumentOutOfRangeException(0未満または1より大きい) |
IsPlayBackSpeedEnabled | bool | get, set | 再生速度を変更可能かどうか | なし |
PanningPosition | float | get, set | 音量のパン位置(-1に行く程左,1に行く程右) | ArgumentOutOfRangeException(-1未満または1より大きい) |
PlayBackSpeed | float | get, set | 再生速度(音程も変化) | ArgumentOutOfRange(0.25未満または4より大きい)InvalidOperationException(IsPlayBackSpeedEnabled がfalse ) |
メソッド
メソッド名 | 引数 | 説明 | 例外 |
---|---|---|---|
Fade | float second, float targetedVolume | 指定した値に音量を変化させていく | ArgumentOutOfRangeException(second が0未満またはLength より大きい,又はtargetedVolume が0未満または1より大きい)InvalidOperationException(音が再生中ではない) |
FadeIn | float second | 指定した時間をかけてフェードインする | ArgumentOutOfRangeException(second が0未満またはLength より大きい)InvalidOperationException(音が再生中ではない) |
FadeOut | float second | 指定した時間をかけてフェードアウトする | ArgumentOutOfRangeException(second が0未満またはLength より大きい)InvalidOperationException(音が再生中ではない) |
Pause | なし | 再生中の音を一時停止する | InvalidOperationExcetion(音が再生中ではない又は既に一時停止している) |
Play | なし | 音の再生を最初から開始する | InvalidOperationExcetion(音が既に再生中又は一時停止している) |
ForceToPlay | なし | 音の最初からの再生を現在の再生状況問わず行う | なし |
Resume | なし | 一時停止されている状態から再生を再開する | InvalidOperationExcetion(音が再生中ではない又は一時停止していない) |
Stop | なし | 音の再生を停止する | InvalidOperationExcetion(音が再生中ではない) |
最後に
取り敢えずID管理面倒くさい!ってことで実装してみました。今後のゲーム作りで活躍できるといいかなと言う感じです。
ソースコードは私のgithubで申し訳程度に公開されているので見たければどうぞ。
リンク集
SHARE THIS POST
Tweet