6章 ビジュアルプログラミング応用編−Timerを活用したプログラム

 

【学習内容とねらい】

 

本章では、これまでとは違ったプログラムを作成してみましょう。一言で言うと、継続的な“動き”のあるプログラムです。たとえば、モグラたたきゲームのような・・・。

 

1章(【基礎課題1-7-4】)で、モグラたたきゲームのフォームのみを作成しました。これに、第5章で学習したrandom関数を用いると、ランダムにモグラを表示することができます。しかし、それはボタンを押したときの1回きりの変化で、モグラたたきゲームのように、次から次とモグラが現れては消える、という継続的な動き(変化)を実現することができません。これをプログラミングするためには、どのようにしたらよいでしょうか?

そのような継続的変化を実現するために、C++BuilderではTimerコンポーネントが用意されています。本章ではこのTimerコンポーネントを活用したプログラムを作成してみましょう。

 

以下本章では、まず6-1で、ストップウォッチの作成を通じてTimerコンポーネントの基本的な使用方法を学習し、次の6-2でモグラたたきゲームを作ります。この時点でTimerの基本的な活用方法が身に付くはずです。もっとも、モグラたたきゲームと言ってもここで作るのはその“原型”のようなものに過ぎません。実際のゲームらしくするためには幾つかの機能を付け加える必要があります。もし、不満を感じたら各自チャレンジしてみてください。最後に、6-3で5章の5-11で学習したスロットマシンを、Timerコンポーネントを用いて改良してみます。数字やパネルの色がパラパラと変化するようにするのです。これは、6-2までに学習した内容の応用なので、説明を読まなくても自力でプログラムを作成できるかもしれません。ともかくここまでやれば一通りの活用方法を学んだことになり、Timerの学習は“修了”です。

 

Timerを用いたプログラムは動きが伴うので、実際の作成・実行を通じてでなければ内容を理解する事が困難です。逆に“百聞は一見にしかず”のことわざ通り、動作結果を見るとその内容は一目瞭然です。“動き”のあるプログラムは見ていて楽しいものです。各自、動作結果を楽しみながら学習を進めてください。

 

<本章の構成>

 


6−1  ストップウォッチの作成

6−2  モグラたたきゲームの作成

6−3  スロットマシンの作成


6−1 ストップウォッチの作成

練習問題

 

 まず、本節では次のようなデジタル・ストップウォッチを作成してみましょう。これは、[開始]ボタンをクリックすると1秒刻みで時刻が進行し、[停止]ボタンを押すと、その進行が停止するというプログラムです。

 

 

[開始]ボタンをクリックすると、時刻が進行する。

 

 

 

 

 まず、上のようなフォームを作成し、さらにSystemタグ内にあるTimerコンポーネントをフォームに貼り付けてください。Timerコンポーネント(自体)は実行時には見えません。したがって貼り付ける場所もどこでも結構です。このようなコンポーネントを非ビジュアルコンポーネントと呼びます。

 

次に各コンポーネントのNameプロパティを次のように指定してください。

 

スピンエディット:CSpinEdit   開始ボタン:ButtonStart   停止ボタン:ButtonStop

 

 

 Timerコンポーネントについては次の様にプロパティを指定してください。

 

Interval:1000(1秒の意味)     Enabled:false    Name:Timer

 

 ここでTimerコンポーネントについて説明しておきましょう。

 

@   Timerは、ある一定時間(間隔)毎に処理を行うという用途に用いられるコンポーネントです。

A   その一定時間(間隔)はIntervalプロパティで指定します。指定は1/1000秒単位で行うので、例えば1000は1秒という意味です。

B   一定時間(間隔)毎にOnTimerイベントが発生します。したがって、処理内容はこのOnTimerイベントのイベントハンドラに記述します。

 今の場合、(1秒ごとに呼び出される)OnTimerイベントの処理内容は、

 

時刻を1秒ずつ増やす、つまりCSpinEdit内の数字に1を加える。

 

となります。

 

それでは、以上を頭に入れておいてプログラミングに入りましょう。

まず、[開始]および[停止]ボタンクリックのイベントハンドラはそれぞれ次の様になります。

 


void__fastcall TForm1::ButtonStartClick(TObject *Sender)

{

   Timer->Enabled = true;

}

 


void__fastcall TForm1::ButtonStopClick(TObject *Sender)

{

   Timer->Enabled = false;

}

 

Timerの動作開始および停止はEnabledプロパティの値で切り替えることができます。[開始]ボタンをクリックした時にTimerが作動するようにしたいので、ここでEnabledプロパティをTrueにします。一方、[停止]ボタンをクリックした時にTimerを停止するのですから、EnabledをFalseとすればよいのです。この点は皆もすぐに理解できるでしょう。

続いて、OnTimerイベントのイベントハンドラは前ページで説明した通り、次の様になります。

void__fastcall TForm1::TimerTimer(TObject *Sender)

{

   CSpinEdit->Value = CSpinEdit->Value + 1;

}

 

つまり、Intervalで指定した1秒毎にCSpinEditの値を1ずつ増やせば良い訳です。これで、完成です。動作を確かめてください。

 

このように、Timerイベントを扱う場合、Enabledプロパティの切り替えと、Timerイベントのイベントハンドラの作成がポイントになります。

テキスト ボックス: コラム  コンポーネントとコントロールについて

5章5-13でEditコンポーネントのヘルプを開いたときに、

「TEdit オブジェクトは,Windows の 1 行編集コントロールのラッパーです。」

という説明があり、コントロールという言葉を使っていた点が気になった人がいるかもしれません。
実は、ButtonやEditのように、実行時に表示されるコンポーネントをC++Builderでは「コントロール」と呼び、区別しています。コンポーネントには、コントロールと、ここで学んだTimerのような非ビジュアルコンポーネントの2種類があります。
 

 

 

 

 

 

 

 

 

 

 

 


【基礎課題6-1-1】

 

上の【練習問題】に[リセット]ボタンをつけ、これをクリックすると初期状態、つまり0秒の状態に戻るようにしてください。

 

 

【応用課題6-1-A】

 

 【練習問題】を修正して、今度は次の様に制限時間をカウントするタイマーを作成しましょう。 動作内容は以下の通りです。なお、今の場合[停止]ボタンは必要ないので削除しました。

 

今、適当に制限時間を設定します(この例では10秒)。

 

 

 

 

 

 

 

 

 

[開始]ボタンをクリックするとカウントが開始され、残り時間(秒数)が減って行きます。そして、残り時間が0となった時点で、“終了しました!”というメッセージが現れてタイマーは止まります。

 

 

ヒント → 次ページ参照

 

ヒント

OnTimerイベントの処理内容が変わります。その処理内容は次の様になるはずです。

残り時間を一つ(1秒)減らす

 
 

 

 

if (残り時間=0) 

‘終了しました!’を表示する

Timerを止める

else

   何もしない

 
 

 

 

 

 

 

 


【応用課題6-1-B】

 

【応用課題6-1-A】に[リセット]ボタンをつけましょう。動作内容は以下の通りです。

 

【応用課題6-1-A】では、制限時間が経過すると、次の状態になっているはずです。

 

 

 

 

 

 

 

 

 

 

ここで[リセット]ボタンをクリックすると、左のように、初期画面に戻るようにします。ここに制限時間(残り時間)も最初に設定した値(この例では10秒)に戻るようにしてください。

 

 

 

 

 

 

<ヒント>

 最初に設定した制限時間の値を何らかの変数に格納しておく必要があります。そしてそれはグローバル変数でなければなりません。

 


6-2 モグラたたきゲームの作成

 ここでは、お待ちかねのモグラたたきゲームを作成しましょう。Timerコンポーネントを活用することで、モグラが出たり引っ込んだりする動きを実現することができます。

 

【練習問題】

 

まず、3つの穴のどれか一つからモグラが出てくるという、プログラムを作りましょう。動作内容は次の通りです。

プログラムを起動し、

 

 

[ゲーム開始]ボタンをクリックすると、どれか一つの穴からランダムにモグラが出てきます。

 

 

このように、どれか一つの穴からモグラが出てくるという動作が[ゲーム終了]ボタンをクリックするまで続きます。

 

それでは、プログラミングに入りましょう。


次のようにフォームをデザインしてください。

 

 

 各ボタンコンポーネントのNameプロパティを次の通り指定してください。

ButtonのNameプロパティ>

左のモグラ

中央のモグラ

右のモグラ

ゲーム開始

ゲーム終了

ButtonMogura1

ButtonMogura2

ButtonMogura3

ButtonStart 

ButtonStop

 

Timerについては以下のようにコンポーネントを指定してください。

Name

Interval

Enabled

TimerMogura

200

false

 

さて、前節と同様、[ゲーム開始]および[ゲーム終了]ボタンのOnclickイベントハンドラにおいて、Timerの作動開始と停止を、Enabledプロパティの切り替えで行います。

void__fastcall TForm1::ButtonStartClick(TObject *Sender)

{

   TimerMogura->Enabled = true;

   randomize;

}

void__fastcall TForm1::ButtonStopClick(TObject *Sender)

{

   TimerMogura->Enabled = false;

}

 

ここに、[ゲーム開始]ボタンのイベントハンドラにおいて「Randomize」が挿入されていますが、これは、この後乱数を用いてどの位置(穴)からモグラが現れるかを決めるためです。

 

今、OnTimerイベントにおいて、(3つの内の)どこかの穴からモグラを出現させます。その出現位置を0〜2の乱数を用いて次に様に決めましょう。

 

0:左の穴から出現  → モグラ1を表示

1:中央の穴から出現 → モグラ2を表示

2:右の穴から出現  → モグラ3を表示

 

ここに、ボタンの表示/非表示はvisibleプロパティの値で指定できますから、OnTimerイベントのイベントハンドラは次のようになります。

void__fastcall TForm1::TimerMoguraTimer(TObject *Sender)

{

   switch (random(3)) {

       case 0:

           ButtonMogura1->Visible=true;

           ButtonMogura2->Visible=false;

           ButtonMogura3->Visible=false;

           break;

       case 1:

           ButtonMogura1->Visible=false;

           ButtonMogura2->Visible=true;

           ButtonMogura3->Visible=false;

           break;

       case 2:

           ButtonMogura1->Visible=false;

           ButtonMogura2->Visible=false;

           ButtonMogura3->Visible=true;

           break;

   }  

}

 

 プログラムの内容は分かりますね。

これで完成です。早速実行してみてください。モグラが現れては消える、という動作が再現されると思います。

 

【基礎課題6-2-1】

 

 上の【練習問題】のままではゲームになりません。そこで、モグラが現れている時にそれをたたくと(モグラが表示されている時にクリックすると)、得点が加えられるようにしましょう。

 

 

なお続けて、ゲームを行うときに以前のゲーム得点が残っていてはまずいですね。そこで、[ゲーム開始]ボタンをクリックしたときに、得点が0にもどるようにしておきます。

 

 まず、フォームを作成しましょう。【練習問題】のフォームに上のように得点欄を付け加え、EditのNameプロパティを「EditTokuten」としてください。

 

 次に、得点を加える部分をプログラミングします。

下の空欄を埋めて、左のモグラ(ボタン)をクリックした時のイベントハンドラを完成させてください。

 

void__fastcall TForm1::ButtonMogura1Click(TObject *Sender)

{

   if(ButtonMogura1->Visible ==true) {

 


       EditTokuten->Text =                                         

 

   }

}

 

(クリックしたときに、モグラボタンが表示されていれば)現在の得点に1(点)を加えればよいのですね。EditのTextプロパティがAnsiString(文字列)型であることに注意してください。残りの二つのモグラ(ボタン)についても同様にプログラミングできます。

 

最後に、[開始]ボタンをクリックしたときのイベントハンドラに得点を0に戻す部分を付け加えてください。

 

void__fastcall TForm1::ButtonStartClick(TObject *Sender)

{

   TimerMogura->Enabled = true;

   randomize;

 

 

}

 

このように得点をつけることで、ようやくゲームらしくなりました。

 

【基礎課題6-2-2】

 

【基礎課題6-2-1】のプログラムでは、[ゲーム終了]ボタンをクリックし終了させた後も、[モグラ]ボタンをクリックすると得点が加算されるようになっています。これではまずいですね。

 

そこで、[ゲーム終了]ボタンをクリックすると、左のように[モグラ]ボタンが選択不可になるようにしてください。

 

 

 

 

同時に、[ゲーム開始]ボタンをクリックした後に、[モグラ]ボタンを選択可とすることも忘れないでください。これで、ゲーム終了後にも得点が加算される、という“不正”を防ぐことができるようになりました。

 

【応用課題6-2-A】

 

実際のゲームでは、自分で[終了]ボタンをクリックするのではなく、予め制限時間が決められているのが普通です。そこで、制限時間を30秒として、この制限時間内で得点を競うというゲームにしてください。作成するプログラムの動作内容は次の通りです。

 

プログラムを起動すると下の画面が現れます。ここで、[ゲーム開始]ボタンをクリックするとゲームが始まります。

 

 

30秒経過すると、ゲーム終了と表示され、ゲームは終わります。

 

ここで、[ゲーム開始]ボタンをクリックすると、初期画面に戻り、再びゲームが開始されます。

 

<ヒント>

残り時間をカウントするために、新たにTimerを加えます。したがってこのプログラムでは、2つのTimerを用います。

 

【応用課題6-2-B】

 

 今までは、一匹のモグラが3つの穴のどれかから(必ず)出てくるという設定でした。したがって、常にどれかの穴から一匹(だけ)モグラが出てきました。

 

今度は、モグラは3匹でそれぞれの穴から(独立に)出たり入ったりするというように設定を変えましょう。つまり、ある瞬間には、モグラが一匹も現れなくなり、またある瞬間には次のように複数のモグラが同時に現れることもある、ということです。

 

 

 このように、【応用課題6-2-A】のプログラムを改良してください。

 

ヒント

今までは、1種類の乱数(0〜2)を発生させ、3つのモグラのどれかを表示させていました。今度は、3種類の乱数(それぞれ0か1)を発生させ、それぞれを各モグラの表示/非表示の決定に用いればよいのです。


6-3 スロットマシンの作成

 5-11節で作成したスロットマシンを、Timerを使って改良してみましょう。

 

【練習問題】

 

次のようなプログラムを作ってみましょう。

テキスト ボックス: A [停止]ボタンをクリックするとその時点で表示が止まります。テキスト ボックス: @ [開始]ボタンをクリックすると、3つの欄に0〜2までの乱数が次々に表示されます。

 

 

 

 

 

 

 

 

 

 

 

 


まず、上のようなフォームを作成し、各コンポーネントのNameプロパティを次に様に指定してください。

 

左のスピンエディット

中央のスピンエディット

右のスピンエディット

開始ボタン

停止ボタン

CSpinEditLeft

CSpinEditCenter

CSpinEditRight

ButtonStart

ButtonStop

 

続いてTimerコンポーネントのプロパティを次のように指定してください。

Name

Interval

Enabled

TimerSlot

100

false

 

 

 

 

これまで同様、[開始]および[停止]ボタンのイベントハンドラは、次の様になります。

void__fastcall TForm1::ButtonStartClick(TObject *Sender)

{

   TimerSlot->Enabled = true;

   randomize;

}

void__fastcall TForm1::ButtonStopClick(TObject *Sender)

{

   TimerSlot->Enabled = false;

}

 

今の場合、OnTimerイベントの処理は、3つの乱数の値を、スピンエディットに表示させるという処理なので、次の様になります。

void__fastcall TForm1::TimerSlotTimer(TObject *Sender)

{

   CSpinEditLeft->Value = random(3);

   CSpinEditCenter->Value = random(3);

   CSpinEditRight->Value = random(3);

}

 

これで、数字がパラパラと変わって表示されるはずです。動作を確認してください。

 

【基礎課題6-3-1】

 次に、3つの数字が揃った場合には、「大当たり!」、それ以外の場合は「はずれ!」と表示する部分を付け加えましょう。

 

<3つの数字が揃った場合>                <揃っていない場合>

       

 

 まず、判定欄のEditをフォームに貼り付け、Nameプロパティを「EditHantei」としてください。

 (数字が揃っているかどうかの)判定は、[停止]ボタンのイベントハンドラ内で行われます。以下の下線部を埋めてプログラムを完成させてください。

 

void __fastcall TForm1::TimerSlotTimer(TObject *Sender)

{

   TimerSlot->Enabled = false;

   if( (                                                         )

        &&

        (                                                         ) ) {

       EditHantei->Text = "大当たり!";

   else {

       EditHantei->Text = "はずれ!";

   }

}

 

 

※ 判定表示させるように変更したことに応じて、[開始]ボタンのイベントハンドラに、(以前の)判定を消去する部分を付け加えることを忘れないでください。


【応用課題6-3-A】

 

【基礎課題6-3-1】では、0〜2の数字を表示させましたが、これを5-11節【応用課題5-11-B】のように3つのパネルの色が変わるように修正してください。

 

 

3つの乱数の値を、3つのパネルの色に割り当てる処方は、【応用課題5-11-B】と全く同様です。今の場合、それをOnTimerイベントの中で行うという部分が異なるだけです。

 

【応用課題6-3-B】

 

パネルの色を次々に変えるという処方を応用して、次のような簡易ルーレットを作ってください。

動作内容は次の通りです。

プログラムを起動すると、下のフォームが現れます。

 

 

ここで、[開始]ボタンをクリックすると、点灯している部分(黄色の部分)が時計回りに移動し、[停止]ボタンをクリックした時の位置によって、次のように得点が表示されるというものです。

 

 

点灯していない部分(パネル)の色は「clBtnFace」としています。

 

なお、[開始]ボタンをクリックすると最上部の位置つまり100点の位置から新たに始まるものとします。

 

まず、自分でどのようにプログラミングすれば良いかを考えてください。ポイントはOnTimerイベントのイベントハンドラをどのように記述するか、です。

どうしても分からない場合は下のヒントを参照してください。

 

ヒント

@             現在どの位置が点灯しているのかを知らなければなりません。そこで、点灯位置の情報を格納する変数を例えばNum(整数型変数)としておきます。→グローバル変数とする必要があります。

A             Numは1〜4の値をとるものとし、Numの値に応じて点灯位置を次のように決めます。

Num   1   2   3    4

位置  上   右   下   左 

B             OnTimerイベントにおいてこのNumの値を1ずつ増やして行けば、点灯位置が順次ずれることになります。ただし、Num=4の次はNum=1としなければなりません。

C             点灯位置が決まったら、その位置のパネルの色を黄色(clYellow)にし、一つ前の点灯位置にあるパネルの色を元の色(clBtnFace)に戻します。こうすることによって、点灯位置がずれて行く動作を実現できます。