switch文について質問なのですが・・・

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
jun_3453

switch文について質問なのですが・・・

#1

投稿記事 by jun_3453 » 8年前

とりあえず見てください。

main.cpp

コード:

#include "Dxlib.h"
#include "title.h"
#include "select.h"
#include "keyboard.h"

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
        ChangeWindowMode(TRUE), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK );
		SetMainWindowText("ああああ") ; //タイトル

		int game = 0 ;

		//初期化
		Title_Initialize();
		

        while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 ){

			Keyboard_Update(); //キーボード取得

			switch(game){

			case 0 : //タイトル
				Title_menu();
				Title_Draw();
				Title_syori(); //ここからcase 10 に飛ばしたい//
			break;



			case 10 : //セレクト
				Select_top();
			break;


			}

			if(Keyboard_Get(KEY_INPUT_ESCAPE)==1)break;
			ScreenFlip();//裏画面を表画面に反映
		}

		DxLib_End();
        return 0;
} 
title.cpp

コード:

#include "DxLib.h"
#include "title.h"
#include "keyboard.h"

//グローバル変数等 (static int ~~)
static int SelectNum = 0; // 現在の選択番号

//初期化 (イメージのロード、関数の初期化等)
void Title_Initialize(){

}


//計算 (タイトルの動作処理)
void Title_menu(){

		// メニュー項目要素	
	typedef struct{
        int x, y;       // 座標格納用変数
        char name[128]; // 項目名格納用変数
		} MenuElement_t ;



  // メニュー項目要素を5つ作る
        MenuElement_t MenuElement[3]={
                { 280, 300, "一人プレイ" },
                { 280, 350, "二人プレイ" },
                { 280, 400, "遊び方" },
        };
        
	
	if( Keyboard_Get ( KEY_INPUT_DOWN ) == 1 ){ // 下キーが押された瞬間だけ処理
    SelectNum = ( SelectNum + 1 ) % 3; // 現在の選択項目を一つ下にずらす(ループする)
    }
	if( Keyboard_Get ( KEY_INPUT_UP ) == 1 ){ // 上キーが押された瞬間だけ処理
	SelectNum = ( SelectNum + 2 ) % 3; // 現在の選択項目を一つ上にずらす(逆ループする)
	}

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

		}

}



//処理
void Title_syori(){


	if( SelectNum == 0 ){
		if(Keyboard_Get ( KEY_INPUT_SPACE ) > 0){
			DrawString( 250 , 240 - 32 , "現在開発中" , GetColor( 255 , 255 , 255 ) );
		}
	}

	if( SelectNum == 1 ){
		if(Keyboard_Get ( KEY_INPUT_SPACE ) > 0){
                
                // ここに main.cpp の case 10 にいく構文を入れたい //


		}
	}
	
	if( SelectNum == 2 ){
		if(Keyboard_Get ( KEY_INPUT_SPACE ) > 0){
			DrawGraph(100,100,LoadGraph("picture/説明.bmp") , TRUE );
		}
	}

}


//描画 (DrawGraph等)
void Title_Draw(){
	DrawString( 250 , 180 , "ああああ" , GetColor( 255 , 255 , 255 ) );



}


//終了 (グラフィック削除等)
void Title_Finalize(){
}


title.cppのとある文から、main.cppのcase 0をcase 10に飛ばすということはできるのでしょうか?

あと、こうするとわかりやすいよというアドバイスなどを教えてくれると嬉しいです。

nil
記事: 428
登録日時: 8年前

Re: switch文について質問なのですが・・・

#2

投稿記事 by nil » 8年前

コード:

25: game = Title_syori();

としておき

コード:

//処理
int Title_syori(){
 
 
    //略
 
    if( SelectNum == 1 ){
        if(Keyboard_Get ( KEY_INPUT_SPACE ) > 0){
                
                return 10
        }
    }
    //略
    return 0
}



こんなのはどうでしょう?

アバター
へろりくしょん
記事: 92
登録日時: 10年前
住所: 福岡

Re: switch文について質問なのですが・・・

#3

投稿記事 by へろりくしょん » 8年前

質問の内容を額面通りに受け取るならば、setjmp() longjmp() を使えば可能ですが、普通はしません。
基本的に、関数の途中から別の関数の途中に処理を移すのは、禁じ手中の禁じ手だと心得てください。

原則として、1関数は1機能、そして引数と戻り値を窓口に対話します。
関数には引数にもしもしと話しかけ、関数は戻り値でお返事します。 そんなイメージです。
もっと構造化を意識してください。

今回のコードでは、かなり最悪な形でグローバル変数が使われてます。
関数 Title_syori() の中身の行方は、グローバル変数 SeectNum の値に依存します。
その上で他の関数の途中にジャンプしてしまうと、このままプログラムが大きくなっていくにつれ、処理を追うだけでも至難の業でしょう。
いわゆるスパゲッティ化って奴です。


各関数の振る舞い・意味を考え直してみてください。

Title_syori() は SelectNum の値に依存します。 SelectNum の値を決定するのは Title_menu() 関数です。


ですから、今回の場合は、

新しく選択されたメニュー番号 Title_menu();
void Title_syori(処理したいメニュー番号);

と意味づけるのが王道だと思いますよ。
ただ、Title_syori() 関数が、すべてのメニューの状態を処理するというのはいささかどうかと思いますが。

Title_menu() 関数が返した番号によって処理を振り分けるわけです。

jun_3453

Re: switch文について質問なのですが・・・

#4

投稿記事 by jun_3453 » 8年前

コード:

    MenuElement_t MenuElement[3]={
                { 280, 300, "一人プレイ" },
                { 280, 350, "二人プレイ" },
                { 280, 400, "遊び方" },
        };
をstruct.hに収め、
Title_syori() をメニューの数だけ分割してみました。


へろりさんの

新しく選択されたメニュー番号 Title_menu();
void Title_syori(処理したいメニュー番号);

というのは、

case 0 : //タイトル
Title_menu();
Title_Draw();

Title_hitori();
Title_hutari();
Title_setsumei();
break;

の()内に番号を入れればいいということではなく、考え方のことですよね?


とりあえず、
「関数の途中から別の関数の途中に処理を移す」
という考えはやめにしようと思っていますが、
http://dixq.net/g/37.html
などの説明を見ても未だにプログラムが組めません;;

理解が乏しくてすみません・・・


コード:

#include "Dxlib.h"
#include "title.h"
#include "select.h"
#include "keyboard.h"
#include "func.h"

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
        ChangeWindowMode(TRUE), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK );
		SetMainWindowText("ああああ") ; //タイトル
 
		int game = 0 ;

		
		//初期化
		Title_Initialize();
		

        while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 ){

			Keyboard_Update(); //キーボード取得

			switch(game){

			case 0 : //タイトル
				Title_menu();
				Title_Draw();

				Title_hitori();
				Title_hutari();
				Title_setsumei();
			break;


			case 10 : //一人プレイ
			break;


			case 20 : //二人プレイ
				Select_top();
			break;


			}


			if(Keyboard_Get(KEY_INPUT_ESCAPE)==1)break;
			ScreenFlip();//裏画面を表画面に反映
		}

		DxLib_End();
        return 0;
} 

コード:

#include "DxLib.h"
#include "title.h"
#include "keyboard.h"
#include "func.h"

//グローバル変数等 (static int ~~)
static int SelectNum = 0; // 現在の選択番号

//初期化 (イメージのロード、関数の初期化等)
void Title_Initialize(){

}


//計算 (タイトルの動作処理)
void Title_menu(){


  // メニュー項目要素を5つ作る
        MenuElement_t MenuElement[3]={
                { 280, 300, "一人プレイ" },
                { 280, 350, "二人プレイ" },
                { 280, 400, "遊び方" },
        };
        
	
	if( Keyboard_Get ( KEY_INPUT_DOWN ) == 1 ){ // 下キーが押された瞬間だけ処理
    SelectNum = ( SelectNum + 1 ) % 3; // 現在の選択項目を一つ下にずらす(ループする)
    }
	if( Keyboard_Get ( KEY_INPUT_UP ) == 1 ){ // 上キーが押された瞬間だけ処理
	SelectNum = ( SelectNum + 2 ) % 3; // 現在の選択項目を一つ上にずらす(逆ループする)
	}


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

		}

}



//処理系列
void Title_hitori(){
	if( SelectNum == 0 ){
		if(Keyboard_Get ( KEY_INPUT_SPACE ) > 0){
			DrawString( 250 , 240 - 32 , "現在開発中" , GetColor( 255 , 255 , 255 ) );
		}
	}
}


void Title_hutari(){
	if( SelectNum == 1 ){
		if(Keyboard_Get ( KEY_INPUT_SPACE ) > 0){
			
		}
	}
}


void Title_setsumei(){
	if( SelectNum == 2 ){
		if(Keyboard_Get ( KEY_INPUT_SPACE ) > 0){
			DrawGraph(100,100,LoadGraph("picture/説明.bmp") , TRUE );
		}
	}
}



//描画 (DrawGraph等)
void Title_Draw(){
	DrawString( 250 , 180 , "ああああ" , GetColor( 255 , 255 , 255 ) );


}


//終了 (グラフィック削除等)
void Title_Finalize(){
}




アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 10年前
住所: 東海地方
連絡を取る:

Re: switch文について質問なのですが・・・

#5

投稿記事 by softya(ソフト屋) » 8年前

ざっと見させて頂きましたが処理の分け方が適当なのでよろしく無いです。
Draw以外の所にDrawString系が入っていますので、これが良くないです。全部Drawに集めましょう。状態を持つ構造体を作れば管理できると思います。
処理も入り口は一つで良いはずですので、WinMainからはTitleMenu_Update()とTitleMenu_Draw()だけを呼び出すようにしてみてはどうでしょうか?
TitleMenu_Update()の中から必要に応じてファイル内の関数を呼び出すようにします。

あと戻り値を活用していません。WinMainのgameに影響を与えたいならTitleMenu_Update()から切り替えるための情報を戻り値で返せば良いと思います。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
へろりくしょん
記事: 92
登録日時: 10年前
住所: 福岡

Re: switch文について質問なのですが・・・

#6

投稿記事 by へろりくしょん » 8年前

jun_3453 さんが書きました: へろりさんの

新しく選択されたメニュー番号 Title_menu();
void Title_syori(処理したいメニュー番号);

というのは、

case 0 : //タイトル
Title_menu();
Title_Draw();

Title_hitori();
Title_hutari();
Title_setsumei();
break;

の()内に番号を入れればいいということではなく、考え方のことですよね?
いえ、Title_syori() 関数の場合は、() 内に引数として番号を渡しましょう。 ということです。
Title_menu() 関数では、選択された番号を戻り値として返しましょう。 ということです。


龍神録の館のコードをざっくり読むとシンプルに次のような感じになってますね。

コード:

switch(ゲーム状態)
{
	case 状態0:	//オープニング
		Zキーが押されたら、状態1に移動
	break;
	case 状態1:	//メニュー
		Xキーが押されたら、状態2に移動
	break;
	case 状態2:	//ダンジョン
		Cキーが押されたら、状態3に移動
	break;
	case 状態3:	//戦闘画面
		Vキーが押されたら、状態4に移動
	break;
	case 状態4:	//エンディング
		Bキーが押されたら、状態5に移動
	break;
	default:		//上記以外の状態
		終了する
}
ゲーム常態がグローバル変数で管理されてるところがいささかアレなのですが、
構造自体は、非常にシンプルです。

ここで重要なのは、このゲーム常態を誰が管理するか。 です。
龍神録の場合は、メインファイルに静的なグローバル変数なので、管理者はメインファイルです。


jun_3453 さんの例では、ゲーム常態 game 自体は、WinMain() 関数が管理しているものの、
次の常態に移るための関数 Title_syori()が、別のファイル title.cpp に記述されていました。
管理者を飛び越してますね。

ですから、引数・戻り値を活用し、必要なデータはすべて管理者の手元に戻るようにしましょう。 ということです。



そして、2度目のコードなのですが。

Title_hitori() Title_hutari() Title_setsumei() 関数は、main.cpp から呼び出すべきではありません。

メニュー常態に関するあれこれは、title.cpp の管轄下にありますね。
変数 SelectNum も title.cpp の管理下にあります。
であるなら、SelectNum に依存する Title_hitori() Title_hutari() Title_setsumei() の各関数も、
やっぱり title.cpp 内部から呼び出されるべきです。



だれが、なにを、所持して管理するか。 もう少し構造化を意識すると大変幸せになれますよ。

たかぎ
記事: 328
登録日時: 10年前
住所: 大阪
連絡を取る:

Re: switch文について質問なのですが・・・

#7

投稿記事 by たかぎ » 8年前

jun_3453 さんが書きました:title.cppのとある文から、main.cppのcase 0をcase 10に飛ばすということはできるのでしょうか?
とりあえず上の質問にだけ答えます。
結論から言えば可能です。
もう少し簡単なコードで説明すると、次のようにすれば実現できます。

main.cpp

コード:

#include <iostream>

void sub();

int main()
try
{
    int x;
    std::cin >> x;

    switch (x)
    {
    case 0:
        sub();
        break;
    case 10:
case_10:
        std::cout << "case 10" << std::endl;
        break;
    }
}
catch (...)
{
    goto case_10;
}
sub.cpp

コード:

void sub()
{
    throw 0;
}
例外処理を使って関数の途中から別の関数の途中に処理を移すのは、C++では普通に行われることですので禁じ手ではありません。
ただし、例外処理は関数間を行き来することが目的の機能ではありませんので、そこのところは注意してください。
また、C++ではsetjmpやlongjmpは使ってはいけません(特殊な状況では使えないこともありませんが、全面的に禁止した方が無難です)。

閉鎖

“C言語何でも質問掲示板” へ戻る