「ゲーム開発」カテゴリーアーカイブ

ゲーム開発32:XAudio2でWaveを再生する2

続きです。今回はWaveファイルを読み込んで解析する処理です。

mmioというマルチメディア入出力関数があるので、それを使って解析すればOKです。

前回のCAudioBaseクラスと今回のCAudioWaveクラスの上にCAudioクラスという緩衝材を入れてますが、ほとんどCAudioBaseと同じなので省略します。

これでようやく音が鳴るようになりました。次はまた当たり判定かなぁ。いい加減当たり判定もFIXしたいところ。

//-----------------------------------------------------------------------------
// 生成
CAudioWave::CAudioWave()
{
 m_pwfx=NULL;
 m_hmmio=NULL;
 m_pResourceBuffer=NULL;
 m_dwSize=0;
}
//-----------------------------------------------------------------------------
// 解放
CAudioWave::~CAudioWave()
{
 Close();

 SAFE_DELETE_ARRAY(m_pwfx);
}
//-----------------------------------------------------------------------------
// オープン
HRESULT CAudioWave::Open(LPSTR strFileName, WAVEFORMATEX* pwfx)
{
 HRESULT hr;

 if (strFileName==NULL) {
  return FALSE;
 }
 SAFE_DELETE_ARRAY(m_pwfx);

 // ファイルオープン
 m_hmmio=mmioOpen(strFileName, NULL, MMIO_ALLOCBUF | MMIO_READ);
 if (FAILED(hr=ReadMMIO())) {
  mmioClose(m_hmmio, 0);
  return FALSE;
 }

 if (FAILED(ResetFile())) {
  return FALSE;
 }

 // ファイルサイズ保持
 m_dwSize=m_ck.cksize;

 return TRUE;
}
//-----------------------------------------------------------------------------
// マルチメディア関数で読み込み
HRESULT CAudioWave::ReadMMIO()
{
 MMCKINFO  ckIn;
 PCMWAVEFORMAT   pcmWaveFormat;

 m_pwfx=NULL;

 if ((0!=mmioDescend(m_hmmio, &m_ckRiff, NULL, 0))) {
  return E_FAIL;
 }

 // Waveファイルかどうか
 if ((m_ckRiff.ckid!=FOURCC_RIFF) || (m_ckRiff.fccType!=mmioFOURCC('W', 'A', 'V', 'E'))) {
  return E_FAIL;
 }

 // FMTチャンク検索
 ckIn.ckid=mmioFOURCC('f', 'm', 't', ' ');
 if (0!=mmioDescend(m_hmmio, &ckIn, &m_ckRiff, MMIO_FINDCHUNK)) {
  return E_FAIL;
 }

 // pcmWaveFormat取得
 if (mmioRead(m_hmmio, (HPSTR)&pcmWaveFormat, sizeof(pcmWaveFormat))!=sizeof(pcmWaveFormat)) {
  return E_FAIL;
 }

 // 余分なBYTEがある?
 if (pcmWaveFormat.wf.wFormatTag==WAVE_FORMAT_PCM) {
  m_pwfx=(WAVEFORMATEX*)new CHAR[sizeof(WAVEFORMATEX)];
  if (NULL==m_pwfx) {
   return E_FAIL;
  }

  // waveformatexコピー
  memcpy(m_pwfx, &pcmWaveFormat, sizeof(pcmWaveFormat));
  m_pwfx->cbSize=0;
 }
 else {
  // 余分なBYTE読み込み
  WORD cbExtraBytes=0L;
  if (mmioRead(m_hmmio, (CHAR*)&cbExtraBytes, sizeof(WORD))!=sizeof(WORD)) {
   return E_FAIL;
  }

  m_pwfx=(WAVEFORMATEX*)new CHAR[sizeof(WAVEFORMATEX)+cbExtraBytes];
  if (NULL==m_pwfx) {
   return E_FAIL;
  }

  // waveformatexコピー
  memcpy(m_pwfx, &pcmWaveFormat, sizeof(pcmWaveFormat));
  m_pwfx->cbSize=cbExtraBytes;

  // 余分なBYTE読み込み
  if (mmioRead(m_hmmio, (CHAR*)(((BYTE*)&(m_pwfx->cbSize))+sizeof(WORD)), cbExtraBytes )!=cbExtraBytes) {
   SAFE_DELETE(m_pwfx);
   return E_FAIL;
  }
 }

 // FMTチャンクから出る
 if (0!=mmioAscend(m_hmmio, &ckIn, 0)) {
  SAFE_DELETE(m_pwfx);
  return E_FAIL;
 }

 return S_OK;
}
//-----------------------------------------------------------------------------
// サイズ取得
DWORD CAudioWave::GetSize()
{
 return m_dwSize;
}
//-----------------------------------------------------------------------------
// ファイルリセット
HRESULT CAudioWave::ResetFile()
{
 if (m_hmmio==NULL) {
  return CO_E_NOTINITIALIZED;
 }

 // データ検索
 if (-1==mmioSeek(m_hmmio, m_ckRiff.dwDataOffset+sizeof(FOURCC), SEEK_SET)) {
  return E_FAIL;
 }

 // データチャンク検索
 m_ck.ckid=mmioFOURCC('d', 'a', 't', 'a');
 if (0!=mmioDescend(m_hmmio, &m_ck, &m_ckRiff, MMIO_FINDCHUNK)) {
  return E_FAIL;
 }

 return S_OK;
}
//-----------------------------------------------------------------------------
// ファイルから読み込み
HRESULT CAudioWave::Read(BYTE* pBuffer, DWORD dwSizeToRead, DWORD* pdwSizeRead)
{
 MMIOINFO mmioinfoIn;

 if (m_hmmio==NULL) {
  return CO_E_NOTINITIALIZED;
 }
 if (pBuffer==NULL || pdwSizeRead==NULL) {
  return E_INVALIDARG;
 }

 *pdwSizeRead=0;

 if (0!=mmioGetInfo(m_hmmio, &mmioinfoIn, 0)) {
  return E_FAIL;
 }

 UINT cbDataIn=dwSizeToRead;
 if (cbDataIn>m_ck.cksize) {
  cbDataIn=m_ck.cksize;
 }

 m_ck.cksize-=cbDataIn;

 for (DWORD cT=0; cT<cbDataIn; cT++) {
  // BYTEコピー
  if (mmioinfoIn.pchNext==mmioinfoIn.pchEndRead) {
   if (0!=mmioAdvance(m_hmmio, &mmioinfoIn, MMIO_READ)) {
    return E_FAIL;
   }

   if (mmioinfoIn.pchNext==mmioinfoIn.pchEndRead) {
    return E_FAIL;
   }
  }

  // コピー
  *((BYTE*)pBuffer+cT)=*((BYTE*)mmioinfoIn.pchNext);
  mmioinfoIn.pchNext++;
 }

 if (0!=mmioSetInfo(m_hmmio, &mmioinfoIn, 0)) {
  return E_FAIL;
 }

 *pdwSizeRead=cbDataIn;

 return S_OK;
}
//-----------------------------------------------------------------------------
// ファイルを閉じる
HRESULT CAudioWave::Close()
{
 mmioClose(m_hmmio, 0);
 m_hmmio=NULL;
 SAFE_DELETE_ARRAY(m_pResourceBuffer);

 return S_OK;
}

ゲーム開発31:XAudio2でWaveを再生する1

前にXACT3Waveを再生する処理を書きましたが、いちいちWaveファイルを固めるのはメンドくさい。

てな訳で、改めてXAudio2でWaveを再生することにしました。まぁサンプルにそのものずばりな物があるので(XAudio2BasicSound)、それを元にゲームで使える様な物に作り替えます。

基本的な処理の流れは、COMを初期化してXAudio2を使えるようにする。マスターボイスを作ってその中にWaveから取り出したソースボイスを入れる。再生。といった感じです。

今回はXAudio2の部分CAudioBaseクラスだけ記述しました。Waveファイルからデータを取り出す処理は次回。

//--------------------------------------------------------------------------------------
// 生成
CAudioBase::CAudioBase()
{
 for (int i=0; i<MAX_SOUND; i++) {
  pSourceVoice[i]=NULL;
  pXAudioBuffer[i]=NULL;
  pbWaveData[i]=NULL;
 }

 pXAudio2=NULL;
 pMasteringVoice=NULL;
}
//-----------------------------------------------------------------------------
// 解放
CAudioBase::~CAudioBase()
{
 Release();

 // マスターボイス解放
 if (pMasteringVoice!=NULL) {
  pMasteringVoice->DestroyVoice();
  pMasteringVoice=NULL;
 }

 // XAudio解放
 SAFE_RELEASE(pXAudio2);

 // COM解放
 CoUninitialize();
}
//--------------------------------------------------------------------------------------
// 初期化
BOOL CAudioBase::Init()
{
 // COM初期化
 CoInitializeEx(NULL, COINIT_MULTITHREADED);

 // デバッグモードの場合
 UINT32 flags=0;
#ifdef _DEBUG
 flags|=XAUDIO2_DEBUG_ENGINE;
#endif

 // XAudio2生成
 if (FAILED(XAudio2Create(&pXAudio2, flags))) {
  CoUninitialize();
  return E_FAIL;
 }

 // マスターボイス生成
 if (FAILED(pXAudio2->CreateMasteringVoice(&pMasteringVoice))) {
  SAFE_RELEASE(pXAudio2);
  CoUninitialize();
  return E_FAIL;
 }

 return S_OK;
}
//-----------------------------------------------------------------------------
// 解放
void CAudioBase::Release()
{
 // ソースボイス解放
 for (int i=0; i<MAX_SOUND; i++) {
  ReleaseSound(i);
 }
}
//-----------------------------------------------------------------------------
// サウンド解放
void CAudioBase::ReleaseSound(int no)
{
 if (pbWaveData[no]!=NULL) {
  SAFE_DELETE(pbWaveData[no]);
 }
 if (pXAudioBuffer[no]!=NULL) {
  SAFE_DELETE(pXAudioBuffer[no]);
 }
 if (pSourceVoice[no]!=NULL) {
  pSourceVoice[no]->DestroyVoice();
  pSourceVoice[no]=NULL;
 }
}
//--------------------------------------------------------------------------------------
// ファイル読み込み
int CAudioBase::LoadSound(LPSTR szFilename, int loop_flag)
{
 int  i;

 // 空いているWaveがあるか検索
 for (i=0; i<MAX_SOUND; i++) {
  if (pSourceVoice[i]==NULL) {
   break;
  }
 }
 if (i>=MAX_SOUND) {
  return -1;
 }

 // Waveファイル読み込み
 CAudioWave wav;
 if (FAILED(wav.Open(szFilename, NULL))) {
  return -1;
 }

 // Waveファイルのフォーマット取得
 WAVEFORMATEX* pwfx=wav.GetFormat();

 // サイズ計算
 DWORD cbWaveSize=wav.GetSize();

 // メモリにデータを乗せる
 pbWaveData[i]=new BYTE[cbWaveSize];
 if (FAILED(wav.Read(pbWaveData[i], cbWaveSize, &cbWaveSize))) {
  SAFE_DELETE_ARRAY(pbWaveData[i]);
  return -1;
 }

 // ソースボイス生成
 if (FAILED(pXAudio2->CreateSourceVoice(&pSourceVoice[i], pwfx))) {
  SAFE_DELETE_ARRAY(pbWaveData[i]);
  return -1;
 }

 if (loop_flag!=0) {
  loop_flag=XAUDIO2_LOOP_INFINITE;
 }

 // オーディオバッファ設定
 pXAudioBuffer[i]=new XAUDIO2_BUFFER;
 pXAudioBuffer[i]->Flags=XAUDIO2_END_OF_STREAM;
 pXAudioBuffer[i]->AudioBytes=cbWaveSize;
 pXAudioBuffer[i]->pAudioData=pbWaveData[i];
 pXAudioBuffer[i]->PlayBegin=0;
 pXAudioBuffer[i]->PlayLength=0;
 pXAudioBuffer[i]->LoopBegin=0;
 pXAudioBuffer[i]->LoopLength=0;
 pXAudioBuffer[i]->LoopCount=loop_flag;
 if (FAILED(pSourceVoice[i]->SubmitSourceBuffer(pXAudioBuffer[i]))) {
  pSourceVoice[i]->DestroyVoice();
  SAFE_DELETE_ARRAY(pbWaveData[i]);
  return -1;
 }

 return i;
}
//--------------------------------------------------------------------------------------
// サウンド再生
void CAudioBase::PlaySound(int no)
{
 if (no==-1 || pSourceVoice[no]==NULL) {
  return;
 }
 StopSound(no);
 pSourceVoice[no]->FlushSourceBuffers();
 pSourceVoice[no]->SubmitSourceBuffer(pXAudioBuffer[no]);
 HRESULT hr=pSourceVoice[no]->Start(0);
}
//--------------------------------------------------------------------------------------
// サウンド停止
void CAudioBase::StopSound(int no)
{
 if (no==-1 || pSourceVoice[no]==NULL) {
  return;
 }

 pSourceVoice[no]->Stop(0);
}

ゲーム開発30:坂道との当たり判定

坂道との当たり判定だいぶ間があいたけど、いくつかの処理を追加しました。

マップチップをシンプルに
ドット絵をあれこれいじくり回してたけど、結局シンプルな形にしてしまいました。地味だけど仕方ない。そのうちマップチップを増やして、奥行きを表現したいです。

縦スクロール対応
今までは横スクロールしかしませんでしたが、マップエディタ導入を機にマップサイズを可変にし、縦にもスクロール出来るようにしました。横と違い、staticな変数を持って、ゆっくりスクロールするようにしてます。縦が激しいと酔う。

坂道に対応
坂道を上り降り出来るようになりました。方法は線分の交差判定を使ってます。キャラクタの足元を横一直線の線分とし、坂道の線分との重なりで当たり判定を取っています。この方法なら複雑な地形も簡単に作れるなぁ。実装はネット上にあったコードを参考に組んでます。

処理落ち(汗)
大した事してないはずなのに処理落ち(フレームスキップ)が起きてました。ちなみに50fpsだと大丈夫なのに、60fpsだと処理落ち。もしかしたら単にフレームレートの計算ミスの可能性もあるけど、何にしても原因を特定しなきゃなぁ。

引き続き坂道の処理を作ります。実はまだちょっと挙動がおかしい。

//-----------------------------------------------------------------------------
// 交差判定
BOOL CField::CrossLine(POINT p1, POINT p2, POINT q1, POINT q2)
{
 float A, B, C, D;
 BOOL ret=TRUE;

 A=(float)(p2.y-p1.y);
 B=(float)(p1.x-p2.x);
 C=-(A*p1.x+B*p1.y);
 D=(A*q1.x+B*q1.y+C)*(A*q2.x+B*q2.y+C);

 if (D>0) {
  ret=FALSE;
 }
 else {
  A=(float)(q2.y-q1.y);
  B=(float)(q1.x-q2.x);
  C=-(A*q1.x+B*q1.y);
  D=(A*p1.x+B*p1.y+C)*(A*p2.x+B*p2.y+C);

  if (D>0) {
   ret=FALSE;
  }
 }
 return ret;
}

ゲーム開発29:マップ表示

名称未設定 1前回読み込んだマップデータを表示させました。

スプライト表示用のモジュールは既にあるので、並べて表示するだけです。とりあえずマップデータのレイヤー0を背景、レイヤー1をキャラクタ配置として、各所の初期化部分を書き換えました。

マップデータの読み込みは以下のようになります。

MapData= new CMapData;
MapData->File("stage01map.fmf");

そしてマップ表示はこんな感じ。map_chip=255は透明なので表示しません。

for (i=0; i<MapData->GetHeight(); i++) {
 for (j=0; j<MapData->GetWidth(); j++) {
  map_chip=MapData->Get(0, j, i);
  if (map_chip!=255) {
   g->DrawSprite(spr_no[map_chip], (j*48), (i*48), 0.9f, 0, 0);
  }
 }
}

ただ、これだと表示されない場所まで描画するので、クリッピングする必要があります。あと画面スクロールにも対応させないとね。

上記の処理で表示する事が出来ました。んーやっぱりブロックのグラフィックが不自然な気がするなぁ…

表示はしたものの、当たり判定が入っていないので、今はゲーム開始直後に下に落ちてゲームオーバーになりますw

そんな訳で、次回は大ざっぱな当たり判定を入れてみます。

ゲーム開発28:マップデータ読み込み

今回はマップデータを読み込む処理を、CMapDataクラスとして作成しました。

Platinumというマップエディタでマップを作ってますが、このツールから書き出す形式にFMFというのがあって、これがベタデータでわかり易かったので、とりあえずこれをそのまま取り込みます。

サンプルプログラムがあったのでそれを参考に、というか多少手を入れた程度のものを作ってみました。

まずFMFのデータ形式ですが、ヘッダ部は以下のようになっています。

typedef struct FMFHeader
{
 DWORD Identifier; // ファイル識別子 'FMF_'
 DWORD Size;    // ヘッダを除いたデータサイズ
 DWORD Width;    // マップの横幅
 DWORD Height;   // マップの高さ
 BYTE ChipWidth;  // マップチップ1つの幅(未使用)
 BYTE ChipHeight;  // マップチップ1つの高さ(未使用)
 BYTE LayerCount;  // レイヤーの数
 BYTE BitCount;   // レイヤデータのビットカウント(未使用)
}FMFHEADER;

これの後ろに実データがレイヤー毎に区切り無く入ってます。なのでヘッダ内のSizeでメモリを確保し、WidthHeightから1レイヤーのサイズを計算して、任意の場所のデータを読み書きすれば良い感じです。もうサンプルのまんまだけどねw

しかしこのレイヤーという概念は便利で良いですね。1つのデータファイルにマップデータのみならず、キャラクタの配置イベント発生場所といった物を持たせられます。しかも同一のデータ形式で。

今まではキャラクタの位置情報は別形式で持ってたけど、FMFに一緒に載せてしまおうか。

次回は読み込んだマップデータを表示させます。

//-----------------------------------------------------------------------------
// ファイル読み込み
BOOL CMapData::File(char *filename)
{
 HANDLE FP;
 DWORD ReadBytes;

 // ファイルオープン
 FP=CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
 if (FP==INVALID_HANDLE_VALUE) {
  return FALSE;
 }

 // ヘッダ情報取得
 if (!ReadFile(FP, &fmfHeader, sizeof(FMFHEADER), &ReadBytes, NULL) || ReadBytes!=sizeof(FMFHEADER)) {
  CloseHandle(FP);
  return FALSE;
 }

 // 必要なメモリ確保
 LayerAddr= new BYTE[fmfHeader.Size];
 if (LayerAddr==NULL) {
  CloseHandle(FP);
  return FALSE;
 }

 // レイヤーデータ取得
 if (!ReadFile(FP, LayerAddr, fmfHeader.Size, &ReadBytes, NULL) || ReadBytes!=fmfHeader.Size) {
  CloseHandle(FP);
  return FALSE;
 }

 CloseHandle(FP);

 return TRUE;
}

//-----------------------------------------------------------------------------
// マップ情報取得
int CMapData::Get(BYTE layer_no, BYTE x, BYTE y)
{
 int  chip_no;
 BYTE *Layer;

 Layer=LayerAddr+fmfHeader.Width*fmfHeader.Height*layer_no;
 chip_no=*(Layer+y*fmfHeader.Width+x);

 return chip_no;
}

//-----------------------------------------------------------------------------
// マップ情報上書き
void CMapData::Write(BYTE layer_no, BYTE x, BYTE y, BYTE chip_no)
{
 BYTE *Layer;

 // 範囲チェック
 if (layer_no>=fmfHeader.LayerCount || x>=fmfHeader.Width || y>=fmfHeader.Height) {
  return;
 }

 Layer=LayerAddr+fmfHeader.Width*fmfHeader.Height*layer_no;
 *(Layer+y*fmfHeader.Width+x)=chip_no;
}

//-----------------------------------------------------------------------------
// マップの横幅取得
int CMapData::GetWidth()
{
 return fmfHeader.Width;
}

//-----------------------------------------------------------------------------
// マップの縦幅取得
int CMapData::GetHeight()
{
 return fmfHeader.Height;
}

ゲーム開発27:マップチップ作成中2

名称未設定 4マップチップの方向とか角度とか間違ってたので修正しました。

前のだと進行方向の先からマップを見てる事になるから、変だよね。あと俯瞰角度も広く見えるように変更しました。

何度か作り直して今のカタチにしたけど、これで大丈夫かなぁ。まぁキリが無いのでひとまずFIXとして、マップデータ読み込み処理を作ることにします。

ちなみにこの画像の構成のままプレイ出来るとは限りません。技術的に解決すべき事が色々あるからね。

先は長い。

ゲーム開発26:マップチップ作成中

2b65fcceゲーム開発再開です。

とりあえずマップを作ることにしました。あれこれ試してみたけど、やはり立体っぽく見えるのが良いなぁと思い、微妙に見下ろし視点にする事にしました。

しかし真横からの視点に比べて情報量が一気に増えるなぁ。お陰で必要なマップチップが数倍に膨れ上がったよ。

しかも作ってて合ってるのかどうかわからなくなったり。たまにマップエディタで仮組みして確認しながら作ってはいるけど、よくかわらなくなってきた。

ちなみにマップエディタはPlatinumというフリーソフトを使っています。

とりあえずもう少しマップチップを作ったら、マップデータをゲームに取り込む処理に移ります。

2009/11/4追記
やっぱりマップチップが間違ってたので作り直し中…

ゲーム開発25:サウンド処理の作成

前回に引き続きサウンド処理です。古いDirectSoundを使うか、新しいXAudio2を使うか迷ったのですが、やっぱり新しい方でやる事にしました。

しかしネットで検索してもあまり情報が無いね。新しいと言っても数年前のものなのに。リファレンスとサンプルプログラムで調べたのですが、物凄く面倒です。サウンドファイルを自前で解析してライブラリに投げる方式のようで、サンプルではCWaveFileという独自クラスで解析してました。なのでCWaveFileをいじってたんだけど、いまいち良くわからず。

さらに調べてたらXACT3というライブラリがXAudio2のラッパーであると知る。ラッパー使った方が楽じゃんという訳で、XACT3をいじってたのですが、こっちも資料が少なくて難航する。

さらにさらに調べたらXACT3は元々XBOX用のなので、素材もゲーム機に特化したデータ形式に変換して使うのだとわかりました。つまり複数の音楽ファイルを1つに結合して(Waveバンク)、属性なんかを埋め込んだファイル(Soundバンク)にしないとダメだったのです。

以前のとは全然違うな…

だいぶ回り道をしたけど、ようやく音楽を鳴らすことが出来ました。以下はオーディオ基底クラスCAudioBaseです。1シーンにつき1ファイルって考えて設計したんだけど、もしかしたら変更になるかも。

//-----------------------------------------------------------------------------
// 生成
CAudioBase::CAudioBase()
{
 // いちおう
 pXACT3Engine=NULL;
 pbWaveBank=NULL;
 pbSoundBank=NULL;
}

//-----------------------------------------------------------------------------
// 解放
CAudioBase::~CAudioBase()
{
 Release();
}

//-----------------------------------------------------------------------------
// 初期化
BOOL CAudioBase::Init()
{
 XACT_RUNTIME_PARAMETERS  EngineParameters={0};

 if (pXACT3Engine!=NULL) {
  Release();
 }

 // COM初期化
 CoInitializeEx(NULL, COINIT_MULTITHREADED);

 // XACT3生成
 XACT3CreateEngine(0, &pXACT3Engine);

 // XACT3パラメータ
 pXACT3Engine->Initialize(&EngineParameters);

 return S_OK;
}

//-----------------------------------------------------------------------------
// 解放
void CAudioBase::Release()
{
 // XACT3解放
 if (pXACT3Engine!=NULL) {
  pXACT3Engine->ShutDown();
  pXACT3Engine->Release();
 }
 pXACT3Engine=NULL;

 // Soundバンク解放
 if (pbSoundBank!=NULL) {
  delete[] pbSoundBank;
 }
 pbSoundBank=NULL;

 // Waveバンク解放
 if (pbWaveBank!=NULL) {
  UnmapViewOfFile(pbWaveBank);
 }
 pbWaveBank=NULL;

 // COM解放
 CoUninitialize();
}

//-----------------------------------------------------------------------------
// ファイル読み込み
BOOL CAudioBase::LoadSound(PCSTR filename)
{
 HRESULT hr;
 HANDLE hFile;
 DWORD dwFileSize;
 HANDLE hMapFile;
 DWORD dwBytesRead;
 char pathfile[512];

 // Waveバンク生成
 hr=E_FAIL;
 sprintf_s(pathfile, 512, "data\%s.xwb", filename);
 hFile=CreateFile(pathfile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
 if (hFile!=INVALID_HANDLE_VALUE) {
  dwFileSize=GetFileSize(hFile, NULL);
  if (dwFileSize!=-1) {
   hMapFile=CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, dwFileSize, NULL);
   if (hMapFile) {
    pbWaveBank=MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, 0);
    if (pbWaveBank) {
     hr=pXACT3Engine->CreateInMemoryWaveBank(pbWaveBank, dwFileSize, 0, 0, &pWaveBank);
    }
    CloseHandle(hMapFile);
   }
  }
  CloseHandle(hFile);
 }
 if (FAILED(hr)) {
  return E_FAIL;
 }

 // Soundバンク生成
 hr=E_FAIL;
 sprintf_s(pathfile, 512, "data\%s.xsb", filename);
 hFile=CreateFile(pathfile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
 if (hFile!=INVALID_HANDLE_VALUE) {
  dwFileSize=GetFileSize(hFile, NULL);
  if (dwFileSize!=-1) {
   pbSoundBank= new BYTE[dwFileSize];
   if (pbWaveBank) {
    if (0!=ReadFile(hFile, pbSoundBank, dwFileSize, &dwBytesRead, NULL)) {
     hr=pXACT3Engine->CreateSoundBank(pbSoundBank, dwFileSize, 0, 0, &pSoundBank);
    }
   }
  }
  CloseHandle(hFile);
 }
 if (FAILED(hr)) {
  return E_FAIL;
 }

 return S_OK;
}

//-----------------------------------------------------------------------------
// サウンド再生
void CAudioBase::PlaySound(PCSTR zap)
{
 XACTINDEX iIndex;

 iIndex=pSoundBank->GetCueIndex(zap);
 pSoundBank->Play(iIndex, 0, 0, NULL);
}

//-----------------------------------------------------------------------------
// サウンド停止
void CAudioBase::StopSound(PCSTR zap)
{
 XACTINDEX iIndex;

 iIndex=pSoundBank->GetCueIndex(zap);
 pSoundBank->Stop(iIndex, XACT_FLAG_SOUNDBANK_STOP_IMMEDIATE);
}

ゲーム開発24:サウンド関連を見てみる

0fbbff93.jpg前回書いたとおり画面解像度を800×600に変更しました。面積にして1.5倍。だいぶ広くなったね。これで凝ったマップが作れるな。

次にサウンドを入れ込もうと昔のソースコードを引っ張り出してコンパイルしたら、大量のエラーが出ました。調べてみたらDirectMusicが無くなってたよ。

DirectX8の頃はDirectSoundとDirectMusicの2つを使って音楽再生してたんだけど、今使ってるDirectX9.0c(March 2008)からDirectMusicが無くなり、さらにDirectSoundもXAudio2とやらに差し替えるよう書かれてました。

とりあえずDirectMusicの処理を消したらコンパイルは通ったけど、残りの処理もXAudio2に書き換えた方がいいんだろうか…

XBOXとの互換性対策らしいけど、仕様変更しすぎ。

とりあえず、もう少し調べてみますか。

「ゲームプログラマになる前に覚えておきたい技術」

本屋でゲームプログラマになる前に覚えておきたい技術という本を見つけたので買ってみました。850ページ以上もある電話帳のような技術書です。とりあえず半分だけ読み終えたよ。

文字とコンソール入力のシンプルなゲームからはじまり、一通りの機能を入れた2Dアクションゲームを作成、数学的知識を導入して3Dアクションに進んでいく流れですが、セガの現役プログラマが新人教育用に書いたというだけあって、実戦的です。

ゲーム製作本は結構読んでるけど、ここまで即戦力になるよう書かれた本は今まで無かったです。いきなり「ここはこうやる」という結論から入り、「だが上手くいかない場合がある。どうすればいいか」と考えさせ、「こうする。さらに高速化のためには」等と応用に進む展開は良いですね。

あとC++の機能を駆使して組んでるのも嬉しいです。これも意外と他の本ではやっていない事です。クラスを継承してシーケンス遷移をシンプルにする方法などは、俺がやってる事と方向性が同じで安心したりw 我流でやってる事も多いから、現役の人達のノウハウを読めるのは良いね。

読み終えてない残り半分は3Dアクションのページなので、一旦止めて自分のプログラムを見直してみよう。