ページ 11

メイン画面の作り方について・・・・

Posted: 2011年11月27日(日) 00:34
by ルート
只今、メイン画面、選択画面を作り中なのですが、ゲームプログラミングの館より以下のコードを参考にして作りたいと思っているのですが、

コード:

#include "DxLib.h"

int Key[256]; // キーが押されているフレーム数を格納する

// キーの入力状態を更新する
int gpUpdateKey(){
	char tmpKey[256]; // 現在のキーの入力状態を格納する
	GetHitKeyStateAll( tmpKey ); // 全てのキーの入力状態を得る
	for( int i=0; i<256; i++ ){ 
		if( tmpKey[i] != 0 ){ // i番のキーコードに対応するキーが押されていたら
			Key[i]++;     // 加算
		} else {              // 押されていなければ
			Key[i] = 0;   // 0にする
		}
	}
	return 0;
}

// メニュー項目の表示に必要な構造体を用意する
typedef struct{
        int x, y;       // 座標格納用変数
        char name[128]; // 項目名格納用変数
} MenuElement_t ;

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
        ChangeWindowMode(TRUE), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK ); //ウィンドウモード変更と初期化と裏画面設定

        // メニュー項目要素を5つ作る
        MenuElement_t MenuElement[5]={
                {  80, 100, "ゲームスタート" }, // タグの中身の順番で格納される。xに80が、yに100が、nameに"ゲームスタート"が
                { 100, 150, "おまけ" },
                { 100, 200, "ヘルプ" },
                { 100, 250, "コンフィグ" },
                { 100, 300, "ゲーム終了" },
        };
        int SelectNum = 0; // 現在の選択番号

        // while(裏画面を表画面に反映, メッセージ処理, 画面クリア, キー更新)
        while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 && gpUpdateKey()==0 ){

                // 計算フェーズ 

                if( Key[ KEY_INPUT_DOWN ] == 1 ){ // 下キーが押された瞬間だけ処理

                        SelectNum = ( SelectNum + 1 ) % 5; // 現在の選択項目を一つ下にずらす(ループする)
                }

		if( Key[ KEY_INPUT_UP ] == 1 ){ // 上キーが押された瞬間だけ処理

			SelectNum = ( SelectNum + 4 ) % 5; // 現在の選択項目を一つ上にずらす(逆ループする)
		}

		if( Key[ KEY_INPUT_DOWN ] == 1 || Key[ KEY_INPUT_UP ] == 1 ){ // 下キーか、上キーが押された瞬間
			for( int i=0; i<5; i++ ){              // メニュー項目数である5個ループ処理
				if( i == SelectNum ){          // 今処理しているのが、選択番号と同じ要素なら
					MenuElement[i].x = 80; // 座標を80にする
				} else {                       // 今処理しているのが、選択番号以外なら
					MenuElement[i].x = 100;// 座標を100にする
				}
			}
		}

		// 描画フェーズ

		for( int i=0; i<5; i++ ){ // メニュー項目を描画
			DrawFormatString( MenuElement[i].x, MenuElement[i].y, GetColor(255,255,255), MenuElement[i].name );
		}

	}

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

コード:

#include "DxLib.h"
 
int function_status=0,White;
char KeyBuf[ 256 ] ;
 
void Opening(){
        DrawString(100,100,"オープニング画面 (zをプッシュ)",White);
        if(KeyBuf[KEY_INPUT_Z]==1)
                function_status=1;
}
 
void Menu(){
        DrawString(100,140,"メニュー画面 (xをプッシュ)",White);
        if(KeyBuf[KEY_INPUT_X]==1)
                function_status=2;
}
 
void Danjon(){
        DrawString(100,180,"ダンジョン画面 (cをプッシュ)",White);
        if(KeyBuf[KEY_INPUT_C]==1)
                function_status=3;
}
 
void attack(){
        DrawString(100,220,"戦闘画面 (vをプッシュ)",White);
        if(KeyBuf[KEY_INPUT_V]==1)
                function_status=4;
}
 
void Ending(){
        DrawString(100,260,"エンディング画面 (bをプッシュ)",White);
        if(KeyBuf[KEY_INPUT_B]==1)
                function_status=5;
}
 
 
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow ){
        ChangeWindowMode( TRUE ) ;                                   //ウィンドウモードに変更
        if( DxLib_Init() == -1 ) return -1;                      // DXライブラリ初期化処理 エラーが起きたら終了 
         
        White = GetColor( 255 , 255 , 255 ) ;                    //色の取得
          
        SetDrawScreen( DX_SCREEN_BACK ) ;                                // 描画先を裏画面に設定
           
        while( 1 ){
                ClearDrawScreen();                                                               // 裏画面のデータを全て削除
                GetHitKeyStateAll( KeyBuf ) ;                                    // すべてのキーの状態を得る
 
                switch(function_status){
                        case 0:
                                Opening();
                                break;
                        case 1:
                                Menu();
                                break;
                        case 2:
                                Danjon();
                                break;
                        case 3:
                                attack();
                                break;
                        case 4:
                                Ending();
                                break;
                        default:
                                DxLib_End() ;                                // DXライブラリ使用の終了処理
                                return 0;
                                break;
                }
 
                if( ProcessMessage() == -1 ) break ;                 //エラーが起きたら終了
                
                ScreenFlip() ;                                   // 裏画面データを表画面へ反映
        }
 
        DxLib_End() ;                                            // DXライブラリ使用の終了処理
        return 0 ;                                               // ソフトの終了
}
この二つのソースを使いたいと思っているのですが、上のソースは、選択画面としてはいいのですが・・・・
キーを押したときの次の画面へ進める方法がいまいち、わからなくて、
下のソースだと、選択文字が一文ずつしかひょうじできないので、・・・・

要点をまとめると
・選択メニューが何列でも表示できて、決定キーみたいなのは、一つのキーで行いたい

*これでわからなかったらすいません(説明が下手なもので・・・

Re: メイン画面の作り方について・・・・

Posted: 2011年11月27日(日) 01:14
by softya(ソフト屋)
サンプルはあくまでサンプルですので、これを参考に自分の作りたいものを作るわけです。
そのためにはサンプルが理解出来ないといけません。

まず手始めに、上のコードメニュー選択部分を別関数にして選択された番号を返す関数を作って見ませんか?
そうですね。決定されていない時は-1を返すでどうでしょうか?

その時に
while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 && gpUpdateKey()==0 ){
の部分はWinMainに残したままにして下さい。
呼び出すう関数側に同様のループを作ったらダメでよ。

Re: メイン画面の作り方について・・・・

Posted: 2011年11月27日(日) 12:38
by Dixq (管理人)
> 下のソースだと、選択文字が一文ずつしかひょうじできないので、・・・・

あれはメニュー項目を表示しているのではないです。
現在何の処理を行っているかを表しています。

まず手始めに、(softyaさんの回答もご覧ください) 下のソースを参考に
「メニュー画面トップ」「ゲーム」「コンフィグ」の3つのモジュールを作りその時のシーンによってコールするモジュールを変える仕組みを作ってみてはいかがでしょう。
各モジュールが行うべき振る舞いは以下のように定義したとします。

・メニュー画面トップ
 「ゲーム」「コンフィグ」「終了」の3つが選べ、上記2つが選択されたら以後そのモジュールをコールするようにする。終了が呼ばれたらプログラムを終了する

・ゲーム
 単に画面に「ゲーム中」とでも表示して、Escを押したらメニュー画面トップに帰る

・コンフィグ
 単に画面に「コンフィグ」とでも表示して、Escを押したらメニュー画面トップに帰る

もしC++であれば、シーンが決定されるたびに、シーンクラスをリストに詰めていき、
常にbackにあるクラスを呼んであげると楽に実装出来るようです。

Re: メイン画面の作り方について・・・・

Posted: 2011年11月27日(日) 13:51
by ルート

コード:

#include "DxLib.h"
#include "gamen.h"
int Key[256]; // キーが押されているフレーム数を格納する

// キーの入力状態を更新する
int GetHitKeyStateAll_2(int KeyStateBuf[]){
        char GetHitKeyStateAll_Key[256];
        GetHitKeyStateAll( GetHitKeyStateAll_Key );
        for(int i=0;i<256;i++){
                if(GetHitKeyStateAll_Key[i]==1) KeyStateBuf[i]++;
                else                            KeyStateBuf[i]=0;
        }
       return 0;
}



int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int)
{
	ChangeWindowMode(TRUE);   //ウィンドウモードに設定

	SetWindowSize(1000,600);   //ウィンドウサイズ変更

    int y  = 300;
   
    int White = GetColor(255,255,255);

    while(!ProcessMessage()   && !GetHitKeyStateAll_2(Key)&& !ClearDrawScreen() && !Key[KEY_INPUT_ESCAPE]){
      
      

      DrawString( 260 , y   , "→"    , White ); //  矢印(選択を示す)
      DrawString( 280 , 300 , "はじめから"  , White );
      DrawString( 280 , 320 , "つづきから"  , White );

	  // もし『はじめから』(y=300)でボタンが押されたら
      if(y == 300 && Key[KEY_INPUT_RETURN] == 1){
          /* 初期化処理 */
          ClearDrawScreen();
		  Gamen();
		DxLib_End();
		return 0;

      }
      // もし『つづきから』(y=320)でボタンが押されたら
      if(y == 320 && Key[KEY_INPUT_RETURN] == 1){
          
          ClearDrawScreen();

          DxLib_End();
		return 0;
      }

    
      
      
      if( Key[KEY_INPUT_DOWN]==1 || ( Key[KEY_INPUT_UP]%5==0 && Key[KEY_INPUT_UP]>30)){
           // たった今押したか、30カウンター以上押しっぱなしかつ5回に一度
              y+=20;
              if(y==340){  // y座標が200なら(選択が一番下なら)
                y=300;     // 選択座標を一番上に
			  }
      }
      if( Key[KEY_INPUT_UP]==1 || ( Key[KEY_INPUT_DOWN]%5==0 && Key[KEY_INPUT_DOWN]>30)){
           // たった今押したか、30カウンター以上押しっぱなしかつ5回に一度
              y-=20;
      if(y==280){  // y座標が200なら(選択が一番下なら)
                y=320;     // 選択座標を一番上に
              }
      }
           ScreenFlip();
    }
	DxLib_End();
	return 0;
	}
前回載せたコードとは別で一応書いてみたのですが、実際イメージ通りにはいきませんね(笑
このコードをデバックしてみたら、処理自体は成功になっているのですが・・・・・
実行すると、.exeは動作を停止しましたと出ます。
どこがおかしいのでしょうか・・・?

Re: メイン画面の作り方について・・・・

Posted: 2011年11月27日(日) 14:09
by softya(ソフト屋)
問題はDXLIBを初期化していないからです。あとインデン(字下げ)トは正確にお願いしますね。

それと私かDixq (管理人) さんのアイデアを使って組まれることをおすすめします。
Gamen();側にメインループを持っている非常に良くないプログラムの組み方がされている気がしてなりません。

Re: メイン画面の作り方について・・・・

Posted: 2011年11月27日(日) 14:26
by ルート

コード:

//画像表示2

#include "DxLib.h"


void Gamen()
{
	int Handle00,Handle01;               //データ格納用変数
	Handle01=LoadGraph("サンプル4普通.png");  //画像のロード
	Handle00=LoadGraph("kokuban3.png"); //画像をロード

	DrawGraph(0,0,Handle00,TRUE);      //データハンドルを使って画像を描画
	DrawGraph(400,0,Handle01,TRUE);    //データハンドルを使って画像を描画
	 
	WaitKey();
	
}
gamenの中身はこんな感じなのですが・・・・・
ダメな感じでしょうか?

Re: メイン画面の作り方について・・・・

Posted: 2011年11月27日(日) 14:33
by softya(ソフト屋)
今のコードだと、Gamen()の中で WaitKey();でキー入力を待っていると戻ってくると必ずDxLib_End();を呼び出しているところですかね。
普通、 WaitKey();はデバッグ時ぐらいしか使わない関数です。
それにDxLib_End();を呼んでしまったら、もうプログラムを終わるしか無いのでタイトルに戻ることもエンディングに行くこともできません。

【追記】
もう一つ有りました。
LoadGraph()でロードしているところです。これだと何回も呼び出せません。
ロードとメイン処理は分離して下さい。ゲームプログラミングの館にも書かれていると思います。

Re: メイン画面の作り方について・・・・

Posted: 2011年11月27日(日) 14:40
by ルート
WaitKey();は画面維持のためにいるかと思ったのですが、なしでも大上ででしたね・・・・・
後、本当は、管理人さんや副管理人さんの言っている方法でやるのが一番なのかもしれませんが、
理解力が乏しくて、いまいち文章だけでは、よくわかりませんでした・・・・
すいません。

さっきの実行できなかったのは、自分の見落としでした;;

Re: メイン画面の作り方について・・・・

Posted: 2011年11月27日(日) 15:03
by softya(ソフト屋)
まずゲームメインだけ分離したコードです。まず、これが基本の形で内容を理解して下さい。
新ゲームプログラミングの館のコードをベースにしてあります。

コード:

#include "DxLib.h"

int Key[256]; // キーが押されているフレーム数を格納する

// キーの入力状態を更新する
int gpUpdateKey()
{
	char tmpKey[256]; // 現在のキーの入力状態を格納する
	GetHitKeyStateAll( tmpKey ); // 全てのキーの入力状態を得る
	for( int i=0; i<256; i++ ){ 
		if( tmpKey[i] != 0 ){ // i番のキーコードに対応するキーが押されていたら
			Key[i]++;     // 加算
		} else {              // 押されていなければ
			Key[i] = 0;   // 0にする
		}
	}
	return 0;
}

//	ゲームメインで使用する変数
int posx = 0;
int Handle;     // 画像格納用ハンドル 

//	ゲームメインの画像ロード
void gmain_load()
{
	Handle = LoadGraph( "char.png" ); // 画像のロード
}

//	ゲームメイン
int gmain()
{
	DrawGraph( posx, 100, Handle, TRUE ); //画像の描画
	posx = posx + 2; // xを2増やす
	
	return 0;
}

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
	ChangeWindowMode(TRUE), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK ); //ウィンドウモード変更と初期化と裏画面設定

	//	ゲームメインの画像ロード
	gmain_load();

	// while(裏画面を表画面に反映, メッセージ処理, 画面クリア, キーの更新)
	while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 && gpUpdateKey()==0 ){
		
		//	ゲームメイン
		gmain();
	}

	DxLib_End(); // DXライブラリ終了処理
	return 0;
}
つづきます。

Re: メイン画面の作り方について・・・・

Posted: 2011年11月27日(日) 15:16
by ルート
自分の推測ですが、
別の関数で画像をloadして、ゲームに使用する関数内で、画像を描画する感じでしょうか?

Re: メイン画面の作り方について・・・・

Posted: 2011年11月27日(日) 15:27
by softya(ソフト屋)
ロードと描画は分けなくてはいけません。毎フレーム描画は呼び出されますので分離していないと多重にロードしてしまいます。
本当は移動と描画も分離するのがセオリーですが、今回は略しています。分離したときにどう書くかは新ゲームプログラミングの館に書かれていますので読んでみてください。

Re: メイン画面の作り方について・・・・

Posted: 2011年11月27日(日) 15:44
by softya(ソフト屋)
画面の遷移を行うようにしたもの。これが理解できますか? [補足]理解というのは自分で拡張できるかって事です。
まだメニュー選択部分は作っていません。

コード:

#include "DxLib.h"

int Key[256]; // キーが押されているフレーム数を格納する

//	画面状態の定数
enum {
	STATE_TITLE,	//タイトル画面
	STATE_SELECT,	//選択画面
	STATE_MAIN,		//メイン画面
};

// キーの入力状態を更新する
int gpUpdateKey()
{
	char tmpKey[256]; // 現在のキーの入力状態を格納する
	GetHitKeyStateAll( tmpKey ); // 全てのキーの入力状態を得る
	for( int i=0; i<256; i++ ){ 
		if( tmpKey[i] != 0 ){ // i番のキーコードに対応するキーが押されていたら
			Key[i]++;     // 加算
		} else {              // 押されていなければ
			Key[i] = 0;   // 0にする
		}
	}
	return 0;
}

//-------------------------------------------------
//	タイトル画面
//-------------------------------------------------
int title()
{
	DrawString(100,100,"タイトル画面 (zをプッシュ)",GetColor(255,255,255));
	if(Key[KEY_INPUT_Z]==1) return TRUE;	//Zが押されたらTRUE
	return FALSE;	//Zが押されなければFALSE
}

//-------------------------------------------------
//	選択画面
//-------------------------------------------------
int select()
{
	//	まだわざと選択画面ではありません。
	DrawString(100,100,"選択画面 (zをプッシュ)",GetColor(255,255,255));
	if(Key[KEY_INPUT_Z]==1) return STATE_MAIN;	//Zが押されたらゲームメインへ。
	return STATE_SELECT;	//Zが押されなければF状態継続
}

//-------------------------------------------------
//	ゲームメイン系
//-------------------------------------------------

//	ゲームメインで使用する変数
int posx = 0;
int Handle;     // 画像格納用ハンドル 

//	ゲームメインの画像ロード
void gmain_load()
{
	Handle = LoadGraph( "char.png" ); // 画像のロード
}

//	ゲームメイン
int gmain()
{
	DrawString(100,100,"ゲームメイン (zをプッシュ)",GetColor(255,255,255));
	
	DrawGraph( posx, 100, Handle, TRUE ); //画像の描画
	posx = posx + 2; // xを2増やす
	if( posx > 640 ) posx = 0;	//オーバーガード
	
	if(Key[KEY_INPUT_Z]==1) return TRUE;	//Zが押されたらTRUE
	return FALSE;	//Zが押されなければFALSE
}

//-------------------------------------------------
//	メインループ
//-------------------------------------------------

//	プログラムのメイン(メインループをする所)
int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
	ChangeWindowMode(TRUE), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK ); //ウィンドウモード変更と初期化と裏画面設定

	//	ゲームメインの画像ロード
	gmain_load();

	//	現在の状態
	int state = STATE_TITLE;

	// while(裏画面を表画面に反映, メッセージ処理, 画面クリア, キーの更新)
	while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 && gpUpdateKey()==0 ){
		
		switch( state ) {
		case STATE_TITLE:		//タイトル画面
			//	タイトル
			if( title() ) {
				//	なにか押されたら選択画面へ
				state = STATE_SELECT;
			}
			break;
			
		case STATE_SELECT:		//選択画面
			//	選択画面
			state = select();
			break;
		
		case STATE_MAIN:		//メイン画面
			//	ゲームメイン
			if( gmain() ) {
				//	終了したらタイトルへ
				state = STATE_TITLE;
			}
			break;
			
		}
	}

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

Re: メイン画面の作り方について・・・・

Posted: 2011年11月27日(日) 16:38
by ルート
推測・考えとしては
処理をグループごとにまとめて
あらかじめ与えてあったそれぞれのラベル(?)をZが押されたら
stateに代入して、画面を切り替える感じでしょうか?

Re: メイン画面の作り方について・・・・

Posted: 2011年11月27日(日) 16:41
by softya(ソフト屋)
ルート さんが書きました:推測・考えとしては
処理をグループごとにまとめて
あらかじめ与えてあったそれぞれのラベル(?)をZが押されたら
stateに代入して、画面を切り替える感じでしょうか?
グループにまとめることは「ゲームプログラミングの館」でも示されていますよね?
その推測であっているかは、自分で機能拡張して確認してみてください。
enumも調べてくださいね。

Re: メイン画面の作り方について・・・・

Posted: 2011年11月27日(日) 17:13
by naohiro19
C++とBoost.msmを使えば簡単に状態遷移ができます。
boost::msm - C++でゲームプログラミング

Re: メイン画面の作り方について・・・・

Posted: 2011年11月27日(日) 18:01
by ISLe
ゲームは、initialize(初期化)、update(更新)、render(描画)、release(解放)、の4つのカテゴリに適切に処理を分けてやれば良いです。
initializeは最初に一回だけ呼ばれます。
updateは1フレームに一回呼ばれます。
renderも基本的に1フレームに一回呼ばれますが不定期に呼び出される場合もあります。
releaseは最後に一回だけ呼ばれます。
これをシーンやオブジェクトごとにグループ分けして、入れ子にしていくことでゲームプログラムは完成します。

C言語でも関数ポインタをメンバに持った構造体でリストを作れば同様に実装できます。

Re: メイン画面の作り方について・・・・

Posted: 2011年11月28日(月) 23:51
by ルート

コード:

//画像表示2
 
#include "DxLib.h"
 


void Gamen()
{



  int Handle00,Handle01;               //データ格納用変数
  Handle01=LoadGraph("サンプル4普通.png");  //画像のロード
  Handle00=LoadGraph("kokuban3.png"); //画像をロード
 
  DrawGraph(0,0,Handle00,TRUE);      //データハンドルを使って画像を描画
  DrawGraph(400,0,Handle01,TRUE);    //データハンドルを使って画像を描画
  }
ソースは前の話のとこに戻りますが、この前載せた、選択メニューから項目を選んで、仮に画像が出るように設定しているのですが、
WaitKey();を消して実行すると描画したら、すぐ消えてしまう感じなので、
描画をWaitKey();を使わずに維持するにはどのようにすればよいでしょうか?

Re: メイン画面の作り方について・・・・

Posted: 2011年11月28日(月) 23:54
by softya(ソフト屋)
ルート さんが書きました:ソースは前の話のとこに戻りますが、この前載せた、選択メニューから項目を選んで、仮に画像が出るように設定しているのですが、
WaitKey();を消して実行すると描画したら、すぐ消えてしまう感じなので、
描画をWaitKey();を使わずに維持するにはどのようにすればよいでしょうか?
その答えが私の提示したコードなのですが違いがわかりませんでしょうか?

Re: メイン画面の作り方について・・・・

Posted: 2011年12月01日(木) 15:05
by ルート

コード:

#include "DxLib.h"
 
 
//  画面状態の定数
enum {
    STATE_TITLE,    //タイトル画面
    STATE_SELECT,   //選択画面
    STATE_MAIN,     //メイン画面
};
 
// キーの入力状態を更新する
int Key[256]; // キーが押されているフレーム数を格納する

int GetHitKeyStateAll_2(int KeyStateBuf[]){
        char GetHitKeyStateAll_Key[256];
        GetHitKeyStateAll( GetHitKeyStateAll_Key );
        for(int i=0;i<256;i++){
                if(GetHitKeyStateAll_Key[i]==1) KeyStateBuf[i]++;
                else                            KeyStateBuf[i]=0;
        }
        return 0;
}

 
//-------------------------------------------------
//画像ロード
//-------------------------------------------------

//  ゲームメインで使用する変数
int posx = 0;
int Handle[5];     // 画像格納用ハンドル 

void g_load()
{
	Handle[0]= LoadGraph( "校門3.png" ); // 画像のロード
	Handle[1]= LoadGraph("window.png");  //画像のロード
	Handle[2]= LoadGraph("window1.png"); //画像のロード
	Handle[3] = LoadGraph("メニューwindow.png"); //画像のロード
}

 
//-------------------------------------------------
//  タイトル画面
//-------------------------------------------------
int title()
{

	DrawGraph( posx, 0, Handle[0], TRUE ); //画像の描画
	DrawGraph(195,110,Handle[1],TRUE);     //画像の描画
	DrawGraph(210,370,Handle[2],TRUE);     //画像の描画

	SetFontSize(25);
    DrawString(200,120,"教えてナツミさん ",GetColor(255,255,255));
	SetFontSize(18);                       //文字サイズ標準18
	DrawString(200,150,"~楽しい算数1年生~",GetColor(255,255,255));
	DrawString(230,375,"(エンターを押す)",GetColor(255,255,255));
    if(Key[KEY_INPUT_RETURN]==1) return TRUE;    //エンターが押されたらTRUE
    return FALSE;   //エンターが押されなければFALSE
}


 
//-------------------------------------------------
//メニュー画面
//-------------------------------------------------
int menu()
 {
    int y  = 300;
   
    int White = GetColor(255,255,255);
	
		  // while(裏画面を表画面に反映, メッセージ処理, 画面クリア, キーの更新)
    while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 &&!GetHitKeyStateAll_2(Key) && !Key[KEY_INPUT_ESCAPE]){

	  DrawGraph( posx, 0, Handle[0], TRUE ); //画像の描画
	  DrawGraph( 240, 280, Handle[3], TRUE ); //画像の描画
      DrawString( 260 , y   , "★"    , White ); //  矢印(選択を示す)
      DrawString( 280 , 300 , "トレーニング"  , White );
      DrawString( 280 , 320 , "予習・復習"  , White );
	  DrawString( 280 , 340 , "終了", White);

 
      // もし『トレーニング』(y=300)でボタンが押されたら
      if(y == 300 && Key[KEY_INPUT_RETURN] == 1){
          /* 初期化処理 */
          ClearDrawScreen();
        return STATE_MAIN;  //エンターが押されたらゲームメインへ。
    return STATE_SELECT;    //エンターが押されなければF状態継続

      }

      // もし『予習・復習』(y=320)でボタンが押されたら
      if(y == 320 && Key[KEY_INPUT_RETURN] == 1){
          
          ClearDrawScreen();
        return STATE_MAIN;  //エンターが押されたらゲームメインへ。
    return STATE_SELECT;    //エンターが押されなければF状態継続

      }
 
	  // もし『終了』(y=340)でボタンが押されたら
      if(y == 340 && Key[KEY_INPUT_RETURN] == 1){
		  break;
	  }                    //エンターが押されたら終了

      

     
      if( Key[KEY_INPUT_DOWN]==1 || ( Key[KEY_INPUT_UP]%5==0 && Key[KEY_INPUT_UP]>30)){
           // たった今押したか、30カウンター以上押しっぱなしかつ5回に一度
              y+=20;
              if(y==360){  // y座標が360なら(選択が一番下なら)
                y=300;     // 選択座標を一番上に
              }
      }
      if( Key[KEY_INPUT_UP]==1 || ( Key[KEY_INPUT_DOWN]%5==0 && Key[KEY_INPUT_DOWN]>30)){
           // たった今押したか、30カウンター以上押しっぱなしかつ5回に一度
              y-=20;
      if(y==280){  // y座標が200なら(選択が一番下なら)
                y=320;     // 選択座標を一番上に
              }
      }
	}
	DxLib_End();
	return 0;
}

         


//-------------------------------------------------
//  ゲームメイン系
//-------------------------------------------------
 

//  ゲームメイン
int gmain()
{
    DrawString(100,100,"ゲームメイン (エンターを押す)",GetColor(255,255,255));
    
   
    
    if(Key[KEY_INPUT_RETURN]==1) return TRUE;    //エンターが押されたらTRUE
    return FALSE;   //エンターが押されなければFALSE
}
 
//-------------------------------------------------
//  メインループ
//-------------------------------------------------
 
//  プログラムのメイン(メインループをする所)
int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
    ChangeWindowMode(TRUE), SetWindowSize(1000,600), DxLib_Init(), 
SetDrawScreen( DX_SCREEN_BACK ); //ウィンドウモード変更、サイズ変更と初期化と裏画面設定
 
    g_load();
 
    //  現在の状態
    int state = STATE_TITLE;
 
    // while(裏画面を表画面に反映, メッセージ処理, 画面クリア, キーの更新)
    while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 
&&!GetHitKeyStateAll_2(Key) && !Key[KEY_INPUT_ESCAPE]){
        
        switch( state ) {
        case STATE_TITLE:       //タイトル画面
            //  タイトル
            if( title() ) {
                //  なにか押されたら選択画面へ
                state = STATE_SELECT;
            }
            break;
            
        case STATE_SELECT:      //選択画面
            //  選択画面
            state = menu();
            break;
        
        case STATE_MAIN:        //メイン画面
            //  ゲームメイン
            if( gmain() ) {
                //  終了したらタイトルへ
                state = STATE_TITLE;
            }
            break;
            
        }
    }
 
    DxLib_End(); // DXライブラリ終了処理
    return 0;
}
なんとかできました。
ありがとうございます。

また、別の案件で、トピックを立てさせていただきますその時は、よろしく、お願いします。

Re: メイン画面の作り方について・・・・

Posted: 2011年12月01日(木) 15:12
by softya(ソフト屋)
メニュー画面処理にもう一つのwhileループがあるので良くない書き方です。
while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 &&!GetHitKeyStateAll_2(Key) && !Key[KEY_INPUT_ESCAPE]){
はプログラム中に1つになるように工夫して下さい。

ちなみに、こんな感じで書けば良いです。

コード:

#include "DxLib.h"

int Key[256]; // キーが押されているフレーム数を格納する

//	画面状態の定数
enum {
	STATE_TITLE,	//タイトル画面
	STATE_SELECT,	//選択画面
	STATE_MAIN,		//メイン画面
	STATE_END,		//ゲーム終了
};

// キーの入力状態を更新する
int gpUpdateKey()
{
	char tmpKey[256]; // 現在のキーの入力状態を格納する
	GetHitKeyStateAll( tmpKey ); // 全てのキーの入力状態を得る
	for( int i=0; i<256; i++ ){ 
		if( tmpKey[i] != 0 ){ // i番のキーコードに対応するキーが押されていたら
			Key[i]++;	  // 加算
		} else {			  // 押されていなければ
			Key[i] = 0;   // 0にする
		}
	}
	return 0;
}

//-------------------------------------------------
//	タイトル画面
//-------------------------------------------------
int title()
{
	DrawString(100,100,"タイトル画面 (zをプッシュ)",GetColor(255,255,255));
	if(Key[KEY_INPUT_Z]==1) return TRUE;	//Zが押されたらTRUE
	return FALSE;	//Zが押されなければFALSE
}

//-------------------------------------------------
//	選択画面の関連
//-------------------------------------------------

// メニュー項目の表示に必要な構造体を用意する
typedef struct{
		int x, y;		// 座標格納用変数
		char name[128]; // 項目名格納用変数
} MenuElement_t ;

//	選択画面
int select()
{
	//	メニュー項目要素を作る
	//	staticは状態を常に覚えておくため。単なるローカル変数は毎回初期化される。
	#define SELECT_NUM	3
	static MenuElement_t MenuElement[SELECT_NUM]={
				{  80, 150, "ゲームスタート" }, // タグの中身の順番で格納される。xに80が、yに100が、nameに"ゲームスタート"が
				{ 100, 200, "タイトルに戻る" },
				{ 100, 250, "ゲーム終了" },
		};
	static int SelectNum = 0; // 現在の選択番号。
	
	
	// 計算フェーズ 
	if( Key[ KEY_INPUT_DOWN ] == 1 ){ // 下キーが押された瞬間だけ処理
		SelectNum = ( SelectNum + 1 ) % SELECT_NUM; // 現在の選択項目を一つ下にずらす(ループする)
	}
	if( Key[ KEY_INPUT_UP ] == 1 ){ // 上キーが押された瞬間だけ処理
		SelectNum = ( SelectNum + SELECT_NUM - 1 ) % SELECT_NUM; // 現在の選択項目を一つ上にずらす(逆ループする)
	}
 	if( Key[ KEY_INPUT_DOWN ] == 1 || Key[ KEY_INPUT_UP ] == 1 ){ // 下キーか、上キーが押された瞬間
		for( int i=0; i<SELECT_NUM; i++ ){			   // メニュー項目数である5個ループ処理
			if( i == SelectNum ){		   // 今処理しているのが、選択番号と同じ要素なら
				MenuElement[i].x = 80; // 座標を80にする
			} else {					   // 今処理しているのが、選択番号以外なら
				MenuElement[i].x = 100;// 座標を100にする
			}
		}
	}
 
	// 描画フェーズ
	DrawString(60,100,"選択画面 (上下で選んで、zをプッシュ)",GetColor(255,255,255));
	for( int i=0; i<SELECT_NUM; i++ ){ // メニュー項目を描画
		DrawFormatString( MenuElement[i].x, MenuElement[i].y, GetColor(0,255,255), MenuElement[i].name );
	}
	
	//	Zボタンで選択
	if(Key[KEY_INPUT_Z]==1) {
		switch( SelectNum ) {
		case 0://ゲームスタート
			return STATE_MAIN;
			
		case 1://タイトルに戻る
			return STATE_TITLE;
			
		case 2://ゲーム終了
			return STATE_END;
		
		}
	}
	
	return STATE_SELECT;	//状態継続
}

//-------------------------------------------------
//	ゲームメイン系
//-------------------------------------------------

//	ゲームメインで使用する変数
int posx = 0;
int Handle; 	// 画像格納用ハンドル 

//	ゲームメインの画像ロード
void gmain_load()
{
	Handle = LoadGraph( "char.png" ); // 画像のロード
}

//	ゲームメイン
int gmain()
{
	int local;
	int *pl = &local;

	DrawString(100,100,"ゲームメイン (zをプッシュ)",GetColor(255,255,255));
	
	DrawGraph( posx, 100, Handle, TRUE ); //画像の描画
	posx = posx + 2; // xを2増やす
	if( posx > 640 ) posx = 0;	//オーバーガード
	
	if(Key[KEY_INPUT_Z]==1) return TRUE;	//Zが押されたらTRUE
	return FALSE;	//Zが押されなければFALSE
}

//-------------------------------------------------
//	メインループ
//-------------------------------------------------

//	プログラムのメイン(メインループをする所)
int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
	ChangeWindowMode(TRUE), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK ); //ウィンドウモード変更と初期化と裏画面設定

	//	ゲームメインの画像ロード
	gmain_load();

	//	現在の状態
	int state = STATE_TITLE;

	// while(裏画面を表画面に反映, メッセージ処理, 画面クリア, キーの更新)
	while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 && gpUpdateKey()==0 && (state!=STATE_END)){
		
		switch( state ) {
		case STATE_TITLE:		//タイトル画面
			//	タイトル
			if( title() ) {
				//	なにか押されたら選択画面へ
				state = STATE_SELECT;
			}
			break;
			
		case STATE_SELECT:		//選択画面
			//	選択画面
			state = select();//戻りが画面状態
			break;
		
		case STATE_MAIN:		//メイン画面
			//	ゲームメイン
			if( gmain() ) {
				//	終了したらタイトルへ
				state = STATE_TITLE;
			}
			break;
			
		}
	}

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