ページ 11

DxLibのマウスの挙動について

Posted: 2013年12月01日(日) 20:16
by kgtekito
はじめまして。C++は初心者ですがよろしくお願いします。
http://dixq.net/g/sp_06.html を見ながらメニューが作れたので、どうしたらもっと使いまわせる仕様になるのかなと思い、
キーボードとマウスの状態についてのクラスを作成したところ、期待した通りの動作をしてくれませんでした。
http://dixq.net/forum/viewtopic.php?f=3&t=11602
ここにあったように、

コード:

int Keys::updateKey() {
	// 現在のキーの入力状態を格納する
	GetHitKeyStateAll(tmpKey); // 全てのキーの入力状態を得る
	nextFrame(); //マウスの入力状態をアップデートする
	for (int i = 0; i<256; i++){
		if (tmpKey[i] != 0){ // i番のキーコードに対応するキーが押されていたら
			Key[i]++;     // 加算
		}
		else {              // 押されていなければ
			Key[i] = 0;   // 0にする
		}
	}
	return 0;
}

void Keys::nextFrame() { //フレーム遷移
	int x, y;
	prevMouse = mouse;
	mouse = GetMouseInput();
	if (mouse ^ prevMouse & mouse & MOUSE_INPUT_LEFT){
		status = OFF_ON;
	}
	if (mouse ^ prevMouse & prevMouse & MOUSE_INPUT_LEFT){
		status = ON_OFF;
	}
	GetMousePoint(&x, &y);

	coord.real(x);
	coord.imag(y);
}
として、

コード:

int Keys::getMouseWithStatus(buttonState x) {
	
	switch (x){
	case OFF_ON: 
		if (status == OFF_ON )
		{
			return 1;
		}
	case ON_OFF:
		if (status == ON_OFF )
		{
			return 1;
		}
	default :
		return 0;
	}
	
}

とすれば、たとえば

コード:

boost::shared_ptr<Keys> key(new Keys);
	//boost::shared_ptr<Keys> key = Keys::CreateInstance();

	key->updateKey();
//	key->getKeyWithStatus(MOUSE_INPUT_LEFT, ON_OFF);
	if (key->mouseOnArea(225, 425, 150, 196))
	{

		menunum = 1;
		if (key->getMouseWithStatus(ON_OFF))
		{
			mSceneChanger->ChangeScene(eScene_ModeSelect);
		}
	}
}
のようにした時に、特定領域内でマウスを一度押してから離した瞬間にシーンを変更してくれると期待したのですが、うまく行きませんでした。ご教示いただけると幸いです、

Re: DxLibのマウスの挙動について

Posted: 2013年12月01日(日) 21:05
by だんごさん
C++の動きがよくわかりませんが、おそらくupdateKeyとnextFrameは独立していませんか?
updateKeyでマウスの押している時間をカウントしているのに、nextFrameでただ左クリックが押しているか(1)押していないか(0)しか判定していないように思います。
つまり、ある範囲に入った瞬間にボタンが押されていなければ(0)、ただ「押されていない(0)」とだけ判断してプログラムが実行されたのではないですか?

Re: DxLibのマウスの挙動について

Posted: 2013年12月01日(日) 21:18
by kgtekito
updateKeyで取得しているKey[256]については
http://dixq.net/g/02_09.html
ここを参照しました。updateKeyでカウントさせているKeyってマウスボタンにも対応しているのでしょうか・・?

コード:

    prevMouse = mouse;
    mouse = GetMouseInput();
 if (mouse ^ prevMouse & mouse & MOUSE_INPUT_LEFT){
        status = OFF_ON;
    }
    if (mouse ^ prevMouse & prevMouse & MOUSE_INPUT_LEFT){
        status = ON_OFF;
    }
この部分で「一度ONになってからOFF」、「一度OFFになってからON」(http://dixq.net/forum/viewtopic.php?f=3&t=11602
を判定させて、これで場合分けしようと試みているのですが、これでは不十分だったでしょうか?(ビット演算子の挙動がよくわかっていないということもあるかもしれません・・)

Re: DxLibのマウスの挙動について

Posted: 2013年12月01日(日) 21:53
by だんごさん
あ、キーボードのカウントでした、申し訳ないです。さっきのは無視してください。
ただ、GetMouseInput関数では、マウスが押されているか、押されていないか、しか判定しないはずです。押されていないときにある範囲内に入ると、「マウスが押されていない」としか判定しないことになります。つまり、
マウスが押されていない→常にON_OFF
マウスが押されている→常にOFF_ON
となっているのだと思います。
これで実装するとなると、「押された瞬間」と「離した瞬間」を判定したいのだから、それ以外の時はstatusに別の値を入れて、判定されないようにしてみてはどうですか?

Re: DxLibのマウスの挙動について

Posted: 2013年12月02日(月) 00:33
by kgtekito
nextFrame()関数を、以下のように定義しなおしたところ、

コード:

void Keys::nextFrame() { //フレーム遷移
	int x, y,u;
	for (std::map<int, std::pair<bool,bool> >::iterator it = mouse.begin(); it != mouse.end(); it++)
	{

		if ((*it).first & GetMouseInput()) //今のフレームの状態を格納
		{
			(*it).second = P((*it).second.second, TRUE);
		}
		else
		{
			(*it).second = P((*it).second.second, FALSE);
		}

	}
	GetMousePoint(&x, &y);

	coord.real(x);
	coord.imag(y);
}

コード:

bool Keys::test(){
	return mouse[MOUSE_INPUT_LEFT].second; // クリックするたびに0になったり、1になったりする
}
bool Keys::test2(){
	return mouse[MOUSE_INPUT_LEFT].first; //0のままかわらない
}
「今のフレームの状態」はクリックするごとに0や1に期待通りに変わってくれるのですが、なぜか「今より1フレーム前の状態」の値が0のまま不変でした・・・
nextFrameでしているクリックの処理のコードはかなりシンプルなのでどこが間違っているのかさっぱりわかりません・・

Re: DxLibのマウスの挙動について

Posted: 2013年12月02日(月) 14:21
by だんごさん
GetMouseInputは押された(1)押されていない(0)しか返しません。毎フレームごとにカウントしてくれる関数ではないです。
ホントに申し訳ないですがC++は全く触れたことが無く、動きが分からないので予想として書きます。

毎フレーム監視したプログラムだと分かりやすいのでお勧めします。
40b. 「マウスがどれ位押されたか」を判定する関数を参考にしてみてください。
「if(count == 0)で押されていない」
「if(count == 1)でその瞬間(1フレーム目)」
「if(count > 0)で押している状態を常に取得」
という風になります。これで離された瞬間を得るには、
1.範囲内でクリックされたら(count == 1)フラグを立てる(範囲外でマウスが押されたまま範囲内に入っても検知する場合はcount>0)
2.その範囲内で押されていない(count==0)かつ、フラグが立っていた場合は離されたということ
※範囲外に出たときはフラグを回収する必要があります。

kgtekito さんが書きました:

コード:

    prevMouse = mouse;
    mouse = GetMouseInput();
 if (mouse ^ prevMouse & mouse & MOUSE_INPUT_LEFT){
        status = OFF_ON;
    }
    if (mouse ^ prevMouse & prevMouse & MOUSE_INPUT_LEFT){
        status = ON_OFF;
    }
このコードで行くとなると、範囲内に入る前にマウスが押されていなければON_OFFがstatusが入っています。そこで範囲内に入ってくるとstatusがON_OFFなので「あ、マウスが離されたんだな」ってgetMouseWithStatusの方から1が戻り値で返ってきますね。
まだ押してもないのにこれだといけないので、前フレームを監視して、「前回押されていたが今離されている」時だけstatusにON_OFFを入れるようにします。それ以降はgetMouseWithStatusで検知されないように別の値を入れるべきなのです。

Re: DxLibのマウスの挙動について

Posted: 2013年12月03日(火) 01:39
by kgtekito
C++の書き方について追記すると、No.5の投稿で新しく定義したnextFrameではMOUSE_INPUT_LEFTやMOUSE_INPUT_RIGHTなどのキーコードに対応したペア(一つ前のフレームの入力状態、現在のフレームの入力状態)を利用して、全てのキーコードについて現在のフレームの状態をペアの第一変数に格納すると同時に第二変数については現在のフレームの情報を更新していくような書き方をしてみたのですが…クリックと同時に第2変数はちゃんと切り替わってくれたのですが第1変数はずっと0のままでした・・何度もすみません

Re: DxLibのマウスの挙動について

Posted: 2013年12月03日(火) 16:54
by だんごさん
kgtekito さんが書きました:

コード:

        if ((*it).first & GetMouseInput()) //今のフレームの状態を格納
C++の評価する順番は分かりませんが、if ( ((*it).first & GetMouseInput()) )とカッコをつけて、先に代入するようにしてみてはどうですか?
変わらなかったらすみません。。

Re: DxLibのマウスの挙動について

Posted: 2013年12月03日(火) 18:01
by ISLe
まさかとは思いますが、nextFrame関数をupdateKey関数以外でも呼び出しているということはないですよね。

updateKey関数を1フレームに2回以上呼び出している可能性もありますけど、それだとキーでも不具合が出ますし。

Re: DxLibのマウスの挙動について

Posted: 2013年12月03日(火) 18:40
by kgtekito
>>8
変わりませんでした・・
>>9
nextFrame()の呼び出しはupdateKey()でしか確認できませんでした・・後者についても1回の呼び出しのみでした。

Re: DxLibのマウスの挙動について

Posted: 2013年12月04日(水) 00:55
by へにっくす
Pというマクロ(?)を展開してもダメかな?

コード:

for (std::map<int, std::pair<bool,bool> >::iterator it = mouse.begin(); it != mouse.end(); it++)
{
  (*it).second.first = (*it).second.second;
  if ((*it).first & GetMouseInput()) //今のフレームの状態を格納
  {
      (*it).second.second = TRUE;
  }
  else
  {
      (*it).second.second = FALSE;
  }
}

Re: DxLibのマウスの挙動について

Posted: 2013年12月04日(水) 02:27
by kgtekito
Pはtypedef pair<bool,bool> Pです…そのまま掲載してしまってすみません…

Re: DxLibのマウスの挙動について

Posted: 2013年12月04日(水) 05:16
by へにっくす
kgtekito さんが書きました:Pはtypedef pair<bool,bool> Pです…そのまま掲載してしまってすみません…
いや、だから。
私がききたいのは、展開しても同じかどうかですよ。

Re: DxLibのマウスの挙動について

Posted: 2013年12月04日(水) 10:04
by kgtekito
forの中身を貼っていただいたスニペットに置き換えても、同様の結果が得られました。

Re: DxLibのマウスの挙動について

Posted: 2013年12月04日(水) 17:04
by ISLe
やっぱり2回以上呼び出されているとしか思えないですけどね。
kgtekito さんが書きました:nextFrame()の呼び出しはupdateKey()でしか確認できませんでした・・後者についても1回の呼び出しのみでした。
こちらの意図していることを理解していただけているでしょうか。
ほんとうに1箇所ではなくて1回ですか?

そもそも、コンポーネント指向にしたほうがもっと使い回しやすく、こんなめんどうな話にもならないのではないかという気がします。

Re: DxLibのマウスの挙動について

Posted: 2013年12月04日(水) 17:39
by ISLe
すみません。
演算子の優先順位の問題で^演算を括弧で囲わないと正しく動きませんでした。
参考にしたというトピックのコードが間違ってました。
#あとで修正しておきます。
#…トピックが閉鎖されてて修正できなかった。

検証したコードはこちら。

コード:

#include "DxLib.h"
static int prevMouse;
enum buttonState {
	OFF_ON,
	ON_OFF,
	OTHER
};
static buttonState status = OTHER;
static void Keys_nextFrame() {
    int mouse = GetMouseInput();
    status = OTHER;
    if ((mouse ^ prevMouse) & mouse & MOUSE_INPUT_LEFT){
        status = OFF_ON;
    }
    if ((mouse ^ prevMouse) & prevMouse & MOUSE_INPUT_LEFT){
        status = ON_OFF;
    }
    prevMouse = mouse;
}
int Keys_getMouseWithStatus(buttonState x) {
	return x == status ? 1 : 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	ChangeWindowMode(TRUE);
	if (DxLib_Init() != 0) return 0;
	SetDrawScreen(DX_SCREEN_BACK);
	int frame_cnt = 0;
	while (ProcessMessage() == 0 && ScreenFlip() == 0 && ClearDrawScreen() == 0) {
		Keys_nextFrame();
		if (Keys_getMouseWithStatus(ON_OFF)) printfDx("%d:ON_OFF\n", frame_cnt);
		if (Keys_getMouseWithStatus(OFF_ON)) printfDx("%d:OFF_ON\n", frame_cnt);
		frame_cnt++;
	}
	DxLib_End();
	return 0;
}
std::pairを使ったコードに関しては分かりません。