【雑談】よくある質問

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

【雑談】よくある質問

#1

投稿記事 by バグ » 15年前

『キーボードの押した瞬間の判定』って結構な回数質問されてますよね?
で、簡単に扱いやすいようにクラス化してみたんですが、どうでしょう?
判定するのは『押されているか否か』、『今押されたか否か』、『押しっ放しにされているカウント数』の3つです。


まずはクラス本体…
#pragma once

#include <limits.h>
#include "DxLib.h"

class Keyboard
{
private:
	unsigned long long m_uPressCount[256];

public:
	Keyboard(void)
	{
		for (int i = 0; i < 256; ++i)
		{
			m_uPressCount = 0;
		}
	}

	virtual ~Keyboard(void)
	{

	}

	bool Refresh(void)
	{
		char chKeybuf[256] = {0, };
		if (GetHitKeyStateAll(chKeybuf) != 0)
		{
			return false;
		}

		for (int i = 0; i < 256; ++i)
		{
			m_uPressCount = chKeybuf == 1 && m_uPressCount < ULLONG_MAX ? m_uPressCount + 1 : 0;
		}
		return true;
	}

	bool GetPressNow(int nKeyCode)
	{
		return m_uPressCount[nKeyCode] == 1 ? true : false;
	}

	bool GetPressed(int nKeyCode)
	{
		return m_uPressCount[nKeyCode] > 0 ? true : false;
	}

	unsigned long long GetPressCount(int nKeyCode)
	{
		return m_uPressCount[nKeyCode];
	}
};



そして使用例を…


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

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	if (DxLib_Init() == -1)
	{
		return -1;
	}

	Keyboard cKeyboard;

	while (ProcessMessage() == 0 && cKeyboard.Refresh() == true && clsDx() == 0)
	{
		if (cKeyboard.GetPressed(KEY_INPUT_Z) == true)
		{
			printfDx("Pressed = true\n");
		}
		else
		{
			printfDx("Pressed = false\n");
		}

		if (cKeyboard.GetPressNow(KEY_INPUT_Z) == true)
		{
			printfDx("PressNow = true\n");
		}
		else
		{
			printfDx("PresseNow = false\n");
		}

		printfDx("Pressed = %I64u\n", cKeyboard.GetPressCount(KEY_INPUT_Z));
		Sleep(5);
	}

	DxLib_End();
	return 0;
}

kazuoni

Re:【雑談】よくある質問

#2

投稿記事 by kazuoni » 15年前

スマートですね^^
シングルトン化しちゃってもいいのかな?なんて思ったり、思わなかったり・・・。

ただ、ここに来る人が関数をスッ飛ばして、クラスが扱えるって人は、
そこまでたくさんいないかと思うので、使用方法はしっかり書いておかないと
同じように質問が来てしまうかもしれませんね。

たかぎ

Re:【雑談】よくある質問

#3

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

DXライブラリはよく知らないので外しているかもしれませんが...

unsigned long longはやりすぎではないでしょうか?
unsigned longにしても、1msに1回カウントしても、49日間持ちます。
現実問題として、オーバーフローに対処する必要もないように思います。

細かな点としては、const修飾を徹底すべきなのと、ゼロクリアにはstd::fill_nを使ったほうがよいといったことはあります。
シングルトンにするのもよいですが、グローバルオブジェクトにしてしまっても良いと思います。

Justy

Re:【雑談】よくある質問

#4

投稿記事 by Justy » 15年前

そうですね、雑感ですが


■ クラスは理解できない人がいるかもしれない
 ゲームの館、龍神録の館の延長線上で質問してくる人が多いので、
多分関数タイプの方が判り安いのではないかな、と。


■ long longは VCでは 7.1以上、BCCでは 5.61以上でないと使え無い
 まんまです。
 そんな古い環境を使っているのに問題があるとも言えなくもないですが、
DXライブラリの対応環境には VC6とか BCC5.5も含まれていますんで……。


■ 押した瞬間、押されているだけでいいのか
 押した瞬間(Press)、押されている(On)があるのなら、
離された瞬間(Release)、押されていない(Off)は必要ないでしょうか?

 Offは Onの否定で判定できるので要らないかもしれませんが、
Releaseは様々なケースで必要になるかと思います。

 あとプライオリティは低いですがリピートとか、
デバッグ用途向けにダブルクリック(?)もあると便利ですね。


■ GetPressNow / GetPressed / GetPressCount
 メンバ関数に constをつけておいた方が良かったりしませんか?


■ GetPressNow関数などは引数の範囲チェックを
 配列の範囲外に対するアクセスになる可能性があるので、せめて DEBUGビルドの時だけでも
範囲チェックして、ユーザに対して何らかの警告を促したほうがいいのではないでしょうか。


■ シングルトン
 二つめ三つめのキーボードに対応しないのであれば
シングルトンにしてしまった方がいいかと思います。

 1つのオブジェクトを全体で使い回すような使い方にしてくれればいいのですが、
よくわかっていない人はあちこちでインスタンス化する可能性があり、
その場合に正しく動作しなかったり、同一フレームでも Refleshのタイミング次第で
食い違いが発生する可能性があります。


■ デストラクタが virtualになっている
 純粋な疑問ですが、このクラスを継承して何かしようとする予定があるのでしょうか?

tk-xleader

Re:【雑談】よくある質問

#5

投稿記事 by tk-xleader » 15年前

VCでlong long が使えることは知りませんでした。ずっと__int64のtypedefで通していましたから…

ただひとつだけ、C++であれば、
<limits.h>よりも<climits>のほうがいいと思います、できれば、<limits>のnumeric_limits<>::max()を使った方がいいと思います。

バグ

Re:【雑談】よくある質問

#6

投稿記事 by バグ » 15年前

>> kazuoniさん
>> シングルトン

そうですね、シングルトンにしてしまった方がすっきりしそうですね。




>> たかぎさん
>> unsigned long longはやりすぎではないでしょうか?

確かにやり過ぎですね(苦笑)


>>細かな点としては、const修飾を徹底すべきなのと、ゼロクリアにはstd::fill_nを使ったほうがよいといったことはあります。

了解です。




>> Justyさん
>> DXライブラリの対応環境には VC6とか BCC5.5も含まれていますんで……。

すっかり忘れておりました(^_^;)
ULONGLONGみたいにWindef.hで定義されてあるものに変更しておけばよさそうですね。

>> 押した瞬間、押されているだけでいいのか、押した瞬間(Press)、押されている(On)があるのなら、離された瞬間(Release)、押されていない(Off)は必要ないでしょうか?

>> Offは Onの否定で判定できるので要らないかもしれませんが、Releaseは様々なケースで必要になるかと思います。

>> あとプライオリティは低いですがリピートとか、デバッグ用途向けにダブルクリック(?)もあると便利ですね。

たしかにReleaseはあった方が良さそうですね。
リピートというと一定間隔でオンオフを繰り返す機能の事でしょうか?
ダブルクリックは判定が面倒そうなので…(苦笑)

>> GetPressNow関数などは引数の範囲チェックを配列の範囲外に対するアクセスになる可能性があるので、せめて DEBUGビルドの時だけでも範囲チェックして、ユーザに対して何らかの警告を促したほうがいいのではないでしょうか。

これは本家のCheckHitKey関数に合わせたんで、ああなってます(^_^;)
リファレンスかコメントを載せていない私が悪いですね…。




>> tkmakwins15さん
>> <limits.h>よりも<climits>のほうがいいと思います、できれば、<limits>のnumeric_limits<>::max()を使った方がいいと思います。

VC++6.0だとなぜかエラーが出て使えないんですよ。MSDNのサンプルをそのまんまコピーしてもエラーが出るので、よく分かりません…ちなみに下記のようなコード(MSDNのコピーです)とエラーです(^_^;)
#include <iostream>
#include <limits>

using namespace std;

void main() {
    cout << " 1 The minimum value for char is " <<
        (int)numeric_limits<char>::min() << endl;
}



warning C4003: マクロ 'min' に指定された実引数の数が少なすぎます。
error C2589: '(' : スコープ解決演算子 (::) の右側にあるトークンは使えません。
error C2059: 構文エラー : '::'

tk-xleader

Re:【雑談】よくある質問

#7

投稿記事 by tk-xleader » 15年前

>VC++6.0だとなぜかエラーが出て使えないんですよ。

処理系が勝手にminやmaxというマクロを定義してしまっていると思います。
#undef min
#undef max
で一応コンパイルは通ると思います。

tk-xleader

Re:【雑談】よくある質問

#8

投稿記事 by tk-xleader » 15年前

>バグさん

なるほど、windef.hのなかにmax,minの両方が定義されているようです。ヘッダを見る限りは、NOMINMAXマクロで何とかなりそうです。

Justy

Re:【雑談】よくある質問

#9

投稿記事 by Justy » 15年前


> ULONGLONGみたいにWindef.hで定義されてあるものに変更

 まぁ、そうですね。
 ただこのクラスのターゲットがターゲットなだけに、ユーザーがこの関数の戻り値を
あまり見慣れないだろう ULONGLONG型で受けなければならないということを考えると
たかぎさんの仰るように int、unsigned int型の方がいいかなと思ったりします。



>  リピートというと一定間隔でオンオフを繰り返す機能の事でしょうか?

 それです、それです。
 メニュー選択とかではあると便利です。

Ma

Re:【雑談】よくある質問

#10

投稿記事 by Ma » 15年前

些細なことですが、

return m_uPressCount[nKeyCode] == 1 ? true : false;

より

return (m_uPressCount[nKeyCode] == 1);

のほうがいいのでは?

(処理速度の違いとかは知りません)

バグ

Re:【雑談】よくある質問

#11

投稿記事 by バグ » 15年前

>> Maさん
式の結果の真偽値とbool型のtrue、falseは同じなのでしょうか?
VC++6.0のbool型はunsigned charの1、0と同等でしたが…
最近、C#での仕事が多いので、こういう書き方になってしまいますね…

たかぎ

Re:【雑談】よくある質問

#12

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

> 式の結果の真偽値とbool型のtrue、falseは同じなのでしょうか?

C++では、等価演算子や関係演算子の評価結果はbool型ですので、trueやfalseと一緒です。
それはそれとして、個人的には、

return m_uPressCount[nKeyCode] == 1 ? true : false;

で問題ないと思います。

bagu

Re:【雑談】よくある質問

#13

投稿記事 by bagu » 15年前

>>tkmakwins15さん
とりあえず、下記のように記述することでマクロとの競合は防ぐことができました。
#include <iostream>
#include <limits>

void main()
{
	int int_max = (std::numeric_limits<int>::max)();
	int int_min = (std::numeric_limits<int>::min)();
	std::cout << "max = " << int_max << std::endl;
	std::cout << "min = " << int_min << std::endl;
}
>>たかぎさん
>>C++では、等価演算子や関係演算子の評価結果はbool型ですので、trueやfalseと一緒です。

なるほど、勉強になります。ありがとうございました。

バグ

Re:【雑談】よくある質問

#14

投稿記事 by バグ » 15年前

すみません、1つ前の投稿は私です。名前を打ち間違えました…(^_^;)

バグ

Re:【雑談】よくある質問

#15

投稿記事 by バグ » 15年前

色々と意見を頂きましたので、新しく組みなおしてみました。
開発はVC++6.0で行いました。
まずはヘッダを…

#pragma once

#include <vector>
#include <limits>
#include "DxLib.h"

class Keyboard
{
private:
	static std::vector<bool> m_vbOnOff;
	static std::vector<bool> m_vbRelease;
	static std::vector<bool> m_vbRepeatOnOff;
	static std::vector<bool> m_vbRepeatEnable;
	static std::vector<unsigned int> m_vuRepeatCount;
	static std::vector<unsigned int> m_vuRepeatInterval;
	static std::vector<unsigned int> m_vuPressCount;

private:
	Keyboard(void);
	Keyboard(const Keyboard& cKeyboard);

public:
	static bool Refresh(void);

	static const bool GetOnOFF(int nKeyCode);
	static const bool GetPress(int nKeyCode);
	static const bool GetRelease(int nKeyCode);
	static const unsigned int GetPressCount(int nKeyCode);
	static void SetRepeat(int nKeyCode, bool bEnable, unsigned int uInterval = 1);
};
次に実装部を…

#include "Keyboard.h"

// スタティックメンバの初期化
std::vector<bool> Keyboard::m_vbOnOff(256);
std::vector<bool> Keyboard::m_vbRelease(256);
std::vector<bool> Keyboard::m_vbRepeatOnOff(256);
std::vector<bool> Keyboard::m_vbRepeatEnable(256);
std::vector<unsigned int> Keyboard::m_vuRepeatCount(256);
std::vector<unsigned int> Keyboard::m_vuRepeatInterval(256);
std::vector<unsigned int> Keyboard::m_vuPressCount(256);

// コンストラクタ
Keyboard::Keyboard(void)
{
	// ベクタクラスの初期化
	std::fill_n(m_vbOnOff.begin(), m_vbOnOff.size(), false);
	std::fill_n(m_vbRelease.begin(), m_vbRelease.size(), false);
	std::fill_n(m_vbRepeatOnOff.begin(), m_vbRepeatOnOff.size(), false);
	std::fill_n(m_vbRepeatEnable.begin(), m_vbRepeatEnable.size(), false);
	std::fill_n(m_vuRepeatCount.begin(), m_vuRepeatCount.size(), 0);
	std::fill_n(m_vuRepeatInterval.begin(), m_vuRepeatInterval.size(), 0);
	std::fill_n(m_vuPressCount.begin(), m_vuPressCount.size(), 0);
}

// キーボード情報の更新
bool Keyboard::Refresh(void)
{
	// キーボードの押下状態の更新
	char chKeybuf[256] = {0, };
	if (GetHitKeyStateAll(chKeybuf) != 0)
	{
		// GetHitKeyStateAll関数が失敗したのでfalseを返す
		return false;
	}
	else
	{
		for (int i = 0; i < 256; ++i)
		{
			m_vbOnOff = chKeybuf == 1 ? true : false;
		}
	}

	for (int i = 0; i < 256; ++i)
	{
		if (m_vbRepeatEnable == true)
		{
			if (++m_vuRepeatCount > m_vuRepeatInterval)
			{
				m_vuRepeatCount = 0;
				m_vbRepeatOnOff = m_vbRepeatOnOff == true ? false : true;
			}
			m_vbOnOff = m_vbRepeatOnOff;
		}

		if (m_vbOnOff[i] == true)
		{
			m_vuPressCount[i] += m_vuPressCount[i] < (unsigned int)(std::numeric_limits<int>::max)() ? 1 : 0;
		}
		else
		{
			m_vbRelease[i] = m_vuPressCount[i] > 0 ? true : false;
			m_vuPressCount[i] = 0;
		}
	}
	return true;
}

// 単純なオン・オフの取得
// int nKeyCode = DXライブラリで設定されてあるキーコード
const bool Keyboard::GetOnOFF(int nKeyCode)
{
	return m_vbOnOff[nKeyCode];
}

// 今押されたかどうかの取得
// int nKeyCode = DXライブラリで設定されてあるキーコード
const bool Keyboard::GetPress(int nKeyCode)
{
	return m_vuPressCount[nKeyCode] == 1 ? true : false;
}

// 今離されたかどうかの取得
// int nKeyCode = DXライブラリで設定されてあるキーコード
const bool Keyboard::GetRelease(int nKeyCode)
{
	return m_vbRelease[nKeyCode];
}

// 押しっ放しのカウント数の取得
// int nKeyCode = DXライブラリで設定されてあるキーコード
const unsigned int Keyboard::GetPressCount(int nKeyCode)
{
	return m_vuPressCount[nKeyCode];
}


// リピート機能の設定
// int nKeyCode = DXライブラリで設定されてあるキーコード
// bool bEnable = true : リピートのオン  false : リピートのオフ
// unsigned int uInterval = オン・オフの間隔
void Keyboard::SetRepeat(int nKeyCode, bool bEnable, unsigned int uInterval)
{
	m_vbRepeatOnOff[nKeyCode] = true;
	m_vbRepeatEnable[nKeyCode] = bEnable;
	m_vuRepeatInterval[nKeyCode] = uInterval;
	m_vuRepeatCount[nKeyCode] = 0;
}

最後に使用例を…

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

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	ChangeWindowMode(TRUE);
	if (DxLib_Init() == -1)
	{
		return -1;
	}

	// エンターキーのオートリピートを有効にする
	Keyboard::SetRepeat(KEY_INPUT_RETURN, true, 25);

	SetDrawScreen(DX_SCREEN_BACK);
	while (ProcessMessage() == 0 && Keyboard::Refresh() == true && ClearDrawScreen() == 0)
	{
		// エンターキーの情報を表示
		DrawFormatString(0, 0, GetColor(0xFF, 0xFF, 0xFF), "OnOff = %d\n", (int)(Keyboard::GetOnOFF(KEY_INPUT_RETURN)));
		DrawFormatString(0, 32, GetColor(0xFF, 0xFF, 0xFF), "Press = %d\n", (int)(Keyboard::GetPress(KEY_INPUT_RETURN)));
		DrawFormatString(0, 64, GetColor(0xFF, 0xFF, 0xFF), "Release = %d\n", (int)(Keyboard::GetRelease(KEY_INPUT_RETURN)));
		DrawFormatString(0, 96, GetColor(0xFF, 0xFF, 0xFF), "Count = %u\n", (int)(Keyboard::GetPressCount(KEY_INPUT_RETURN)));
		ScreenFlip();

		// Aが押されたら終了させる
		if (Keyboard::GetPress(KEY_INPUT_A) == true)
		{
			break;
		}
		Sleep(20);
	}

	DxLib_End();
	return 0;
}

たかぎ

Re:【雑談】よくある質問

#16

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

vectorを使っている理由がよくわかりませんでした。
これなら、bitsetやvalarrayを使ったほうがよいのではないでしょうか?
コンストラクタで呼出しているfill_nも、vectorを使うのであれば冗長なだけです。

欲をいえば、GetHitKeyStateAll関数などはハードコーディングするのではなく、ポリシークラスをテンプレート引数として与える方がよいのではないでしょうか?
それなら、GetKeyboardStateやWM_INPUTなどを使う方法にも簡単に切り替えられます。SDLやWindows以外に移植することさえ簡単です。
さらには、テンプレートですので、ヘッダファイルをインクルードするだけで使えるようになります。

経験の浅い人向けの対策としてはドキュメントさえちゃんと用意すれば問題ない気もしますが、心理的な敷居を避けるには、DXライブラリ用のポリシークラスとtypedef名またはラッパー関数をセットにしたヘッダファイルでも用意すればよいでしょう。

バグ

Re:【雑談】よくある質問

#17

投稿記事 by バグ » 15年前

たしかにvectorは冗長ですね。配列で充分ですね。
あまりSTLを使用した事がなかったので、なんだか楽しくて調子に乗ってしまいましたm(_ _)m
STLはまだまだ勉強不足です…

>>欲をいえば、GetHitKeyStateAll関数などはハードコーディングするのではなく、ポリシークラスをテンプレート引数として与える方がよいのではないでしょうか?
それなら、GetKeyboardStateやWM_INPUTなどを使う方法にも簡単に切り替えられます。SDLやWindows以外に移植することさえ簡単です。
さらには、テンプレートですので、ヘッダファイルをインクルードするだけで使えるようになります。

>>経験の浅い人向けの対策としてはドキュメントさえちゃんと用意すれば問題ない気もしますが、心理的な敷居を避けるには、DXライブラリ用のポリシークラスとtypedef名またはラッパー関数をセットにしたヘッダファイルでも用意すればよいでしょう。

今回はあくまでも、『DXライブラリを使用した場合のキーボード入力関係』のみに特化して、多少でも分かりやすくなればいいなと考えておりましたので、そこまでは考えておりません(^_^;)

Justy

Re:【雑談】よくある質問

#18

投稿記事 by Justy » 15年前

 うーん。
 
■ コンストラクタの存在意義
 これ、どこから実行されるんですか?


■ vector
 このケースで vectorを使うこと自体は良しとして、引っかかるのは
同じ要素数の vectorが7つもあることです。

 纏めれば1つで十分ですよね?


■ 静的メンバ関数だけ
 コンストラクタが意味を成さないものだとすると、残りは静的メンバ関数だけになるので
クラスである意味が薄れます。
 特殊なことを考えていないのであれば、Keyboardという名前空間で十分です。


■ リピート

 えーとですね例えば、一部のキーではメニューを操作するけど、一部のキーは
押しっぱなしになっているかもしれません。
 分かりやすい例だと、メタルギアソリッドシリーズのアイテム・武器選択とか。

 そういう場合など ON/OFFとは独立したリピート情報が必要です。
 
 あと、'O'が trueを返し、Xが falseを返すのだとすると
[color=#d0ffff" face="monospace]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
ON O O O O O O O O O O O O O O O O O O O O
REPEAT O X X X X X X O X X X O X X X O X X X O
[/color]

のように初回の Xの時間と、2回目以降の Xの時間は異なるように設定できると
メニューなどでは操作しやすくなります。

※23:03 勘違いしていたところを修正 


 なんかまぁ面倒そうですし、本来のコンセプトからすると外れたところにあるので
提案しておいて何ですが、ばっさり削ってしまった方がいいのかもしれません。


■ 256
 変わることはまずないとはいえ、同じ意味を持つ直値があちこちに。

バグ

Re:【雑談】よくある質問

#19

投稿記事 by バグ » 15年前

名前空間でも構わないかもしれませんが、プライベートメンバをユーザーに公開したくない場合はどのように処理すればよいのでしょうか?
ご教授くださいm(_ _)m

Justy

Re:【雑談】よくある質問

#20

投稿記事 by Justy » 15年前

 クラスではない場合メンバではなくなりますので、普通に cpp側で
[color=#d0d0ff" face="monospace]
namespace
{
int key_status[256];
void internal_update()
{
}
}
[/color]

のように無名名前空間の中に入れたり
[color=#d0d0ff" face="monospace]
static int key_status[256];
static void internal_update()
{
}
[/color]

と staticを使えばいいかと。

バグ

Re:【雑談】よくある質問

#21

投稿記事 by バグ » 15年前

それは分かるのですが、その方法ではユーザーに勝手に書き換えられてしまう危険性があるのではないかと思うのですが…?
そうなると、今回のクラスでいうRefresh関数での処理との整合性が取れなくなりますよね?

まぁ、書き換えたユーザーが悪いと言ってしまえばそれまでなのですが…(^_^;)

バグ

Re:【雑談】よくある質問

#22

投稿記事 by バグ » 15年前

一応、最終バージョンという事で…
リピートを外して、シングルトンに変更して、よりシンプルになりました。
ヘッダ部です。


#pragma once

#include <limits>
#include <algorithm>
#include "DxLib.h"

class Keyboard
{
private:
	enum {Buffer_Size = 256};

	static Keyboard* m_pcKeyboard;
	bool m_bOnOff[Buffer_Size];
	bool m_bRelease[Buffer_Size];
	unsigned int m_uPressCount[Buffer_Size];

private:
	Keyboard(void);
	Keyboard(Keyboard* cKeyboard);

public:
	static Keyboard* GetInstance(void);
	static void DeleteInstance(void);

	bool Refresh(void);

	bool GetOnOFF(int nKeyCode) const;
	bool GetPress(int nKeyCode) const;
	bool GetRelease(int nKeyCode) const;
	unsigned int GetPressCount(int nKeyCode) const;
};





実装部です。



#include "Keyboard.h"

// スタティックメンバ変数の初期化
Keyboard* Keyboard::m_pcKeyboard = NULL;

// コンストラクタ
Keyboard::Keyboard(void)
{
	m_pcKeyboard = NULL;
	std::fill_n(&m_bOnOff[0], (int)Buffer_Size, false);
	std::fill_n(&m_bRelease[0], (int)Buffer_Size, false);
	std::fill_n(&m_uPressCount[0], (int)Buffer_Size, 0);
}

// コピーコンストラクタ
Keyboard::Keyboard(Keyboard* pcKeyboard)
{
	m_pcKeyboard = pcKeyboard;
	memcpy(&m_bOnOff[0], &pcKeyboard->m_bOnOff[0], Buffer_Size);
	memcpy(&m_bRelease[0], &pcKeyboard->m_bRelease[0], Buffer_Size);
	memcpy(&m_uPressCount[0], &pcKeyboard->m_uPressCount[0], Buffer_Size);
}

// インスタンスの取得
Keyboard* Keyboard::GetInstance(void)
{
	if (m_pcKeyboard == NULL)
	{
		m_pcKeyboard = new Keyboard();
	}

	return m_pcKeyboard;
}

// インスタンスの削除
void Keyboard::DeleteInstance(void)
{
	if (m_pcKeyboard != NULL)
	{
		delete m_pcKeyboard;
	}

	m_pcKeyboard = NULL;
}

// キーボード情報の更新
bool Keyboard::Refresh(void)
{
	// キーボードの押下状態の更新
	char chKeybuf[Buffer_Size] = {0, };
	if (GetHitKeyStateAll(chKeybuf) != 0)
	{
		// GetHitKeyStateAll関数が失敗したのでfalseを返す
		return false;
	}

	for (int i = 0; i < 256; ++i)
	{
		// オン・オフの更新
		m_bOnOff = chKeybuf == 1 ? true : false;

		if (m_bOnOff == true)
		{
			// 押しっ放し時間の更新
			m_uPressCount += m_uPressCount < (unsigned int)(std::numeric_limits<unsigned int>::max)() ? 1 : 0;
		}
		else
		{
			// 離した瞬間の判定
			m_bRelease = m_uPressCount > 0 ? true : false;
			m_uPressCount = 0;
		}
	}

	return true;
}

// オン・オフの取得
// int nKeyCode = DXライブラリで設定されてあるキーコード
bool Keyboard::GetOnOFF(int nKeyCode) const
{
	return m_bOnOff[nKeyCode];
}

// 今、押されたかどうかの取得
// int nKeyCode = DXライブラリで設定されてあるキーコード
bool Keyboard::GetPress(int nKeyCode) const
{
	return m_uPressCount[nKeyCode] == 1 ? true : false;
}

// 今、離されたかどうかの取得
// int nKeyCode = DXライブラリで設定されてあるキーコード
bool Keyboard::GetRelease(int nKeyCode) const
{
	return m_bRelease[nKeyCode];
}

// 押しっ放し状態のカウント数の取得
// int nKeyCode = DXライブラリで設定されてあるキーコード
unsigned int Keyboard::GetPressCount(int nKeyCode) const
{
	return m_uPressCount[nKeyCode];
}



使用例です。



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

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	// DXライブラリの初期処理
	ChangeWindowMode(TRUE);
	if (DxLib_Init() == -1)
	{
		return -1;
	}
	SetDrawScreen(DX_SCREEN_BACK);

	// Keyboardクラスのインスタンスを取得する
	Keyboard* pcKeyboard = Keyboard::GetInstance();

	// メインループ
	while (ProcessMessage() == 0 && pcKeyboard->Refresh() == true && ClearDrawScreen() == 0)
	{
		// Enterキーの情報を表示
		DrawFormatString(0, 0, GetColor(0xFF, 0xFF, 0xFF), "OnOff = %d", (int)(pcKeyboard->GetOnOFF(KEY_INPUT_RETURN)));
		DrawFormatString(0, 32, GetColor(0xFF, 0xFF, 0xFF), "Press = %d", (int)(pcKeyboard->GetPress(KEY_INPUT_RETURN)));
		DrawFormatString(0, 64, GetColor(0xFF, 0xFF, 0xFF), "Release = %d", (int)(pcKeyboard->GetRelease(KEY_INPUT_RETURN)));
		DrawFormatString(0, 96, GetColor(0xFF, 0xFF, 0xFF), "Count = %u", (int)(pcKeyboard->GetPressCount(KEY_INPUT_RETURN)));
		ScreenFlip();

		// Aが押されたらループを抜ける
		if (pcKeyboard->GetPress(KEY_INPUT_A) == true)
		{
			break;
		}

		Sleep(20);
	}

	// Keyboardクラスのインスタンスを削除する
	pcKeyboard->DeleteInstance();

	// DXライブラリを終了させる
	DxLib_End();
	return 0;
}

バグ

Re:【雑談】よくある質問

#23

投稿記事 by バグ » 15年前

すみません、コピーコンストラクタがおかしいですね…。
// コピーコンストラクタ
Keyboard::Keyboard(Keyboard* pcKeyboard)
{
	m_pcKeyboard = new Keyboard();
	memcpy(&m_pcKeyboard->m_bOnOff[0], &pcKeyboard->m_bOnOff[0], Buffer_Size);
	memcpy(&m_pcKeyboard->m_bRelease[0], &pcKeyboard->m_bRelease[0], Buffer_Size);
	memcpy(&m_pcKeyboard->m_uPressCount[0], &pcKeyboard->m_uPressCount[0], Buffer_Size);
}
こう書くべきでした。
まぁ、実際には使われる事は無いコンストラクタではありますが…

Justy

Re:【雑談】よくある質問

#24

投稿記事 by Justy » 15年前


> その方法ではユーザーに勝手に書き換えられてしまう危険性があるのではないかと思うのですが

 書き換えられる、というのは Keyboard.h/cppの外から、ということですか?
それとも、Keyboard.hや Keyboard.cppを直接書き換える人がいる、ということですか?

閉鎖

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