開発日誌的にちょろっと(05/08/08)
テクスチャをキャラクタ基底クラスに持たせた場合、派生クラスが生成される段階(つまり対戦開始時)でテクスチャをロードすることになる。といっても試合開始前にロード時間を設ければこの問題は回避されると思ったが、たとえば試合中に生成する必要のあるオブジェクト(波動とかソニックとか)、これらは生成されると同時にテクスチャをファイルから読み込むことになるのだがこれはいかがなものか。おそらく試合中にファイル読み込みなどやってはいけないと思われるので、となるとこの「テクスチャを基底クラスに持たせる」案が微妙になってくる。試合開始前に試合中に使われるであろうオブジェクトはすべて生成して待機状態にしておく、という回避策が考えられるがそれはメモリの無駄遣いではなかろうか。
かといってグローバル領域に大量のテクスチャを置いておくのも余りエレガントとは言えない気がする。構造体にまとめてしまえばそれでいいとは言えるのだろうが。
あ、ちょっと待てよ。そういえばシューティングの時は気にしてなかったが、例えば波動などの飛び道具はこれは当然キャラクタ基底クラスの派生クラスとして存在するのだろうが、これは誰が管理するのだろう?生成を行うのはプレイヤなのだから、この場合リュウクラスが波動クラスを所持していればよいのでは?こうすればリュウクラス生成時に同時に波動クラスが生成され、必要に応じて画面に表示させたり判定を出現させたりすれば…いや駄目だ、結局波動クラスはリュウクラスと同じライフサイクルを持つことになり、波動が出てないのにオブジェクトリストには居続けることになる。オブジェクト指向的(?)に考えるなら、波動はリュウが呼び出すと生成され、画面外あるいは敵にぶつかることでその生涯を終えるようにしないとあかんよな。
結局画像読み込み時の処理速度が問題なのだから(そもそも問題ある範囲なのかは不明だが、いざやってみて重すぎてゲームになりません、では困る。メインゲーム中はなるべく無駄な処理は行わないようにした方がよいはず)、そこだけ分離してしまうのが賢いのだろうか。
つまりテクスチャをプレイキャラ毎にまとめて、キャラ選択時~ゲーム開始時の間に画像ファイルだけまとめて読み込んでしまう、という。オブジェクトがリストに居続けるのは問題で画像をまとめて読み込んでおくのは問題じゃないのか、という声があるかもしれないが、そこはほらリストは毎フレームすべてのオブジェクトを回すのだから、なにもしないオブジェクトが存在する意味はないのですよ。納得。
よし決定、ほしたらテクスチャはキャラクタ毎にまとめておき、生成時に読み込む。つまりプレイヤクラスがすべての利用するテクスチャを生成時に読み込んでおくという手法でとりあえずやってみよう。
typedef class CRyu : public CCharBase{
struct{
LTEXTURE lTexStand;
LTEXTURE lTexJump;
LTEXTURE lTexHadouken;
// 以降リュウが行動するであろう全ての状態をあらわすテクスチャを定義
}m_lTex;
}CRyu, *PCRyu;
といった感じ。
次に、テクスチャを一枚読み込んだとするとその一枚の中に何パターンかのアニメーションが存在(したり)し、これを描画時に切り替えて表示することで動きを表現するという手法を取るつもりなのだが、この切り替えるタイミング、要するに各パターンの表示フレームをどこに置いておくか、である。テクスチャのアニメーション数とそれに応じた各パターンの表示フレーム、軸ずれなどをまとめた構造体を一つ用意してみる。
struct CTextureProperty{
unsigned int iWaitFrame; // 表示フレーム
int iXGap;
int iYGap; // XY軸ずれ
};
これのアニメーションパターン数分の配列を作成し、プレイヤクラスに持たせる。
typedef class CRyu : public CCharBase{
struct{
LTEXTURE lTexStand;
LTEXTURE lTexJump;
LTEXTURE lTexHadouken;
// 以降リュウが行動するであろう全ての状態をあらわすテクスチャを定義
}m_lTex;
int m_iAnimationPtn; // 最大アニメーションパターン数
CTextureProperty m_aTextureProperty[m_iAnimationPtn]; // ↑分のテクスチャ情報の配列
}CRyu, *PCRyu;
な感じでどうか。コンストラクタでファイルから必要な情報を読み込んでおくことで大分データの分離が実現出来たのではなかろうか。
…あほな事に、というか単に勉強不足なだけだが、最大アニメーションパターン数が定義されてないのに配列の要素数に使おうというその感情がわからんとコンパイラに言われた。まったくそのとおりで、実際嫌な予感はしてたので別にいいけど。しゃあないからvectorに書き換えた。あれほしたら最大アニメーションパターン数っていらないんじゃ……?いりませんよね。
vector<CTextureProperty> m_aTextureProperty;
これでええはず。はず。
次回予告
・状態名
・テクスチャファイルパス
・ウェイトフレーム{
攻撃枠領域xn
防御枠領域xn
ぶつかり枠領域xn
}xn
といったデータファイルを用意してみるのはどうだろうか(えらい具体的なタイトルやな~)。おっ楽しみに~!
覚書というか(05/07/31)
まずはこちらを御覧( ´∀`)つ
こちらは一分で描いたのでこんなんですが、こんなんでもまあ十分でしょう。
ここから得るものを抽出すると
・木と山と雲と雷
・人形
・下のゲージ
・上のゲージ
・時間
はぅ、カプエスのしすぎで勝利ポイント数の表示や顔絵の表示などなんかいろいろ抜けてるような気もするけどヽ(゚д゚)ノ。
でこれらをもうちょいまとめると
・背景
・プレイヤ(敵もプレイヤ)
・体力ゲージ
・SCゲージ
・時間
あんま変わってねぇな。でこのへんは全てキャラクタ基底クラスCCharBaseというのを用意して派生。これはいいか。
これらをCApplicationクラスが適当に生成するという訳だが、時間はよしとして各ゲージの扱いというか実際のゲージ値はプレイヤが所持しているのだろうから、これが変化が起こったらCApplicaionに通知して更新描画を行うという方向で考えてみる。
LunaListに追加してしまったら実は各種オブジェクトのポインタを管理する必要はあんまなくて相互に情報を送信し合う必要が出てきた場合のみ中継役としてCApplicaionがそれぞれのポインタを管理、getメソッドで受信してsetメソッドに送信、みたいな形でプレイヤの体力を体力ゲージに反映させることが出来る。けどなんかキレイじゃないような。
いっそのことプレイヤクラスがゲージクラスを包含してたらどうなんやろう。
typedef class CPlayer{
CLife Life_;
CSC SC_;
public:
setLife(float Life){Life_.setLife(Life);}
}CPLAYER, *LPCPLAYER;
LPCPLAYER CApplication::pPlayer_;
void CApplication::HitCheck()
{
if(攻撃が当たったら)
{
pPlayer_->setLife(こんくらいダメージ);
}
}
みたいな。で今書きながら思ったのが上の「こんくらいのダメージ」ってどんくらいやねん、要するに攻撃当てた側が何出してたかが重要になってくるわけで、こんな技出してますよ的なあれを一度まとめて防御側に渡す必要があるのか。
攻撃側の状態(例えば体力が下がると攻撃力があがったり、怒り状態だったりの補正)を攻撃側で行った上で攻撃値を取得し、setLife()で防御側の補正をかけてから代入?
あそれ以前にこのCPlayerが持ってるCLifeクラスのインスタンスはリスト登録されてないから描画ループで呼び出されないような…あ、CPlayerがコンストラクタで明示的に追加してやればよいのか。
でこんな感じ(05/07/25)
画像はlunaサンプルSTGより。配布したりしてないから別にいいですよね?特にやってみたいSTGというのもないもので今のところこんな感じ。あ、弾は手製ですよw
敵一体の動作パターンみたいなんを作ったので、これを拡張してイベントデータ扱いで外部データ読み込みが出来るようにしてみようかな。
一応当たり判定もあったり。LunaCollisionクラスという非常ーに便利なクラスがあるので最近の若い者は録にあたり判定も出来ませんな。わしのことやけど。
長くなってきたので各回毎にコメント欄を。 -- mugi (2005-07-31 15:11:28)
遂にデザパタ登場か…?(05/07/13)
え~しばらく続いたスプライト問題にもようやく中盤に差し掛かったとでも申しましょうか今日この頃、皆さんいかがお過ごしでしょうか。僕は元気にヒッキしてます。
で、スプライトを各オブジェクトが生成されると同時に作成し、解体と同時に破棄という手順を踏んでいたんやけど(要するにコンストラクタで作成してデストラクタで解体)、この場合同じ画像を使うオブジェクトが使用するスプライトは一枚でいいんじゃないかというわけで、メンバに配列として
static LTEXTURE m_lSpr[キャラクタの種類数]
みたいな感じで持ってたんですよ。ほしたら複数オブジェクトを生成してどれか一つが解体された場合に、同じスプライトを持つオブジェクトは全て描画されなくなる、と。そら当たり前ですな。
というわけで、解体は最後のオブジェクトが行うというかそもそも各クラスに一つしか存在しない
というようにすればよいのではないか、ということで奴の登場ですよ。
続きは後ほど。
あーで結局使わずじまいというなんとも計画性のない手記だなあおい。
小メモ(05/07/10)
えと、ちょっとあれからテクスチャからスプライトへ移行したんだけども、ふと思ったのでメモ。
現在LTEXTURE及びLSPRITEのインスタンスをキャラクタ基底クラスに持たせているのだが、例えば同じ画像を使用する複数生成されるオブジェクトの場合、同一のスプライトが重複してしまうのではないかという話。mbxではグローバルにスプライトの配列を用意して、アプリケーション初期化の段階でその配列の各要素にテクスチャから範囲指定で読み込んでいたわけだが、これは単にグラフィック画像データを一枚で済ませるという点だけでなく、生成されたオブジェクトはここから参照しなさいよということなのではということで今の段階では設計上問題があるのかもと思った。
またその配列が二次元になっているのは下記参照ということで、要するに一列目において0番ならプレイヤのスプライト、1番なら敵Aのスプライト、という風に決められているのではないかと。どうなんだろ。
あ、静的メンバにしたらどうなんか、な。勉強不足乙。
↑あーそれだとオブジェクトが生成されるまでテクスチャを確保しとかなあかんからメモリのムダか。
となるとやっぱりグローバルですかね。
さすが詰まらなければ早いなぁ(´ι _` )(05/07/06)
↓のは夜中の話。で今昼間なんやけど、あれからリストでスプライトオブジェクトを(ほんまはまだテクスチャやけど)まわすことに成功。んでついでにサウンドクラスも作ってパックファイルからもういつでも音がならせる様に。ほぼLunaSTG(以降mbx)のLuna9.0c移植みたいな形で、ゲームシステム部分以外は結構パクリに近いのだが、そこはそれということで。
むー、Ayameが初期化出来ないorz。Ayameが読めないとOgg使えないじゃないか。
しゃあないから今のところ全部Wavで、短いループファイル作ってBGM代わり。
とかいうてたけどLuna最新verにしたら全部解決した(||´・ω・)
なんかしらんけどパックファイル対応してる関数が消滅してたのでさしあたって
パック化はなしで代わりにOggが使えるようになって…ややこいなー。まぁフリーのライブラリなんだしどんどん改良してくれてるし文句いうてはいかんわな。感謝感謝。
とりあえず身近な関数の変更だけメモ。
void LunaSound::CreateFromFile(const char*, bool);
const char*pFileName:ファイル名 bool IsNoStop:わかんないw止まらないか否か?謎。
void Luna::SetFrameRate(long FrameRate);
以前あったLuna::SyncFramte()関数が隠匿されて代わりに現れてきたのがこれ。
まあ差し替えるだけ。
void LunaInput::Refresh( void )
void LunaPad::Refresh(void)
内部へ。まあ書かなくてすんだということで。
更新履歴は頻繁に確認せんといかんなー。
二次元配列の謎を解く!(05/07/06)
解く!つーか解いたので忘れないうちにc⌒っ*゚д゚)φ メモメモ...
// スプライト生成
for ( long i = 0; i < 10; i++ )
{
if ( i == 6 )
{
Sprite[i][0] = LunaSprite::Create( 512, Graphic, BLEND_NORMAL );
RECT uv = { 0 };
Sprite[i][1] = LunaSprite::CreateLaser( LASER_DIV, &uv, Graphic, BLEND_ADD );
}
else
{
Sprite[i][0] = LunaSprite::Create( 512, Graphic, BLEND_NORMAL );
Sprite[i][1] = LunaSprite::Create( 512, Graphic, BLEND_ADD );
}
}
main.cpp内でのひとコマなんやけど、注目すべきはSprite[i][1]のある行。ここでケツの引数がBLEND_ADDとなっている点、さらに描画部分でSprite[i][0]の次にSprite[i][1]をレンダリングしている点から、まあレーザやら弾やら、そういった光るやつを加算合成しているのだ。多分。というわけで解決。
進行具合:LunaTextureを用いてキャラクタ基底から派生させたオブジェクトの画像を画面に表示するところまで。
しかし今使ってるのがLunaDx9.0cでこのSTGはLunaDx8で書かれた奴やからもうない関数やらあったりして混乱する(´・ω・)
キャラクタの生成(05/06/28)
大概のオブジェクト(見えるもの)はCSpriteクラスの派生クラスである。CSpriteクラスはメンバ変数としてSortという列挙型を保持し、これによりどの種類のオブジェクトか判断している。例えばCPlayerクラスはCSpriteクラスを継承している。このとき、CPlayerのコンストラクタでCSpriteクラスのメンバイニシャライザを利用して、変数SortにSORT_PLAYERを代入している。
CPLAYER::CPLAYER( void ) : CSPRITE( SORT_PLAYER )
これにより、後々当たり判定のチェック等で、オブジェクトの種類を調べることで分岐をたてやすくなっていると思われる。
void CApplication::AllHitCheck( void )
{
//--------------------------------------------
// 自機&妖精とコイン
//--------------------------------------------
{
LPCSPRITE p1 = (LPCSPRITE)SpriteList.Top();
while ( p1 != NULL )
{
if ( p1->Sort == SORT_PLAYER )
{
LPCSPRITE p2 = (LPCSPRITE)SpriteList.Top();
while ( p2 != NULL )
{
if ( p2->Sort == SORT_COIN )
{
POLYGON2D poly1 = { p1->HitCount, p1->lpHitPt };
POLYGON2D poly2 = { p2->HitCount, p2->lpHitPt };
if ( LunaCollision::Polygon_Polygon( poly1, poly2 ) )
{
AddGold( 50000 );
p2->Damage( 0 );
}
}
p2 = (LPCSPRITE)p2->Next;
}
}
p1 = (LPCSPRITE)p1->Next;
}
}
こんな感じ。いかんせんリスト回すためにキャラクタ基底クラスは必須ぽ。とりあえずメンバ変数及び関数についてはのんびり増やすとして、リストに追加削除回して描画までできんことには…。
Luna製STGの解析(05/未明)
とにかく絵を持つクラスというクラスはCSpriteから派生している。これはBase.hにグローバルで置かれているCList型のインスタンスであるSpriteListにオブジェクトを突っ込み、CSprite型にキャストしてリストを回すことで全てのオブジェクトに対して動作の指示が出せるというものだと思われる。
(CApllication::Main()参照のこと)
しかしSpriteListにオブジェクトを追加している部分が見当たらないんですが、これってどういうこと?(,,゚Д゚)∩オセーテ
一応CApplicationクラスが静的にCPlayerクラスのポインタを持ってて、そいつにnewしたCPlayerへのポインタをCApllication::SetPlayer()というこれまた静的な関数でメンバ変数にコピーしている。
この作業を行うのがCApplication::SceneStg()であり、実際シーン開始時(CApplication::SceneFunc[現在のシーン]()内)にCPlayerはnewされている。のだが…。
ハケーン自己解決。CSpriteのコンストラクタで
CSPRITE::CSPRITE( eCharSort sort )
{
// 処理リストに追加
SpriteList.InsertEnd( this );
}
とあるねorz。
この時点でnewしたポインタはリストの前の要素のメンバにあてられるということでいいのかな。
ソースにSetPlayer(new CPlayer());とあるもんだから、あれnewしたポインタは誰が指してんの?って思ってたんだが。
05/07/04追記
↑なんか変なこというてるぞ~。CApplicationクラスがCPlayerクラスへのポインタを静的に保持してるんだから、SetPlayer()関数でそいつにアドレス代入してるんだからそれでいいのじゃないのか。
前回気になってたのはおそらくSetPlayer(new CPlayer());のCPlayerの後の括弧なによ?って思ってたんやろうが、これはただコンストラクタ呼び出してるよっていうだけなんよね、こういう書式もありだったと知った。(´・ω・`)ショボーン
次。もうひとつのグローバル変数であるSprite[10][2]の部分。
これはLSPRITE型のインスタンスで、LSPRITEはluna.h内で
typedef class LunaSprite LSPRITE;
となってるので、要するに画像関係はこいつに託せばよいと。
でまあ単純にパックされた画像をループでまわしながらSprite[i]に放り込んでるので、
Sprite[1][0]はなんの画像で、とかいう風にきまってるぽ。説明はないけど作者ならわかるというか。
Sprite[10][2]やから、10*2で20枚画像があるということで。各スプライトにおそらくアニメーション用の
画像も入ってるので実際はもう少し多いということか。
で、CApplication::Main()内で
//-------------------------------------------
// 画面描画
//-------------------------------------------
for ( long i = 0; i < 10; i++ )
{
Sprite[i][0]->Rendering();
Sprite[i][1]->Rendering();
}
とあるので、おそらくSprite[][]の後半の配列はレイヤーを表してんじゃないかと。
この場合二層のレイヤーという。
……いや違う、この順番だとそういうレイヤーな感じにならんじゃないか。何故二次元配列に……?
最終更新:2005年08月08日 23:49