第4回 入力用GUIを作る!
第4回 数字ボタンを押して入力しよう
前回は、画像を読み込み、数字ボタンと4桁の入力欄を画面へ並べました。
ただし、前回の数字ボタンは画像を表示しているだけです。
数字ボタンを表示する
↓
クリックしても何も起こらない
今回は、マウスで数字ボタンをクリックし、押した数字をinputへ追加できるようにします。
この回は、第3回終了時点の
main.cppから作業を始めます。
このページの進め方
このページでは、読む部分とソースを書き換える部分を分けています。
説明
仕組みを確認する
STEP ○ ここでソースを書き換える!
main.cppを実際に変更する
動作確認
ビルドして実行する
先に完成させたい場合は、STEPを順番に進めます。
仕組みを詳しく確認したい場合は、各STEPの前後にある説明も読みます。
ソースを書き換える場所は、コード内のここから追加、ここを変更、この部分を削除などのコメントで示します。
1.今回の完成状態
今回のプログラムでは、次の操作ができるようになります。
- 0~9の数字ボタンをクリックする
- クリックした数字が入力欄へ左から追加される
- 最大4桁まで入力できる
- 同じ数字も入力できる
- 「入力を消す」ボタンで4桁をすべて消せる
- 1回のクリックで1桁だけ入力される
最初
[*][*][*][*]
3をクリック
[3][*][*][*]
8をクリック
[3][8][*][*]
判定ボタンは、まだ動きません。
HIT・BLOWの判定は次回作ります。
説明1 マウス入力をUpdateで扱う
画面へ画像を表示する処理はDraw()へ書きました。
マウスの状態を調べ、ゲームの変数を書き換える処理はUpdate()へ書きます。
Update()
マウスの位置を調べる
左クリックを調べる
押された数字をinputへ追加する
Draw()
inputの内容を画像で表示する
マウス入力をDraw()へ書かないようにします。
Draw()は表示、Update()は入力とゲームの更新を担当します。
マウス座標
マウスカーソルがある場所は、X座標とY座標で表します。
画面左上が(0, 0)です。
X座標
右へ進むほど大きくなる
Y座標
下へ進むほど大きくなる
GetMousePointで座標を取得する
マウス座標はGetMousePoint()で取得します。
GetMousePoint(&mouseX, &mouseY);
&を付けたmouseXとmouseYへ、X座標とY座標を書き込んでもらいます。
アドレスやポインタの詳しい仕組みは、ここでは扱いません。
GetMouseInputでボタン状態を取得する
マウスボタンの状態はGetMouseInput()で取得します。
mouseInput = GetMouseInput();
左ボタンが押されているかは、次の条件で調べます。
(mouseInput & MOUSE_INPUT_LEFT) != 0
ここでの&はビット演算子で、&mouseXの&とは別の意味です。ビット演算の詳しい仕組みは、ここでは扱いません。
押している間ではなく、押した瞬間を調べる
Update()は何度も繰り返されるため、押している状態だけを見ると1回のクリックで同じ数字が複数回入ることがあります。
そこで、前回は押しておらず、今回は押しているときだけを「クリックした瞬間」とします。
leftClicked = (mouseInput & MOUSE_INPUT_LEFT) != 0 && (previousMouseInput & MOUSE_INPUT_LEFT) == 0;
Update()の最後で、今回の状態を次回のために保存します。
previousMouseInput = mouseInput;
STEP 1 マウス入力と入力桁数の変数を追加する
変更する場所:グローバル変数を宣言している場所
確認すること:エラーなくビルドできること
マウス座標、前回のマウス状態、現在の入力桁数を保存する変数を追加します。
まず、char input[5];のすぐ下へinputCountを追加します。
// ここに追加
// 現在入力されている桁数
int inputCount;
次に、int blow;のすぐ下へマウス用の変数を追加します。
// ここから追加
// マウスカーソルの座標
int mouseX;
int mouseY;
// 1回前のUpdateで取得したマウス状態
int previousMouseInput;
// ここまで追加
inputCountは、現在の入力桁数と、次に文字を入れる配列の添え字として使います。
inputCount = 2
input[0] = '3'
input[1] = '8'
input[2] = '\0'
STEP 2 IsPointInRect関数を追加する
変更する場所:
Initialize()より前
確認すること:エラーなくビルドできること
数字ボタンは、画面上の長方形として考えます。
左端 left
上端 top
右端 right
下端 bottom
マウス座標が長方形の中にある条件は、次のとおりです。
left <= mouseX
mouseX < right
top <= mouseY
mouseY < bottom
この処理を関数へまとめます。
IsPointInRect()は、Initialize()より前へ追加します。
// Initialize()より前へ、ここから関数を追加
// 点が長方形の中にあるか調べる
bool IsPointInRect(int pointX, int pointY, int left, int top, int right, int bottom)
{
return left <= pointX && pointX < right && top <= pointY && pointY < bottom;
}
// ここまで関数を追加
長方形の中ならtrue、外ならfalseを返します。
説明2 数字ボタンの位置と入力文字を求める
描画と同じ座標計算を使う
前回、数字ボタンを描画したときは、まず添え字iから列と行を求めました。
column = i % NUMBER_BUTTON_COLUMNS;
row = i / NUMBER_BUTTON_COLUMNS;
次に、その列と行からボタンの左上座標x、yを求めました。
x = NUMBER_BUTTON_START_X + column * (NUMBER_BUTTON_SIZE + NUMBER_BUTTON_GAP);
y = NUMBER_BUTTON_START_Y + row * (NUMBER_BUTTON_SIZE + NUMBER_BUTTON_GAP);
ここまでが、前回の描画で使った一連の計算です。
クリック判定でも、この一連の計算をそのまま使います。
ボタン番号i
↓
列と行を求める
↓
左上座標x、yを求める
描画とクリック判定で同じ座標計算を使うことで、
画面に見えているボタンの位置
と
クリックできる判定の位置
がずれないようにできます。
どの数字ボタンが押されたか調べる
左クリックした瞬間だけ、0~9のボタンを順番に調べます。
▲ leftClicked = true
|■ iを0から9まで繰り返す
||・iから列と行を求める
||・列と行からボタンの左上座標を求める
||▲ マウスがi番のボタン内にある
|||・i番の数字ボタンが押されたと判断する
||▼
|■
▼
iは、押された数字と同じ値になります。
0.pngの範囲をクリック
→ iは0
7.pngの範囲をクリック
→ iは7
整数を数字の文字へ変換する
ボタン番号iは整数ですが、inputへ入れるのは文字です。
input[inputCount] = (char)('0' + i);
C/C++では'0'~'9'が順番に並んでいるため、整数を足すと対応する数字の文字を作れます。
'0' + 3 → '3'
文字を入れたら、入力桁数を1増やします。
inputCount++;
その後、文字列の最後へ'\0'を入れます。
input[inputCount] = '\0';
処理順序は次のとおりです。
input[inputCount]へ文字を入れる
↓
inputCountを1増やす
↓
新しい末尾へ'\0'を入れる
4桁を超えて入力させない
inputは5要素ですが、数字を保存できるのは4桁までです。
input[0]~input[3]
4桁の数字
input[4]
'\0'
そのため、数字を追加する前にinputCountを確認します。
▲ inputCount < INPUT_DIGIT_COUNT
|・押した数字をinputへ追加する
|・inputCountを1増やす
|・文字列の最後へ'\0'を入れる
▼
4桁入力済みなら、数字ボタンを押しても何も追加しません。
実際のC++コードは、STEP 4でUpdate()へまとめて書きます。
同じ数字は判定時にエラーにする
今回は、次の入力も受け付けます。
1123
7777
数字ボタンを押す段階では、重複を禁止しません。
次回、判定ボタンを押したときにCheckAnswerError()を呼び出し、重複エラーとして表示します。
第4回
とりあえず4桁入力できるようにする
第5回
入力内容が正しいか確認する
入力処理と入力確認を分けて作ります。
STEP 3 入力の初期化と入力消去処理を追加する
変更する場所:
Initialize()より前へClearInput()を追加、Initialize()
確認すること:エラーなくビルドできること
仮入力を削除する
前回までは、マウス入力の代わりに"1234"をプログラム内へ直接設定していました。
// この部分を削除
strcpy_s(input, sizeof(input), "1234");
マウスで数字を入力するようになるため、仮入力の処理は削除します。
代わりに、入力欄を空文字列で初期化します。
仮入力を削除した後は、inputCountを0、input[0]を\0にして空文字列から始めます。
配列の最初に'\0'を入れると、長さ0の文字列になります。
入力を消す処理を追加する
数字を押し間違えた場合に備え、入力中の数字をすべて消す処理を作ります。
第4回では、右クリックしたときにClearInput()を呼び出します。
ClearInput()では、inputCountを0、input[0]を\0へ戻します。
// ここから関数を追加
// 入力中の4桁をすべて消す
void ClearInput(void)
{
inputCount = 0;
input[0] = '\0';
inputError = 0;
hit = 0;
blow = 0;
}
// ここまで関数を追加
Initializeを書き換える
Initialize()を、次のように変更します。
void Initialize(void)
{
// 実行するたびに乱数の並びを変える
srand((unsigned int)time(NULL));
// 重複しない4桁の問題を作る
MakeProblem(answer);
// ここから変更
ClearInput();
mouseX = 0;
mouseY = 0;
previousMouseInput = 0;
SetMouseDispFlag(TRUE);
// ゲームで使用する画像を読み込む
imageLoadError = LoadGameImages();
// ここまで変更
}
前回までの仮入力と、起動直後の判定処理は削除します。
STEP 4 Update関数全体をマウス入力対応へ書き換える
変更する場所:現在の
Update()
確認すること:左クリックで数字を入力でき、右クリックで入力をすべて消せる
現在のUpdate()を探し、下のコメントが付いた関数へ丸ごと置き換えます。
左クリックした瞬間は数字ボタンの入力に使います。
右クリックした瞬間はClearInput()を呼び出し、入力中の数字をすべて消します。
// この関数を丸ごと置き換える
// 入力確認とゲームの更新を行う
void Update(void)
{
int mouseInput;
bool leftClicked;
bool rightClicked;
// 現在のマウス座標を取得する
GetMousePoint(&mouseX, &mouseY);
// 現在のマウスボタン状態を取得する
mouseInput = GetMouseInput();
// 左クリックした瞬間を求める
leftClicked = (mouseInput & MOUSE_INPUT_LEFT) != 0 && (previousMouseInput & MOUSE_INPUT_LEFT) == 0;
// 右クリックした瞬間を求める
rightClicked = (mouseInput & MOUSE_INPUT_RIGHT) != 0 && (previousMouseInput & MOUSE_INPUT_RIGHT) == 0;
if (leftClicked)
{
int i;
// 0~9の数字ボタンを順番に調べる
for (i = 0; i < NUMBER_COUNT; i++)
{
int column;
int row;
int x;
int y;
column = i % NUMBER_BUTTON_COLUMNS;
row = i / NUMBER_BUTTON_COLUMNS;
x = NUMBER_BUTTON_START_X + column * (NUMBER_BUTTON_SIZE + NUMBER_BUTTON_GAP);
y = NUMBER_BUTTON_START_Y + row * (NUMBER_BUTTON_SIZE + NUMBER_BUTTON_GAP);
if (IsPointInRect(mouseX, mouseY, x, y, x + NUMBER_BUTTON_SIZE, y + NUMBER_BUTTON_SIZE))
{
// 4桁未満なら数字を追加する
if (inputCount < INPUT_DIGIT_COUNT)
{
input[inputCount] = (char)('0' + i);
inputCount++;
input[inputCount] = '\0';
}
break;
}
}
}
// 右クリックしたら入力中の数字をすべて消す
if (rightClicked)
{
ClearInput();
}
// 今回の状態を次回の比較用に保存する
previousMouseInput = mouseInput;
}
STEP 5 Draw関数の入力欄と操作案内を変更する
変更する場所:入力欄と判定ボタンの下
確認すること:第4回の完成画面になる
入力欄の描画をinputCountへ変更する
前回は、strlen()で仮入力の長さを求めていました。
今回は、入力処理で管理しているinputCountを使用します。
// ここから変更
// 4桁の入力欄を描画する
for (int i = 0; i < INPUT_DIGIT_COUNT; i++)
{
int x;
int imageHandle;
// i番目の桁を表示するX座標
x = INPUT_START_X + i * (INPUT_IMAGE_SIZE + INPUT_IMAGE_GAP);
// 未入力の場合に表示する画像
imageHandle = blankImage;
// 入力済みで、数字が入っている場合
if (i < inputCount)
{
int number;
// 文字の数字を整数へ変換する
number = input[i] - '0';
// 対応する緑色の数字画像を選ぶ
imageHandle = displayNumberImages[number];
}
// 入力欄の画像を80×80で表示する
DrawGraph(x, INPUT_START_Y, imageHandle, TRUE);
}
// ここまで変更
数字ボタンからしか入力できないため、isdigit()による確認は不要になります。
判定前の表示へ変更する
第3回では、仮入力とHIT・BLOWの結果を表示していました。
第4回では数字の入力までを完成させ、HIT・BLOWの判定は次回作ります。
まず、入力欄の上にある説明を次のように変更します。
// ここを変更
DrawString(40, 85, "数字を4桁入力してください", white);
次に、第3回で作ったHIT・BLOWとエラーメッセージの表示を削除し、次の2行へ置き換えます。
// ここから変更
DrawFormatString(40, 215, white, "入力:%s", input);
DrawString(40, 245, "HITとBLOWの判定は次回作ります。", gray);
// ここまで変更
ここで作った「HITとBLOWの判定は次回作ります。」という表示は、第5回でHIT数とBLOW数の表示へ置き換えます。
第3回から残っている判定ボタンのコメントも、現在の状態に合わせて変更します。
// このコメントを変更
// 判定ボタンはまだ表示だけ
DrawGraph(JUDGE_BUTTON_X, JUDGE_BUTTON_Y, judgeImage, TRUE);
右クリックの操作案内を表示する
入力を消す操作は画面上のボタンではなく、右クリックで行います。
判定ボタンの右側へ、操作方法を文字で表示します。
// ここから追加
// 右クリックで入力を消せることを表示する
DrawString(330, 525, "右クリック:入力を消す", gray);
// ここまで追加
この文字は操作方法の案内で、クリックするボタンではありません。
右側の確認表示を整理する
第3回で追加した右側の仮表示は、マウス座標も表示できるように位置を整理します。
第3回の次の2行を、
DrawString(480, 115, "右側は第6回で", gray);
DrawString(480, 145, "解答履歴に使います。", gray);
次の位置へ変更します。
// この2行を変更
DrawString(480, 85, "右側は第6回で", gray);
DrawString(480, 115, "解答履歴に使います。", gray);
その上へ、マウス座標を表示します。
// ここに追加
// マウス座標をデバッグ表示する
DrawFormatString(500, 35, gray, "マウス:(%d, %d)", mouseX, mouseY);
マウスを動かすと、数値が変化することを確認できます。
この3つの確認表示は、第6回で解答履歴を作るときに削除します。
6.プログラム全体
ここまでの内容をすべて入れたmain.cppは、次のようになります。
#include "DxLib.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
// 画面の大きさ
const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 600;
// ゲーム内部を1秒間に更新する回数
const int UPDATE_FPS = 60;
// Update1回分の時間
const double FIXED_DELTA_TIME = 1.0 / UPDATE_FPS;
// 長時間処理が止まった場合に、
// 一度に処理する経過時間の上限
const double MAX_FRAME_TIME = 0.25;
// HIT&BLOWで使用する数字の数
const int NUMBER_COUNT = 10;
// 入力する桁数
const int INPUT_DIGIT_COUNT = 4;
// 入力欄の配置
const int INPUT_START_X = 50;
const int INPUT_START_Y = 110;
const int INPUT_IMAGE_SIZE = 80;
const int INPUT_IMAGE_GAP = 10;
// 数字ボタンの配置
const int NUMBER_BUTTON_START_X = 30;
const int NUMBER_BUTTON_START_Y = 335;
const int NUMBER_BUTTON_SIZE = 70;
const int NUMBER_BUTTON_GAP = 8;
const int NUMBER_BUTTON_COLUMNS = 5;
// 判定ボタンの配置
const int JUDGE_BUTTON_X = 120;
const int JUDGE_BUTTON_Y = 510;
const int JUDGE_BUTTON_WIDTH = 200;
const int JUDGE_BUTTON_HEIGHT = 46;
// ゲームの正解
char answer[5];
// プレイヤーの入力
char input[5];
// ===== 【第4回で追加・変更】現在の入力桁数を保存 ここから =====
// 現在入力されている桁数
int inputCount;
// ===== 【第4回で追加・変更】現在の入力桁数を保存 ここまで =====
// 入力内容のエラー番号
int inputError;
// 判定結果
int hit;
int blow;
// ===== 【第4回で追加・変更】マウス入力の状態を保存 ここから =====
// マウスカーソルの座標
int mouseX;
int mouseY;
// 1回前のUpdateで取得したマウス状態
int previousMouseInput;
// ===== 【第4回で追加・変更】マウス入力の状態を保存 ここまで =====
// 0~9の数字ボタン画像
int numberImages[NUMBER_COUNT];
// 入力欄へ表示する0~9の画像
int displayNumberImages[NUMBER_COUNT];
// 未入力の桁に表示する画像
int blankImage;
// 判定ボタン画像
int judgeImage;
// 画像の読み込み結果
int imageLoadError;
// 重複しない4桁の問題を作る
void MakeProblem(char outputAnswer[5])
{
char temp[] = "0123456789";
int i;
for (i = 0; i < 100; i++)
{
int index1 = rand() % 10;
int index2 = rand() % 10;
char work;
work = temp[index1];
temp[index1] = temp[index2];
temp[index2] = work;
}
for (i = 0; i < 4; i++)
{
outputAnswer[i] = temp[i];
}
outputAnswer[4] = '\0';
}
// 入力内容に問題がないか確認する
int CheckAnswerError(char inputData[])
{
int i;
int j;
// 4桁か確認する
if (strlen(inputData) != 4)
{
return 1;
}
// すべて数字か確認する
for (i = 0; i < 4; i++)
{
if (!isdigit((unsigned char)inputData[i]))
{
return 2;
}
}
// 同じ数字が含まれていないか確認する
for (i = 0; i < 4; i++)
{
for (j = i + 1; j < 4; j++)
{
if (inputData[i] == inputData[j])
{
return 3;
}
}
}
return 0;
}
// HIT数を数える
int CountHit(char correctAnswer[], char inputData[])
{
int count = 0;
int i;
for (i = 0; i < 4; i++)
{
if (correctAnswer[i] == inputData[i])
{
count++;
}
}
return count;
}
// BLOW数を数える
int CountBlow(char correctAnswer[], char inputData[])
{
int count = 0;
int i;
int j;
for (i = 0; i < 4; i++)
{
for (j = 0; j < 4; j++)
{
if (inputData[i] == correctAnswer[j] && i != j)
{
count++;
}
}
}
return count;
}
// ゲームで使用する画像を読み込む
int LoadGameImages(void)
{
char fileName[64];
int i;
// 0~9の画像を読み込む
for (i = 0; i < NUMBER_COUNT; i++)
{
// 青い数字ボタンのファイル名を作る
sprintf_s(fileName, sizeof(fileName), "images/%d.png", i);
numberImages[i] = LoadGraph(fileName);
if (numberImages[i] == -1)
{
return 1;
}
// 緑色の数字画像のファイル名を作る
sprintf_s(fileName, sizeof(fileName), "images/%d%d.png", i, i);
displayNumberImages[i] = LoadGraph(fileName);
if (displayNumberImages[i] == -1)
{
return 1;
}
}
// 未入力の桁に表示する画像を読み込む
blankImage = LoadGraph("images/kome.png");
if (blankImage == -1)
{
return 1;
}
// 判定ボタン画像を読み込む
judgeImage = LoadGraph("images/judge.png");
if (judgeImage == -1)
{
return 1;
}
return 0;
}
// ===== 【第4回で追加・変更】クリック判定と入力消去の処理を追加 ここから =====
// 点が長方形の中にあるか調べる
bool IsPointInRect(int pointX, int pointY, int left, int top, int right, int bottom)
{
return left <= pointX && pointX < right && top <= pointY && pointY < bottom;
}
// 入力中の4桁をすべて消す
void ClearInput(void)
{
inputCount = 0;
input[0] = '\0';
inputError = 0;
hit = 0;
blow = 0;
}
// ===== 【第4回で追加・変更】クリック判定と入力消去の処理を追加 ここまで =====
// ゲーム開始時に1回だけ実行する
void Initialize(void)
{
// 実行するたびに乱数の並びを変える
srand((unsigned int)time(NULL));
// 重複しない4桁の問題を作る
MakeProblem(answer);
// ===== 【第4回で追加・変更】入力とマウスの初期状態を設定 ここから =====
ClearInput();
mouseX = 0;
mouseY = 0;
previousMouseInput = 0;
SetMouseDispFlag(TRUE);
// ===== 【第4回で追加・変更】入力とマウスの初期状態を設定 ここまで =====
// ゲームで使用する画像を読み込む
imageLoadError = LoadGameImages();
}
// 入力確認とゲームの更新を行う
void Update(void)
{
// ===== 【第4回で追加・変更】数字ボタンのクリック入力と右クリック消去を追加 ここから =====
int mouseInput;
bool leftClicked;
bool rightClicked;
// 現在のマウス座標を取得する
GetMousePoint(&mouseX, &mouseY);
// 現在のマウスボタン状態を取得する
mouseInput = GetMouseInput();
// 左クリックした瞬間を求める
leftClicked = (mouseInput & MOUSE_INPUT_LEFT) != 0 && (previousMouseInput & MOUSE_INPUT_LEFT) == 0;
// 右クリックした瞬間を求める
rightClicked = (mouseInput & MOUSE_INPUT_RIGHT) != 0 && (previousMouseInput & MOUSE_INPUT_RIGHT) == 0;
if (leftClicked)
{
int i;
// 0~9の数字ボタンを順番に調べる
for (i = 0; i < NUMBER_COUNT; i++)
{
int column;
int row;
int x;
int y;
column = i % NUMBER_BUTTON_COLUMNS;
row = i / NUMBER_BUTTON_COLUMNS;
x = NUMBER_BUTTON_START_X + column * (NUMBER_BUTTON_SIZE + NUMBER_BUTTON_GAP);
y = NUMBER_BUTTON_START_Y + row * (NUMBER_BUTTON_SIZE + NUMBER_BUTTON_GAP);
if (IsPointInRect(mouseX, mouseY, x, y, x + NUMBER_BUTTON_SIZE, y + NUMBER_BUTTON_SIZE))
{
// 4桁未満なら数字を追加する
if (inputCount < INPUT_DIGIT_COUNT)
{
input[inputCount] = (char)('0' + i);
inputCount++;
input[inputCount] = '\0';
}
break;
}
}
}
// 右クリックしたら入力中の数字をすべて消す
if (rightClicked)
{
ClearInput();
}
// 今回の状態を次回の比較用に保存する
previousMouseInput = mouseInput;
// ===== 【第4回で追加・変更】数字ボタンのクリック入力と右クリック消去を追加 ここまで =====
}
// ゲーム画面を描画する
void Draw(void)
{
unsigned int white = GetColor(255, 255, 255);
unsigned int yellow = GetColor(255, 255, 0);
unsigned int red = GetColor(255, 80, 80);
unsigned int gray = GetColor(160, 160, 160);
// ゲームタイトルを表示する
DrawString(40, 25, "HIT & BLOW", white);
// 完成時には削除する正解表示
DrawFormatString(40, 55, yellow, "デバッグ用正解:%s", answer);
// 画像の読み込みに失敗していた場合
if (imageLoadError != 0)
{
DrawString(40, 110, "画像を読み込めませんでした。", red);
DrawString(40, 140, "imagesフォルダーの場所を確認してください。", red);
return;
}
// ===== 【第4回で追加・変更】入力中の数字と操作案内を表示 ここから =====
DrawString(40, 85, "数字を4桁入力してください", white);
// 4桁の入力欄を描画する
for (int i = 0; i < INPUT_DIGIT_COUNT; i++)
{
int x;
int imageHandle;
// i番目の桁を表示するX座標
x = INPUT_START_X + i * (INPUT_IMAGE_SIZE + INPUT_IMAGE_GAP);
// 未入力の場合に表示する画像
imageHandle = blankImage;
// 入力済みで、数字が入っている場合
if (i < inputCount)
{
int number;
// 文字の数字を整数へ変換する
number = input[i] - '0';
// 対応する緑色の数字画像を選ぶ
imageHandle = displayNumberImages[number];
}
// 入力欄の画像を80×80で表示する
DrawGraph(x, INPUT_START_Y, imageHandle, TRUE);
}
DrawFormatString(40, 215, white, "入力:%s", input);
DrawString(40, 245, "HITとBLOWの判定は次回作ります。", gray);
DrawString(40, 300, "数字ボタン", white);
// 0~9の数字ボタンを描画する
for (int i = 0; i < NUMBER_COUNT; i++)
{
int column;
int row;
int x;
int y;
// 5個ずつ並べるための列と行を求める
column = i % NUMBER_BUTTON_COLUMNS;
row = i / NUMBER_BUTTON_COLUMNS;
// 列と行から画面座標を求める
x = NUMBER_BUTTON_START_X + column * (NUMBER_BUTTON_SIZE + NUMBER_BUTTON_GAP);
y = NUMBER_BUTTON_START_Y + row * (NUMBER_BUTTON_SIZE + NUMBER_BUTTON_GAP);
// 数字ボタンを70×70で表示する
DrawGraph(x, y, numberImages[i], TRUE);
}
// 判定ボタンはまだ表示だけ
DrawGraph(JUDGE_BUTTON_X, JUDGE_BUTTON_Y, judgeImage, TRUE);
// 右クリックで入力を消せることを表示する
DrawString(330, 525, "右クリック:入力を消す", gray);
// マウス座標をデバッグ表示する
DrawFormatString(500, 35, gray, "マウス:(%d, %d)", mouseX, mouseY);
DrawString(480, 85, "右側は第6回で", gray);
DrawString(480, 115, "解答履歴に使います。", gray);
// ===== 【第4回で追加・変更】入力中の数字と操作案内を表示 ここまで =====
}
// Update1回分の時間を秒で返す
double DeltaTime(void)
{
return FIXED_DELTA_TIME;
}
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
LONGLONG previousTime;
double accumulator;
// ウィンドウモードで起動する
ChangeWindowMode(TRUE);
// 画面の大きさを設定する
SetGraphMode(SCREEN_WIDTH, SCREEN_HEIGHT, 32);
// ScreenFlipで垂直同期を待つ
SetWaitVSyncFlag(TRUE);
// DxLibを初期化する
if (DxLib_Init() == -1)
{
return -1;
}
// 描画先を裏画面にする
SetDrawScreen(DX_SCREEN_BACK);
// ゲームを初期化する
Initialize();
// 時間計測を開始する
previousTime = GetNowHiPerformanceCount();
// まだUpdateで処理していない時間
accumulator = 0.0;
// ゲームループ
while (ProcessMessage() == 0)
{
LONGLONG currentTime;
double frameTime;
// ESCキーが押されたら終了する
if (CheckHitKey(KEY_INPUT_ESCAPE) != 0)
{
break;
}
// 現在時刻を取得する
currentTime = GetNowHiPerformanceCount();
// 前回から実際に経過した時間を秒で求める
frameTime = (currentTime - previousTime) / 1000000.0;
// 次の計測に備えて現在時刻を保存する
previousTime = currentTime;
// 長時間止まった場合は、処理する時間を制限する
if (frameTime > MAX_FRAME_TIME)
{
frameTime = MAX_FRAME_TIME;
}
// 実際に経過した時間をためる
accumulator += frameTime;
// 1/60秒以上たまっている間、ゲームを更新する
while (accumulator >= DeltaTime())
{
Update();
// Updateで処理した時間を引く
accumulator -= DeltaTime();
}
// 裏画面を黒で消す
ClearDrawScreen();
// ゲーム画面を描画する
Draw();
// 裏画面を表画面へ表示する
ScreenFlip();
}
// DxLibを終了する
DxLib_End();
return 0;
}
7.動作確認
次の操作を確認してください。
数字入力
- 数字ボタンを1回クリックすると、1桁だけ入力される
- 押した数字が左から順番に表示される
- 4桁まで入力できる
- 5個目の数字は追加されない
- 0も入力できる
- 同じ数字も入力できる
クリック
- 左ボタンを少し長く押しても、同じ数字が連続入力されない
- ボタンとボタンの隙間を押しても入力されない
- マウス座標の表示がカーソルに合わせて変化する
入力消去
- 「入力を消す」を押すと4桁がすべて消える
- 1桁だけ入力した状態でも消せる
- 何も入力していない状態で押しても問題ない
8.うまく動かない場合
1回のクリックで同じ数字が何個も入る
前回のマウス状態を確認しているか調べます。
leftClicked = (mouseInput & MOUSE_INPUT_LEFT) != 0 && (previousMouseInput & MOUSE_INPUT_LEFT) == 0;
Update()の最後で、今回の状態を保存しているかも確認します。
previousMouseInput = mouseInput;
画像とクリック位置がずれている
描画とクリック判定で、同じ定数と座標計算を使っているか確認します。
NUMBER_BUTTON_START_X
NUMBER_BUTTON_START_Y
NUMBER_BUTTON_SIZE
NUMBER_BUTTON_GAP
NUMBER_BUTTON_COLUMNS
4桁目を入力すると表示がおかしくなる
文字を追加したあと、文字列の最後へ'\0'を入れているか確認します。
input[inputCount] = '\0';
9.今回のまとめ
今回は、マウスで数字ボタンを操作できるようにしました。
GetMousePoint()
マウス座標を取得する
GetMouseInput()
マウスボタンの状態を取得する
IsPointInRect()
マウスが長方形の中にあるか調べる
1回のクリックで1桁だけ入力するため、前回と今回のマウス状態を比較しました。
前回は押されていない
かつ
今回は押されている
↓
クリックした瞬間
押した数字は、整数から文字へ変換してinputへ追加しました。
整数の3
↓
文字の'3'
↓
inputへ追加
現在は4桁を入力できますが、入力内容の確認とHIT・BLOWの判定はまだ行いません。
次回は、判定ボタンをクリックできるようにし、CheckAnswerError()、CountHit()、CountBlow()を呼び出します。







No Comments