前のだと進行方向の先からマップを見てる事になるから、変だよね。あと俯瞰角度も広く見えるように変更しました。
何度か作り直して今のカタチにしたけど、これで大丈夫かなぁ。まぁキリが無いのでひとまずFIXとして、マップデータ読み込み処理を作ることにします。
ちなみにこの画像の構成のままプレイ出来るとは限りません。技術的に解決すべき事が色々あるからね。
先は長い。
とりあえずマップを作ることにしました。あれこれ試してみたけど、やはり立体っぽく見えるのが良いなぁと思い、微妙に見下ろし視点にする事にしました。
しかし真横からの視点に比べて情報量が一気に増えるなぁ。お陰で必要なマップチップが数倍に膨れ上がったよ。
しかも作ってて合ってるのかどうかわからなくなったり。たまにマップエディタで仮組みして確認しながら作ってはいるけど、よくかわらなくなってきた。
ちなみにマップエディタはPlatinumというフリーソフトを使っています。
とりあえずもう少しマップチップを作ったら、マップデータをゲームに取り込む処理に移ります。
2009/11/4追記
やっぱりマップチップが間違ってたので作り直し中…
前回に引き続きサウンド処理です。古い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);
}
前回書いたとおり画面解像度を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アクションのページなので、一旦止めて自分のプログラムを見直してみよう。
3ヶ月ぶりのゲーム開発です。体験版が出来て燃え尽きてたけど、さすがにもう復活しないとね。
久しぶりなので、改めて方向性を考えてみます。
・画面解像度を変更する
何も考えずに昔と同じ640×480にしてたけど、今更ながら狭いよね。800×600にしようかと思ってます。
・当たり判定を作り直す
現在の矩形1つだけの当たり判定では、大型キャラや変わった形のキャラクタに対応できません。その辺に対応できるよう考え直してみます。
・キャラクタの共通処理を増やす
これはプログラムとしては当然の方向性だけどね。現在は当たり判定とスプライトくらいしか共通処理が無いので、物理演算等も共通化していきます。
・ストーリー性をもたせる
当初から考えてたことだけど、世界観やストーリーを入れ込んでみます。ちなみに使うかどうかわかりませんが、最近妖怪の本なんか読んでます。
他にはサウンドの追加やマップエディタの作成、キーコンフィグ処理、画面エフェクト、イベント、アイテム、セーブ・ロード等も必要ですね。
まだまだやる事は沢山あるな。
キャラクタのアニメーションは結局シンプルなやり方で実装しました。
スプライトを6枚用意してspr_no[]という配列に格納します。spr_no[]内の0~2に右向きスプライト、3~5に左向きスプライトてな感じで3枚ずつ入れてます。
これを時間経過にあわせて差し替えていきます。時間経過はtimeという変数を用意して、これを加算していく事で実現します。ここではtimeが5の倍数になったら次のスプライトに差し替えます。スプライトの決定方法は(time/5)を3で割った余りになります。
最後にジャンプしてたり落下してる場合にアニメーションさせるのは変なので、重力変数gravityが0の場合のみ時間経過させるようにしてます。
これで歩くアニメだけは出来ました。
次回、というか並行して音楽の処理を触ってたんだけど、同じDirectX9.0cでも色々バージョンによる差異があるみたいで、資料が少なくちょっと苦労してます。
音楽後回しにして、ゲームとしてのギミックなんかを先にやろうかな。
//-----------------------------------------------------------------------------
// 移動
void CMyChara::Move()
{
float work_x, work_y; // 移動量
// 他の処理
// スプライトアニメーション
if (work_x!=0) {
if (work_x<0) {
// 左向き
now_spr_no=spr_no[((time/5)%3)+3];
}
else {
// 右向き
now_spr_no=spr_no[((time/5)%3)];
}
if (gravity==0) {
time++;
}
}
}
ゲームキャラクタのアニメーションやろうとしてますが、まだまとまってないので、とりあえず版。
理論は簡単で、キャラクタが動くたびにアニメーションパターン(スプライト)を差し替えていけば良いだけです。それを実現するためには、現在のフレーム値をキャラクタクラスCCharaに持たせて、フレーム値がある数値になった時に表示するスプライトを変更すればアニメーションしてるように見えます。
ただ諸々のデータをどうやって持たせようか考え中。しかも作ってて思ったけど、結構アニメーションパターン必要だね。描くのたいへん。
画像はとりあえずキャラクタに絵を貼り付けてみたもの。キャラクタの周りの白枠はちゃんと絵が描けたら透明にします。
ゲームモードクラスCGameModeの作成です。
このクラスはゲーム中の場面をまとめたものです。例えばゲーム本編のアクションパートをメインモード、ゲームクリア後のエンディングをエンディングモードといったようにまとめます。他にはゲームオーバーモードとかおまけモードとかね。
それらのモードは基底クラスであるゲームモードクラスを継承させて作ります。こうすればキャラクタクラスの時のように、クラスの種類に依存せず処理できます。
上位クラスであるシステムクラスCSystemにChangeGameMode()という関数を作って、こいつにゲームモードの切り替えをさせます。これで各ゲームモードは独立性が高まり、どこから作っても良くなります。ちなみに前回までキャラクタとかを作っていたのは、実はメインモードCGameModeMainの中だったのです。(語弊あるかな?メインモードからキャラクタ管理クラスが呼び出されているという意味です)
次回は、そろそろやらないとなぁという事で、キャラクタのアニメーションを実装します。
//-----------------------------------------------------------------------------
// ゲームモード変更
BOOL CSystem::ChangeGameMode(int mode_no)
{
// 現在のゲームモードを解放
if (GameMode!=NULL) {
GameMode->Term();
delete GameMode;
GameMode=NULL;
}
// ゲームモード変更
switch (mode_no) {
case MAIN:
GameMode= new CGameModeMain;
break;
case GAMEOVER:
GameMode= new CGameModeGameOver;
break;
}
// ちゃんと変更できた?
if (GameMode==NULL) {
MessageBox(NULL, "ゲームモードの変更に失敗しました", "CSystem::ChangeGameMode", MB_OK);
return E_FAIL;
}
// 初期化
GameMode->Init();
return S_OK;
}
//-----------------------------------------------------------------------------
// ゲームモードクラス(継承させるだけなので、何も処理しない)
class CGameMode {
public:
virtual void Init()=0; // 初期化
virtual void Term()=0; // 解放
virtual void Move()=0; // 移動
virtual void Draw()=0; // 描画
};