staticで作ったメンバ関数を持つサウンドclassの扱い

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
土門

staticで作ったメンバ関数を持つサウンドclassの扱い

#1

投稿記事 by 土門 » 12年前

皆様、はじめまして。
C++、DXライブラリでゲームを制作している者ですが質問させてください。

今、BGMとSEを実装しようと思っています。
単発で読ませて鳴らすというのは出来るのですが
各クラスがなるべく散らからない様にしたくて
サウンドクラスというものを作り、各クラスでは再生関数、停止関数だけ
記述する方法を模索しております。
自分は各シーンごとにクラス分けしています。
ネットで調べたところ、どのクラスからも呼べる(playできる)様に
staticで作ったメンバ関数を持つサウンドclassを作ればいい、とありました。
(ちなみにメインCPPにはシーンを管理するシーンマネージャーしかおいてません)

で、そうすべき理屈はわかってるつもりなのですが、
実際に鳴らすとなると疑問があります。
自クラスで別のクラスの関数を呼ぶのには、自クラスにて
ポインタ宣言して、そこにその別のクラスをインスタンス化してから
呼び出さなければなりませんよね?
(例 自クラスにて 別クラスインスタンス名->メンバ関数名(); 等 )

作ったサウンドクラスのメンバ関数、
たとえばstatic void play(int snum)やvoid playbgm(int snum)を、
呼ぶ各クラスでいちいち上記ポインタ宣言してサウンドクラスのインスタンス化をしてやらなければ
ならないのでしょうか???

また、サウンドクラスのメンバ関数に
void loadFiles()というファイル読み込み関数を作り、
その定義にてゲーム中に使いたいすべてのBGM,SEをハンドルに読み込ませています。
これを実行するのはサウンドクラスのインスタンス化で実行されるコンストラクタ内で
いいのでしょうか?
しかしその場合、各クラスでインスタンス化なんてしていたら、
メモリが大変なことになりそうな気がしますが、それは思い過ごしでしょうか?
それとも、そもそも各クラスで毎度インスタンス化すること自体間違ってますか?

要点をまとめますと
〇各クラス内で呼びたいサウンドクラスはどこでインスタンス化するのか
〇そのサウンドクラスのメンバであるファイル読み込み関数はコンストラクタ内、初期化内?

よろしくお願い致します。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: staticで作ったメンバ関数を持つサウンドclassの扱い

#2

投稿記事 by softya(ソフト屋) » 12年前

staticなメンバ関数、メンバ変数はインスタンスを生成する必要はありません。プログラム起動時に実体が生成されます。
【補足】 クラス名::メンバ関数名();で呼び出すことができます。たぶん、何処かでC++の勉強の過程で見ていると思います。
あるいはシングルトンにしてしまうのも手です(こちらの方が扱いやすい)。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

katariya
記事: 11
登録日時: 12年前

Re: staticで作ったメンバ関数を持つサウンドclassの扱い

#3

投稿記事 by katariya » 12年前

初期化は普通シーンマネージャークラス(CSceneManager)の初期化のタイミングで行うとおもいます。
ロードに関しては、重すぎるためとかで使うシーンごとに分割ロードしたい場合もあるのでなんとも…。
いちばん簡単なのは初期化と同時に全ての楽曲をロードするのが一番簡単です。

あとインスタンス化が気になるのであればサウンドクラスのポインタを渡して使用するのはどうでしょうか。

コード:

// CSceneManager.h
public CSceneManager
{
public:
        Init();
        static CSoundManager* GetSound(){ return m_pSound; };
private:
    static CSoundManager* m_pSound;
}

// CSceneManager.cpp
CSoundManager* CSceneManager::m_pSound = NULL;

void CSceneManager::Init()
{
     // ここでしかnewしないよ!
     m_pSound = new CSoundManager();
  m_pSound->LoadSound();
}

// 自クラス
void CHoge::Update()
{
     CSoundManager* pSound = CSceneManager::GetSound();
     pSound->Play(// 鳴らしたい曲);
}

この場合インスタンス化とロードは一回で、
そのインスタンスを使いまわしています。
便利ですねポインタ渡し。

あと、これが不細工だと思うなら、
デザインパターンのシングルトンを使ってみてはいかがでしょうか?

デザインパターン編 第9章 Singletonパターン
http://www.geocities.jp/ky_webid/design ... n/009.html

土門

Re: staticで作ったメンバ関数を持つサウンドclassの扱い

#4

投稿記事 by 土門 » 12年前

解答をありがとうございます。お二人様の意見をもとに
デザインパターンのSingletonを使って、サウンドクラスを自クラスでインスタンス化したものを
各クラスで呼び出す形にしようと思います。
ですが、ビルド後、タイトル画面が実行される時にエラーが出てしまいます。
エラーはTitle.cppでサウンドクラスの再生関数を記述すると起こり、コメントアウトすると
ちゃんとゲームは表示されますのでSingletonのミスでは無く、
サウンドクラスをうまく扱えてないと思われます。。。。
ソースを記述しますので、おかしい場所を教えて頂けないでしょうか?
また本末転倒ですが、例外処理の「ファイナル読込エラー」まで表示されますので
ファイル自体が読み込まれてない??感じです(ファイル名やパスは確実にあってるんですが)。
サウンドクラスのコンストラクタ内に記述したthis->loadFiles();が実行されてないのでしょうか、、
どうかご教示をお願いいたします。

<SoundBox.h>

コード:

#pragma once

#include "DxLib.h"
#include <vector>
using namespace std;

class SoundBox
{
public:
	static SoundBox* getSoundIns();	
	static vector<int> sounds;
	static int bgm;
	static void loadFiles() throw(int);    //読込

	static int set(int shandle);			//登録
	static void play(int snum);			//再生
	static void stop(int snum);			//停止
	static void playbgm(int snum);		//BGMの再生

private:
	SoundBox(void);
	~SoundBox(void);

	static SoundBox* instance;
};
<SoundBox.cpp>

コード:


#include "SoundBox.h"

SoundBox::SoundBox(void)
{
	this->loadFiles();
}
SoundBox::~SoundBox(void)
{
}

//シングルトンのためのゲッター
SoundBox* SoundBox::getSoundIns()
{
	if(instance==0)
	{
		instance = new SoundBox();
	}
	return instance;
}

SoundBox* SoundBox::instance = new SoundBox();

//サウンド関連クラスのメンバ関数の記述
//SoundBoxクラスメンバ変数の実体
vector<int> SoundBox::sounds;
int SoundBox::bgm = -1;


//●サウンドハンドルの登録(登録インデックスを返す)
int SoundBox::set(int shandle){
	sounds.push_back(shandle);	//曲リストの末尾に追加
	return( sounds.size()-1);
}

//●サウンドの再生
void SoundBox::play(int snum){
	PlaySoundMem(sounds.at(snum) , DX_PLAYTYPE_BACK);
}

//●サウンドの停止
void SoundBox::stop(int snum){
	StopSoundMem(sounds.at(snum));
}

//●BGMの再生(現在のBGMは停止)
void SoundBox::playbgm(int snum){
	int newbgm = sounds.at(snum);	//ローカル変数宣言 sounds(←vector)の番号
	if(bgm == newbgm)return;		//今かかっているBGMと同じなら何もしない
	StopSoundMem(bgm);				//再生中のBGM(番号)を停止
	bgm = newbgm;
	PlaySoundMem(bgm , DX_PLAYTYPE_LOOP);	//ループ再生する
}

//●サウンドファイルの読み込み
void SoundBox::loadFiles()throw(int){
	int sh;

	try{//例外処理の設定
		//ファイルが読めなければ-1を返し、外部エラー。そうで無ければ、soundBoxに格納

		if( (sh=LoadSoundMem("sound/bgm01.wav")) == -1)throw(-5);	//0
		SoundBox::set(sh);//set関数にてvectorにぶちこむ

	}
	catch(int errcode){
		MessageBox(NULL,"ファイナル読込エラー","メッセージ",MB_OK);
		return;
	}
}//ファイルの読込
<Title.cpp>のDraw関数のところ

コード:

//描画
void Title::Draw(){
	SoundBox::getSoundIns()->playbgm(0);//←ここでサウンドクラスの呼出ししてます
      BaseScene::Draw();//親クラスの描画メソッドを呼んでます
      DrawString(0,20,"タイトル画面",GetColor(255,255,255));
	DrawString(0,40,"Sキーで選択先変更、Xキーで決定",GetColor(255,255,255));

	//カーソルの描画
	if(cursor % 2 == 0){
		DrawGraph(190,260,cursorgh,TRUE);//カーソルを上に
	}else{
		DrawGraph(190,280,cursorgh,TRUE);}//カーソルを下に
}

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: staticで作ったメンバ関数を持つサウンドclassの扱い

#5

投稿記事 by softya(ソフト屋) » 12年前

気になること。
・インデントが乱れている。
・Drawでplaybgm
・SoundBox::getSoundIns()ではなくSoundBox::getInstance()の方が素直でしょう。
・throw(-5)はエラーをちゃんと定義しましょう。

エラーの原因
・Dxlib_Init()前にインスタンスが生成されて、loadFiles()されているのが原因です。
明示的にインスタンス生成して初期化したほうが良いです。

その他
・staticじゃなくても良い物。
vector<int> SoundBox::sounds;
int SoundBox::bgm = -1;
とかはstaticじゃ無くても良いでしょう。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

土門

Re: staticで作ったメンバ関数を持つサウンドclassの扱い

#6

投稿記事 by 土門 » 12年前

返答をありがとうございます。

コード:

>・Drawでplaybgm

Title::Update()内に移しました。

>・SoundBox::getSoundIns()ではなくSoundBox::getInstance()の方が素直でしょう。

そうですね、修正しました。

>・throw(-5)はエラーをちゃんと定義しましょう。
>・Dxlib_Init()前にインスタンスが生成されて、loadFiles()されているのが原因です。
明示的にインスタンス生成して初期化したほうが良いです。

すみません、この二点の意味がまだよくわかりません。
もう少し具体的に教えていただけないでしょうか?
エラーの定義というのは(-5)を受け取った場合のものですか??
参考にしてる例外処理のソースには
	catch(int errcode){
		MessageBox(NULL,"ファイナル読込エラー","メッセージ",MB_OK);
		return;
しかありません。。。エラーが出た時(すなわちファイル読み取りできない場合)エラーメッセージの窓も出ますし。。
それと、自分としてはmain.cppのDxlib_Init()よりも前にloadFiles()しているつもりが無いのです、、


>・staticじゃなくても良い物。
vector<int> SoundBox::sounds;
int SoundBox::bgm = -1;

これらをstaticじゃ無くならすと、SoundBox.cpp内の関数で
bgmやlistのsoundsが「静的でないメンバは~」のエラーとなり赤字になります。。。

初心者ゆえに、お門違いな返信となっていましたら申し訳ございません。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: staticで作ったメンバ関数を持つサウンドclassの扱い

#7

投稿記事 by softya(ソフト屋) » 12年前

throw(-5)はエラーをちゃんと定義しましょう。
-5だと意味不明なのでconst intとかenumで定義してくださいって事です。
Dxlib_Init()前にインスタンスが生成されて、loadFiles()されているのが原因です。
Dxlib_Init()実行前にLoadSoundMem()は出来ないか、出来てもDxlib_Init()実行時にハンドルが全て消されて無効になります。
なので順番的に初期化はDxlib_Init()の後でなくてはいけません。つまり、グローバルなインスタンス生成と同時に初期化してはいけないことになります。

リンク先の例に有りましたが、静的ローカル変数を使う方法でインスタンス生成すればタイミングを制御することはできますよね?
これらをstaticじゃ無くならすと、SoundBox.cpp内の関数で
bgmやlistのsoundsが「静的でないメンバは~」のエラーとなり赤字になります。。。
シングルトンパターンの場合は、static なのはGetInstance()と static Singleton* msInstance;だけで良いです。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

土門

Re: staticで作ったメンバ関数を持つサウンドclassの扱い

#8

投稿記事 by 土門 » 12年前

エラーの原因
>・Dxlib_Init()前にインスタンスが生成されて、loadFiles()されているのが原因です。
>明示的にインスタンス生成して初期化したほうが良いです。

この意味が理解でき、自己解決致しました。
場所的にちょっと不細工な気がするんですがどうですか?
もっと適材適所な場所ありますでしょうか?

コード:


int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
	ChangeWindowMode(TRUE);		//ウィンドウモードにする
	SetGraphMode(512,480,8);	//ウィンドウサイズ変更 DxLib_Init()の前に打つ
	if(DxLib_Init() == -1) return -1;	//DXライブラリ初期化

	SetDrawScreen(DX_SCREEN_BACK);
	SoundBox::getInstance()->loadFiles();   ←ここにこじいれ、実行させましたら、サウンドを読込し、エラー無しになりました。

    while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0&& CheckHitKey(KEY_INPUT_ESCAPE)==0 ){//画面更新 & メッセージ処理 & 画面消去
		Update******
		Draw********
    }

    DxLib_End(); // DXライブラリ終了処理
    return 0;
}

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: staticで作ったメンバ関数を持つサウンドclassの扱い

#9

投稿記事 by softya(ソフト屋) » 12年前

そこに他の初期化も並ぶのなら違和感はないと思います。
loadFiles()なのかInitalize()なのかは他のクラスとの統一性で決めてください。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。


閉鎖

“C言語何でも質問掲示板” へ戻る