iPad専用アクションゲーム「スライムは投げる」

最近のエントリー

C/C++言語で文字列解析をやる場合に避けては通れない部分、それが半角・全角の判定です(※ マルチバイト環境に限ります)。
とは言っても、実際には文字が1バイト長か、2バイト長かを判定します。

何バイト文字かを判定する関数は「_mbclen()」です。引数に、文字列を指定してやると、
先頭の文字が1バイトなのか2バイトなのかを返してくれます。

char *str="あいabcうえお";
printf("byte=%d\n",_mbclen((BYTE *)str));

上の例の場合、画面には2が出力されます。

ついでに、一文字ずつ表示する例も記述しておきます。

char *Str="あいabcうえお";
char *p=Str;
char tmp[3];
while(*p!='\0'){
	switch(_mbclen((BYTE*)p)){
	case 1:
		tmp[0]=*(p++);
		tmp[1]='\0';
		break;
	case 2:
		tmp[0]=*(p++);
		tmp[1]=*(p++);
		tmp[2]='\0';
		break;
	default:
		//error
		break;
	}
	printf("%s\n",tmp);
}

よく、条件によって、代入する値を変更する場合があります。

if(bFlag)
	nValue=16;
else
	nValue=165;

上のプログラムを三項演算子を使って書き直すと、次のようになります。

nValue = bFlag ? 16 : 165;

すっきりしましたね(ちょっと見にくいかもしれませんが)。

三項演算子の構文は次のような感じです。

左辺 = 条件式 ? 条件式が真の場合に代入する値 : 条件式が偽の場合に代入する値 ;

代入の時で無くても、値を必要とする場合ならばどこでも使用出来ます。

printf("%d\n", bFlag ? 16 : 126);

ついでに、関数も呼べます。

bFlag ? printf("真") : printf("偽");

文字列にも使えます(数値と同じですね)。

printf("%s\n", bFlag ? "真" : "偽");

結構使い道はあると思います。

何かアクションが起こった際(例えばマウスクリック時)に、キーボードが押されているかどうかを判定したい場合があります(シフトキー等)。

そんな時には「GetKeyState関数」を使います。

押されていると、SHORT型の最上位ビットが1になります。

最下位ビットが1のときはキーがトグル状態にあることを、0のときはトグルが解除されていることを示します。

//シフトキーの判定例(& 0x80でも良いみたい)
if(GetKeyState(VK_SHIFT) & 0x8000){
	//処理
}

ちなみに、シフトやコントロールキーと言ったキーはマクロで定義されていますが(VK_SHIFT等)、
通常の「A」や「B」と言ったキーは定義されていません。これらのキーは、キーコードを指定しれやれば良いです。

if(GetKeyState('A') & 0x8000){
	//処理
}

同時に全キー状態を取得したい場合には、「GetKeyboardState関数」を使います。
この関数は、256バイトの配列を引き渡してやると、そこへ各キーの状態を詰め込んでくれます。

こちらも、最上位ビットが1の場合は押されているという事になります。

BYTE KeyState[256];
GetKeyboardState(KeyState);

//シフトキー判定例
if(KeyState[VK_SHIFT] & 0x80){
	//処理
}

MFC AppWizardを使ってプロジェクトを作成すると、’C’+プロジェクト名+’App’という感じのクラスが作成されます。
このクラスはCWinAppの派生クラスであり、アプリケーションを管理するクラスとなります。
よく、いろいろなクラスからデータを共有したい場合、グローバル変数を使用するのは嫌われるため、このCWinAppの派生クラスのメンバ変数として宣言します。
この場合、そのメンバ変数へアクセスするために、例えばそのCWinAppの派生クラスが「CMfcTestApp」という名前ならば、

CMfcTestApp *pApp=(CMfcTestApp *)AfxGetApp();
pApp->m_・・・・

とします。

一度で良いならば、

((CMfcTestApp *)AfxGetApp())->m_・・・・

でも良いですね。

ただ、こうした事を何度も書いていると、ソースも見にくくなるし、面倒くさくなってきます。
マクロを使うという手もありますが、今回は別の方法をご紹介します。

MfcTestAppのソースファイルを覗いてみると、こんな記述があります。

/////////////////////////////////////////////////////
// 唯一の CMfcTestApp オブジェクト

CMfcTestApp theApp;

という事は、こいつをどこでも使えるようにすれば、手軽にCMfcTestAppクラスへアクセス出来るわけですね。
というわけで、externを使って、CMfcTestAppのヘッダーファイルの最後の方(クラス宣言が終わった次くらい)へ次のような文字列を埋め込みます。

extern CMfcTestApp theApp;

「CMfcTestApp」というは例なので、自分の派生クラス名にしてください。

こうすれば、あとはどこでも、

theApp.m_・・・

という感じでメンバ変数へアクセス出来るようになるというわけです。

・・・。

やっぱり、こういうやり方って邪道なんでしょうかねぇ(^^;

サンプルコードのダウンロード

では、まずサンプルコードをダウンロードし、解凍してください。
私はVisual C++6.0でコンパイルしているので、お持ちの方はVisual C++でプロジェクトファイルを開いてください(「effec_08.dsw」をダブルクリックすれば開けます)。
圧縮ファイルに含まれる「effec_08.exe」をダブルクリックし、実行してみてください(何かキーを押すと終了します)。
どうでしょう?画面が切り替わり、Takabo Softの文字が徐々にデロ~ンと熔けて(?)いくと思います。

というわけで、今回は画像を溶かすような処理について説明していきたいと思います。

早速サンプルコードの解説

というわけで、今回も順番に説明していきまーす。

//----------[ メイン関数 ]----------------------------------------------------------------------
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow){
    
	略
	
	//DirectDraw開始
	StartDirectDraw(hw);

	//パレット設定
	LoadPalette("takabo.bmp");
	
	//ビットマップを作業用サーフェイスへ読み込む
	LoadBitmap(lpWork,"takabo.bmp",0,0,548,90);//46,195

んで、まずメインループ内で使用するデータを初期化します。

	int i;
	//画像の高さが90だから90個分用意する
	float dy[90],dy_add[90];
	int dy_count[90];

	//準備
	for(i=0;i<90;i++){
		dy[i]=i+195.f;	//画面中央にくるように補正しておく
		dy_add[i]=0.f;
		dy_count[i]=(89-i)*3+60*2;
	}

今回は、画像の横一列(548 x 1)につき1つのデータを作成し、高さが90あるので、90個分のデータを
作成します。「dy」はy座標、「dy_add」は(y座標の)毎ループ毎の増加量、「dy_count」は
処理を開始するまでの残りカウントです。


	int y;
	DWORD tim;
	while(1){
		
		tim=timeGetTime();		//疑似タイマー処理
		ClearScreen(lpBack);	//バックバッファクリア
		
		//座標更新
		for(i=0;i<<90;i++){
			if(dy_count[i]==0){
				dy_add[i]+=0.02f;	//加速
				dy[i]+=dy_add[i];
			} else
				dy_count[i]--;
		}

ここ(↑)でまずデータの座標を更新します。

dy_addに適当な定数を加えていくことで「重力」っぽい処理が再現できます。

(ゲーム制作基礎でもやりましたね)

		//表示
		for(i=0;i<90;i++){
			//一番下の画像は普通に表示
			if(i==89){
				y=(int)dy[i];
				if(y<480)
					Blt(lpBack,46,y,548,1,lpWork,0,i,FALSE);
			} 
			//それ以外は、一つ下の画像の座標まで補間(謎
			else {
				for(y=(int)dy[i];y<(int)dy[i+1];y++)
					if(y<480)
						Blt(lpBack,46,y,548,1,lpWork,0,i,FALSE);
			}
		}

データの座標を更新し終えたら次は表示です。

そのまま「dy」の示す座標に画像を貼り付けていくと、「穴」が空きます。

ですから、その穴を埋めるようにfor文とかで間を補ってやります。

		Flip();					//フリッピング
		do{						//メッセージループ
			while(PeekMessage(&msg,NULL,0,0,PM_NOREMOVE)){
				if(!GetMessage(&msg,NULL,0,0)) 
					Quit();
				TranslateMessage(&msg);
				DispatchMessage(&msg);

			}
		}while(timeGetTime()<tim+16);	//1000/60=16.666(60FPS)
	}
	
	return(FALSE);
}

前回に比べれば簡単ですね(^^;

あんまり「熔ける」という感じでは無いですが(爆

これを更に1ドットずつに区切って処理すればもっと本物っぽくなるかなぁ?

記事検索

コミュニティ

Banner designed by 写楽様
ドット絵掲示板
ドット絵掲示板
自作の「ドット絵」を投稿することができます。ドッターさん同士の交流の場としてご利用ください。
TakaboSoft Wiki
TakaboSoft Wiki
ソフトウェアに関する質問・不具合報告・要望などを書き込むことができます。