DXライブラリでキーコンフィグを実装してみた
こんにちは。えあると申します。今回はDXライブラリにてキーコンフィグを実装してみましたので、noteに書き残しておこうと思います。
キーコンフィグを実装した経緯
そもそも、なぜキーコンフィグを実装したのかというお話を。
結論から申しますと、
「製作者の操作しやすいと、プレイヤーの操作しやすいは違う事がある」
と思ったからです。
要はたくさんのプレイヤーのプレイスタイルにお答えしたいと思ったからですね。
実装の前に
今回の処理には、DXライブラリを使用させていただいています。
主にキー入力とキーのID(16進数で定数に宣言されている)を使用しています。
↓DXライブラリはこちら↓
DXライブラリ置き場
https://dxlib.xsrv.jp/index.html
処理の流れ
まずは処理の流れについてお話します。行っている処理としては大きく3つに分かれます。
1つ目は、キーコンフィグのロードです。
その名の通り外部ファイルに保存されているキーコンフィグを読み込みます。
2つ目は、キーコンフィグのセーブです。
こちらもその名の通りで、設定したキーコンフィグを外部ファイルに書き込みます。
そして3つ目は、ロードしたコンフィグの入力処理です。
読み込んだコンフィグに設定されたボタンの入力を受け付ける処理をしています。
ではそれぞれを詳しく説明していきます。
1.ロード処理
ロード処理にはC++のfstreamを主に使用しています。
// コンフィグファイルを開く
ifstream ifs(CONFIG_PATH);
// ファイルが開けなかったときはエラーを表示して処理しない
if (!ifs)
{
MessageBox(0, "コンフィグファイルを読み込めませんでした。", NULL, MB_OK);
}
// 読み込んだ文字列の保持用
string s = "";
// ボタンの数に合わせて読み込む
for (int i = 0; i < BUTTON_COUNT; i++)
{
// ファイルの最後に到達したときに処理を辞める
if (ifs.eof())
{
break;
}
// 文字の部分は読み飛ばす
getline(ifs, s, ':');
// 数値を読み込む
getline(ifs, s, '\n');
// 読み込んだ文字を数値に変換
int id = -1;
id = stoi(s);
// ボタンを保持する
buttonId[i] = id;
}
単純に外部ファイルに書き込まれたキーコードをgetline関数を使って、上から順に読み込んでいきます。
そして、読み込んだ数列の文字列をint型のbuttonIdという配列に保持していきます。
また何かしらのはずみで配列範囲外のインデックスを触ってしまわないよう、読み込むべき数を予め決めておいたうえで読み込むようにしています。
2.セーブ処理
こちらもロード処理同様、fstreamを使用しています。
// コンフィグファイルを開く
ofstream ofs(CONFIG_PATH);
// 開けなかったときはエラーを表示して処理しない
if(!ofs)
{
MessageBox(0, "コンフィグファイルを読み込めませんでした。", NULL, MB_OK);
return;
}
// 上から順にボタンの数、書き込む
for (int i = 0; i < BUTTON_COUNT; i++)
{
ofs << BUTTON_NAME[i] << ":" << buttonId[i] << endl;
}
ロードに比べると非常にシンプルで、単純にボタンの名前とIDを書き込んでいます。特別何もしていません。
書き出したファイルは以下のようになっています。
Up:200
Down:208
Left:203
Right:205
Confirm:44
Cancel:45
Menu:1
ボタンの名前:キーID
という構成になっていて、読み込みでは前半の名前は読み飛ばす様になっています。
3.入力処理
// 入力の更新処理
void Input::InputUpdate()
{
// 前フレームの状態を保持する
for (int i = 0; i < BUTTON_COUNT; i++)
{
oldButtonInput[i] = nowButtonInput[i];
}
// 現在のフレームの状態を取得する
for (int i = 0; i < BUTTON_COUNT; i++)
{
nowButtonInput[i] = CheckHitKey(buttonId[i]);
}
}
まずは毎フレーム入力に対する更新処理を実行します。
こちらは、ロードの際に取得したbuttonIdの配列を使用して、DXライブラリの関数であるCheckHitKeyを実行しています。
// ボタンの入力状態
bool Input::GetButton(Buttons button)
{
return nowButtonInput[(int)button];
}
// ボタンを押した瞬間
bool Input::GetButtonDown(Buttons button)
{
if (nowButtonInput[(int)button] &&
!oldButtonInput[(int)button])
{
return true;
}
return false;
}
// ボタンを離した瞬間
bool Input::GetButtonUp(Buttons button)
{
if (!nowButtonInput[(int)button] &&
oldButtonInput[(int)button])
{
return true;
}
return false;
}
そして入力の取得に関しては3つのstaticなメンバー関数を用意しています。
完全にUnityのインスパイアではありますが、「キーが入力されている間」「キーが入力された瞬間」「キーの入力がなくなった瞬間」の3つです。
引数のButtonsに関しては後ほど説明しております。
また、staticにしたことでキー入力を必要とする箇所に記述するプログラムが短くなっています。
キーワード「配列」
今回のプログラムではキーIDを配列として持っています。
しかし、万が一にでもロードやセーブの際に配列がずれてしまっては正常に動作しない可能性があります。
そこで今回は、Buttonsというenum型を使用しています。
enum class Buttons
{
Up = 0,
Down,
Left,
Right,
Confirm,
Cancel,
Menu,
OverID, // 範囲外
};
// ボタンの最大数
static const int BUTTON_COUNT = (int)Buttons::OverID;
// 各キーIDの変数
static int buttonId[BUTTON_COUNT];
上記のようなプログラムになっていて、すべてのキーIDに関する配列はこのenum型を使い管理しています。
enum型をint型にキャストして使用することで、間違ったインデックスを参照しないようになっているわけです。
またenum型を使うメリットはまだあって、OverIDという要素の上に要素を追加するだけで、配列の要素数が変化します。
これによってロードで読み込むボタンの数、セーブで書き込むボタンの数も変化します。
要するに新たに使用するボタンを増やしたくなったときでも、要素を増やすだけで対応できるわけです。
非常に拡張しやすくなっているのでは無いでしょうか。
実際に作ってみて
まずはenum型の可能性というものを非常に実感しました。
必要なものを必要な数だけ列挙しておくことで、プログラムに確実性が増すだけでなく、要素の追加も楽になるという一石二鳥なプログラムになったと思います。
また入力の取得をstatic関数にしたことで、何度も同じようなプログラムを記述することなく、必要な箇所に入力を用意することができました。
ゲームにキーコンフィグがあるだけで、プレイヤーが思うように操作しやすい環境を提供できるのでは無いかと思います。
こういったゲームの本編以外のところでも、プレイヤーに気を配っていきたいところです。
この記事が気に入ったらサポートをしてみませんか?