ページ 11

選択肢の次の選択肢

Posted: 2014年12月22日(月) 19:37
by しろうと!
お世話になっております。今回は選択肢関係の質問をさせていただきます。
現在カスタマイズ画面で機体のパーツをカスタマイズ出来るようなSTGを作っているのですが、パーツを選択するところでつまずいています。
まず以下が現在組んでいるカスタマイズ画面(Custom.cpp)のプログラムです

コード:

#include "Custom.h"
#include "DxLib.h"
#include "Keyboard.h"

Custom::Custom(ISceneChanger* changer) : BaseScene(changer) {
}

const static int MAIN_X = 20;
const static int SUB_X = 150;
const static int BOMB_X = 300;

typedef enum{
	eCustom_Main,
	eCustom_Sub,
	eCustom_Bomb,

	eCustom_Num,
}eCustom;

typedef enum{
	eMainP_Rapid,
	eMainP_Power,
	eMainP_Junk,
	eMainP_Back,

	eMainP_Num,
}eMainP;

static int NowSelect = eCustom_Main;
static int NowSelectMP = eMainP_Rapid;

int mWeapon_flag =0;
int sWeapon_flag =0;
int bWeapon_flag =0;

int RaipdM_flag = 0;
int PowerM_flag = 0;
int JunkM_flag = 0;

//初期化
void Custom::Initialize(){

}

//更新
void Custom::Update(){
   if(CheckHitKey(KEY_INPUT_G)!=0){ //Gキーが押されていたら
		 mSceneChanger->ChangeScene(eScene_Garage);//シーンをガレージに変更
    }
	if(Keyboard_Get(KEY_INPUT_RIGHT)==1){
		NowSelect=(NowSelect+1)%eCustom_Num;
	}
	if(Keyboard_Get(KEY_INPUT_LEFT)==1){//右キーが押されていたら
        NowSelect = (NowSelect+(eCustom_Num-1))%eCustom_Num;//選択状態を一つ上げる
    }
    if(Keyboard_Get(KEY_INPUT_RETURN)==1){//エンターキーが押されたら
        switch(NowSelect){//現在選択中の状態によって処理を分岐
        case eCustom_Main://メインウェポン選択中なら
			if(mWeapon_flag==0){ //mWeaponフラグが0のとき
			mWeapon_flag = 1; //mWeaponフラグを1にする
			}
			break;
        case eCustom_Sub://サブウウェポン選択中なら
           
            break;
		case eCustom_Bomb://ボムウェポン選択中なら
			
            break;
        }
		
    }

	if(mWeapon_flag==1){
///メイン武器フラグが1のときの動作///
			if(Keyboard_Get(KEY_INPUT_DOWN)==1){
				NowSelectMP =(NowSelectMP + 1)%eMainP_Num;
		}
		if(Keyboard_Get(KEY_INPUT_UP)==1){//上キーが押されていたら
				NowSelectMP = (NowSelectMP + (eMainP_Num-1))%eMainP_Num;//選択状態を一つ上げる
		}
		 if(Keyboard_Get(KEY_INPUT_RETURN)==1){//エンターキーが押されたら
        switch(NowSelectMP){//現在選択中の状態によって処理を分岐
		case eMainP_Rapid://速射武器選択中なら
			RaipdM_flag = 1;
			break;
		case eMainP_Power://高威力武器選択中なら
           
            break;
		case eMainP_Junk://ジャンク武器選択中なら
			
            break;
		case eMainP_Back:
			mWeapon_flag=0;
			break;
        }
		
    }
///動作ここまで///
		}
}

//描画
void Custom::Draw(){
    BaseScene::Draw();//親クラスの描画メソッドを呼ぶ
    DrawString(0, 0,"カスタマイズ画面です。",GetColor(255,255,255));
    DrawString(0,20,"Escキーを押すとメニュー画面に戻ります。",GetColor(255,255,255));
	DrawString(0,40,"Gキーを押すとガレージ画面に切り替わります。",GetColor(255,255,255));

	DrawString(0,    80,  "SELECT PARTS",GetColor(255,255,255));
	DrawString(MAIN_X, 100,  "メイン武装",GetColor(255,255,255));
    DrawString(SUB_X,  100,	 "サブ武装",GetColor(255,255,255));
	DrawString(BOMB_X, 100,	 "爆装",GetColor(255,255,255));

	
	if(mWeapon_flag==1){
		DrawBox(MAIN_X-20,120,301,301,GetColor(255,0,0),TRUE);
		DrawString( MAIN_X , 130 , "WEAPON-A : 速射性の高い武器です",GetColor(255,255,255)) ;
		DrawString( MAIN_X , 150 , "WEAPON-B : 威力の高い武器です",GetColor(255,255,255)) ;
		DrawString( MAIN_X , 170 , "WEAPON-C : 産廃武器です",GetColor(255,255,255)) ;
		DrawString( MAIN_X , 280 , "カテゴリー選択に戻ります",GetColor(255,255,255)) ;
	}

	if(RaipdM_flag==1){
		DrawString(200,    300,  "PARTS SELECTED",GetColor(255,255,255));
	}

	int x = 0;

	 switch(NowSelect){//現在の選択状態に従って処理を分岐
    case eCustom_Main://メイン武装選択中なら
		x = MAIN_X;    //メインの座標を格納
        break;

	case eCustom_Sub://サブ武装選択中なら
		x = SUB_X;    //サブの座標を格納
        break;

	case eCustom_Bomb://爆装選択中なら
		x = BOMB_X;	//ボムの座標を格納
		break;
    }
    DrawString(x-20, 100, "■", GetColor(255,255,255));
	

}

とりあえず基盤を作ろうということでパーツの反映やパーツが増えたときの処理などはまだ考えておらず、今のところ

・メイン武装を選択したらパーツを選択するためのウィンドウが出てくる
・ウィンドウが出てきた後に一番上の選択肢でENTERを押すと画面に「PARTS SELECTED」と表示される
・「カテゴリー選択に戻ります」でENTERを押すと再び「メイン武装」「サブ武装」「爆装」の選択肢に戻る

というのを行えるようにしたいのですが、現在

・「メイン武装」でENTARを押すと選択ウィンドウと一緒に「PARTS SELECTED」が表示されてしまう
・「カテゴリー選択に戻ります」でENTERを押すとパーツ選択ウィンドウは消えるが、「PARTS SELECTED」が消えない
・もう一度「メイン武装」でENTERを押しても選択ウィンドウが出ない

といった問題が出ています。
そこで質問なのですが、これらの問題の解決法としてはどのようなものがあるのでしょうか?
ちなみに自分はC++を学び始めて8ヶ月程度で、ポインタや継承に関してはどういうものなのかは分かってはいるが、どういうときに使うのがいいかはよくわかっていないというのが現状です。
開発環境はWindows7 VisualStudio2012となっています。
是非ともご助力の程よろしくお願いします。

Re: 選択肢の次の選択肢

Posted: 2014年12月24日(水) 19:18
by usao
何が起こっているかを把握するために,
実際に処理がどのように流れていくのかを追っていくと良いと思います.
デバッガなどを使ってもいいですが,
コードも長くないし,注視すべき変数はいくつかのフラグしかないので,紙と鉛筆とかでも容易だと思います.

最初,mWeapon_flagの値は0です
→Update()に入って,そのときにXXXボタンがおされていたら…
→再度Update()に入って,今度は…

という感じで,見ていくと良いでしょう.
思い込みによる読み間違いを避けるために,自分がインタプリタになったつもりで,記述されているコードに忠実に.




>・「メイン武装」でENTARを押すと選択ウィンドウと一緒に「PARTS SELECTED」が表示されてしまう

これは,「キーが押されているとき」という条件に合致する処理が一度に処理されてしまうからではないでしょうか.
ENTER押下時に60行目で mWeapon_flag = 1; になったとき,
処理はそのまま 73行目に到達してifの条件を満たし,81行目あたりの処理が走るのではないでしょうか.
また,これを避けるために,たとえば,60行目の次に return; とか書いてUpdate()を抜けるようにしたとしても,
Keyboard_Get()という関数の動作次第では
次回Update()に処理が来たときに,まださきほどの ENTERキーが押されたままだったらどうなるのか? とかいう問題もあるかもしれませんね.

>・「カテゴリー選択に戻ります」でENTERを押すとパーツ選択ウィンドウは消えるが、「PARTS SELECTED」が消えない
>・もう一度「メイン武装」でENTERを押しても選択ウィンドウが出ない

「フラグの値を1にする」という処理がありますが,それを0に戻す処理が足りないのではないでしょうか.

Re: 選択肢の次の選択肢

Posted: 2014年12月24日(水) 23:55
by しろうと!
>>usaoさん
return;の追加というのは盲点でした……!
試しに足りなかったフラグの値を0にする処理と61行目にreturn;を追加してみたら、ちゃんとウィンドウが表示された後にENTERをもう一回押すことで「PARTS SELECTED」が表示されるようになりました!!
お陰様で問題が解決したと同時に、returnの活用法を再認識することができました!
アドバイスありがとうございます!

あとKeyboard_Get()ですが、中身はゲームプログラミングの館さんで利用されているKeyboardクラスと同じです

Re: 選択肢の次の選択肢

Posted: 2014年12月25日(木) 11:48
by usao
>returnの活用法

というかなんというか…

「現在の状況がAであるときにBキーを押したら処理Cを行う (←状況がAでないときはBキーを押しても処理Cは行われない)」
というような条件の管理がうまくできていないというか書けてないというかそんな雰囲気を感じます.なんとなくですが.

例えば Custom::Updata() の先頭(47行目)では,
「Gキーが押されたら」→「シーンをガレージに変更」ということになっています.
この書き方だと,現在の状態がどのような状況でもとりあえずGキーを押したら必ず48行目の処理が走るわけですが大丈夫なのでしょうか?
同様に50行目や53行目等も,とにかくどんなタイミングでもキーを押下するとNowSelectという変数の値が変わってしまいそうです.
(これらが実際にどんな働きをするのか私にはわかりませんから,実際には何も問題はないのかもしれませんが,
 例えば "PARTS SELECTED" という表示をしている状況下で Gキーや左右キーを押すと何かしら処理が行われるのは意図した動作でしょうか?)

Re: 選択肢の次の選択肢

Posted: 2014年12月25日(木) 23:44
by しろうと!
>>usaoさん
>条件の管理がうまくできていない
>"PARTS SELECTED" という表示をしている状況下で Gキーや左右キーを押すと何かしら処理が行われる

そうですね……実際どういった処理を行なえばいいのかわからなかったのと、そのままでも大した問題にはならないだろうということでその辺り(処理B選択中は処理Aを行なえないようにする処理)は放置している状態ですね。

出来ればどうやればそういった処理が出来るようになるか助言をいただきたいところですが、それはそれで個別にスレッドを立てて聞いたほうがいいのでしょうかね?

Re: 選択肢の次の選択肢

Posted: 2014年12月26日(金) 09:52
by usao
簡単な話を例にとると,
[hr]
(1)最初の状態
{[A]Test1, Test2}とかいう2項目だけのメニューっぽいのが表示される.
Aキーを押されたら状態(2-1)へ遷移.
Bキーを押されたら状態(2-2)へ遷移.

(2-1) (1)でTest1が選択されたときの状態
さびしく"Test1"とだけ表示される.
ENTERキーが押されると,(1)の状態に戻る.

(2-2) (1)でTest2が選択されたときの状態
さびしく"Test2"とだけ表示される.
こっちは スペースキーが押されたら何故か (2-1) の状態にいく.
[hr]
とかいうのを作るとしたら,これをそのままコードに置き換えると,以下のようになる.
まず,現状態が,(1),(2-1),(2-2) のどれなのかによって,やるべきことが違うのだから,そのことを書く.

コード:

enum STATE{  FIRST_STATE, TEST1_STATE, TEST2_STATE };  //状態を表す値
STATE CurrentState = FIRST_STATE;  //現在の状態.初期値は「最初の状態」

void Update()
{
  switch( CurrentState )  //現在の状態が何かによって,行うべき処理が違うので分岐する.
  {
  case FIRST_STATE:  FirstState_Update();  break;
  case TEST1_STATE:  TestState_Update();  break;
  case TEST2_STATE:  Test2State_Update();  break;
  default: break;
  }
}

void Draw()
{
  //描画内容も,状態毎に異なるから,Update()と同じ感じに分岐処理を書く.
}
あとは,個々の状態での処理を書く.

コード:

//「最初の状態」での処理
void FirstState_Update()
{
  if( Aキーがおされた )CurrentState = TEST1_STATE;
  else if( Bキーがおされた )CurrentState =TEST2_STATE;
}

//他は省略
(これが良い書き方かどうかは別にして)例えばこんな感じに書けば,
現状態が「最初の状態」であるときに,ENTERキーやスペースキーを押されても問題ないですよね.

Re: 選択肢の次の選択肢

Posted: 2014年12月31日(水) 01:29
by しろうと!
>>usaoさん
なるほど……そうやって処理を分ける方法があるんですね。勉強になりました!