読者です 読者をやめる 読者になる 読者になる

Aqutras Members' Blog

株式会社アキュトラスのメンバーが、技術情報などを楽しく書いています。

DXライブラリでタイピングゲーム制作 その4

技術

こんにちは,morivdenです.
前回(DXライブラリでタイピングゲーム制作 その3)に引き続いて
DXライブラリを用いた簡単なタイピングゲームを制作していこうと思います.
前回まででタイトル画面,メイン部分,リザルト画面,終了画面といった
タイピングゲームのひととおりの機能を実装しました.
これだけでも遊べないことはないのですが,
最低限の機能しか備えていないので,ゲームとしては面白みに欠けますね.
そこで今回はゲーム性を高めるために,
メイン部分でカウントダウン式の制限時間を設けてみたいと思います.
さらにリファクタリングも行いました.

環境

  • Windows 8.1 Pro
  • Visual Studio 2015
  • DXライブラリ

制限時間の実装

カウントダウン方式で制限時間内に問題文を打たせるプログラムを組んだのですが, ここでGetCharInputWait()関数の欠点が出てきてしまいました.
GetCharInputWain()関数は文字が入力されるまで処理が進まないので,
カウントダウンの実装が上手くいきません.
実装方法が分からなかったため,DXライブラリ置き場の質問掲示板で質問しました.

他の動作とカウントダウン動作を同時に行いたい

すると,GetCharInput()関数を使えばよいというアドバイスをいただきました.
以前GetCharInput()関数を使うと無限ループになってしまいましたが,
文字コードが入力された場合のみ正誤判定を行い,
それ以外の操作コードもしくは入力されていない場合は反復変数を減分させることで,
無限ループを回避できるようになりました.
さらにGetCharInput()関数は処理が動き続けるので,
正誤判定をしない間にカウントダウンの処理を行うことができます.

InputChar = GetInputChar(TRUE);

// GetInputCharが0以外かつ制御コード以外の時のみ正誤判定を行う
if (InputChar != 0 && InputChar >= CTRL_CODE_CMP && TimeLimit > 0) {
    // 正誤判定
    // 省略

} else {
    // 制限時間
    NowTime = GetNowCount();
    CurrentTime = NowTime - StartTime;
    TimeLimit = TIMELIMIT - CurrentTime;
        
    if (TimeLimit < 0) {
        TimeLimit = 0;
        DrawGraph(MAIN_X, MAIN_Y, FailedHandle, TRUE);
    }

制限時間実装後の様子
f:id:morivden:20160607164124g:plain
ちゃんとカウントダウンされていますね.

これで制限時間によるカウントダウンの実装はできました.

リファクタリング

冗長な処理の修正

質問掲示板で質問した際に,
回答者の方から冗長なコードがあるので修正する必要があるとの指摘を受けました.

初めにファイルからの読み込みの際にファイルを閉じていないため,
いずれファイルが開けなくなる可能性があるとの指摘を受けました.
さらにrand()関数は精度が悪いのでGetRand()関数を使用した方が良いという指摘も受けました.

RondomRead = rand() % QuestionNum;

// ファイルから読み込んだデータを格納
// ランダムの行数だけ読み込むことで疑似的にランダムに出題
for (RondomQuestion = 0; RondomQuestion <= RondomRead; RondomQuestion++) {
    FileRead_gets(Kana, BUF, QFileHandle);
    FileRead_gets(Alpha, BUF, AFileHandle); 
    }

// 正誤判定等の処理
// 省略

// ここでもう一度開くことにより終端の判定をさせない
QFileHandle = FileRead_open("Question.dat");
AFileHandle = FileRead_open("Answer.dat");

毎回ファイルの行をランダムに読み込むのではなく,
一度全部読み込んでランダムな行を表示するようにした方が
ファイルを読み込む回数が1回で済むとのアドバイスをいただきました.

コードを修正し,何度もファイルを開く必要がなくなりました.

RondomRead = GetRand(QuestionNum - 1);

// ファイルから読み込んだデータを格納
Kana = AllQuestion[RondomRead];
Alpha = AllAnswer[RondomRead]; 

0除算の可能性のある計算の修正

前回のリザルト画面の実装の際に以下のような計算式でスコアを計算していましたが,
CorrectMissの合計,またはCorrectPercentが0の場合に0除算が発生してしまいます.

int TypeSpeed;        // タイプスピード
int Percentage;       // 正確性
int CorrectPercent;   // 問題文コレクト率
int Score;            // スコア

// タイプスピード計算(key / min)
TypeSpeed = Correct * SECONDS * 1000 / Time;
// 正確性計算(%)
Percentage = Correct * PERCENTAGE / (Correct + Miss);
// コレクト率計算(%)
CorrectPercent = Correct * PERCENTAGE / Total;
// スコア計算(タイプスピード × 正確性(百分率) × 問題文コレクト率 × 難易度補正 + 時間補正)
Score = (TypeSpeed * Percentage * PERCENTAGE / CorrectPercent * Difficult) + (100000 / (Time / 1000));

エラーが発生し,応答なしに…….
f:id:morivden:20160607174346j:plain

現時点での対策として値を0で初期化し,
Correctが0ではない場合のみスコア計算を行うようにすることで
0除算が行われないようにしました.

if (Correct != 0) {
    // スコア計算
    // 省略
}

0除算が行われず,正しく動作しています.
f:id:morivden:20160607174625j:plain

終わりに

今回は制限時間の実装とリファクタリングを行いました.
次回はさらにゲーム性を高めるために,難易度の選択ができるようにしたいと思います.

こちらに試作品を置いています.(前回と同じバージョンです)