【Delphi / C++Builder Starter チュートリアルシリーズ】 第6回 ‟ミニゲームを作ってみよう„ [JAPAN]

Posted by on in Programming


【Delphi / C++Builder Starter チュートリアルシリーズ】

第6回 ‟ミニゲームをつくってみよう„

 10月24日より始まりました 「Delphi / C++Builder Starter チュートリアルシリーズ」。全10回、12月26日まで、毎週17時より30分間で、無料でダウンロード&利用できる開発環境のDelphi / C++Builder Starter エディションを使用して、ゲームを作るまで一通り、セミナーを実施してまいります。

このブログでは第6回のサマリーと参考情報など掲載いたします。 

なお、前回および初回分の内容に関するブログ記事は以下のリンクからお読み頂けます。

<< 第5回 ‟イベントに合わせて動かしてみよう„ 
<< 第1回 ‟無料で始めよう アプリ作成„ 

 

[アジェンダ]

 

  • ランダムな結果を得る方法を知る
  • データの入れ物(変数)を知る
  • 処理の場合分け(条件分岐)を知る

 

[開発環境インストール]

 

[Delphi / C++Builder Starter チュートリアルシリーズ]  第1回 ‟無料で始めよう アプリ作成„をご参考になり、開発環境をインストールしてください。

 

[第6回のサンプルコードのダウンロード]

以下のURLよりzip形式でダウンロード頂けます。演習と応用すべてのコードがDelphi/C++の両方とも含まれています。

 

https://github.com/kazinoue/2016_StarterTutorial_S1-6/archive/master.zip

 

[演習1] さいころ(1~6の値を得る)アプリを作る。

1~6のランダムな値を取得するために Random(6) という処理を実行しています。これは6つの値(0, 1, 2, 3, 4, 5) の中からいずれかの値、小数点を含まない数値(整数)をランダムに返します。指定した数値自体は返されないことに注意が必要です。6 のように処理に対して与えるパラメータのことを引数と言います。なお、引数を与えずに実行した場合は 0 以上 1 未満の小数点を含む数値(実数)が返されます。 

Random の仕様については以下のページもお読みください。
http://docwiki.embarcadero.com/Libraries/ja/System.Random

また、この処理では Random の戻り値を変数に保存しています。変数はデータを一時的に保管するための入れ物です。保管するデータの種類に合わせて予め宣言しておく必要があります。演習1 の時点では変数を使う必要は無いのですが、演習2 へのつなぎのためにあえて変数を使っています。

 

Delphi

//
procedure TForm1.ButtonThrowDiceClick(Sender: TObject);
var
  // 変数 DiceValue の定義
  //
  // 実演では手作業で入力していたが、
  // IDE の入力支援機能を使えば手作業で入力する必要はない。
  // その変数名が登場した箇所で [ctrl] 実際には begin - end の間で
  // Ctrl + Shift + V を押すだけで自動的に変数定義が追加できる。
  DiceValue: Integer;
begin
  // Random(6) は 0, 1, 2, 3, 4, 5 の値をとるので、
  // さいころとして使うために + 1 する。
  DiceValue := Random(6) + 1;

  // Memo1 の先頭にさいころの値を差し込む。
  // DiceValue は数値型(Integer) だけど、
  // 表示の際は文字列型にする必要があるので toString を指定する。
  Memo1.Lines.Insert(0,DiceValue.toString);
end;

 

C++

//
void __fastcall TForm2::ButtonThrowDiceClick(TObject *Sender)
{
	// 変数 DiceValue の定義
	int DiceValue;

	// Random(6) は 0, 1, 2, 3, 4, 5 の値をとるので、
	// さいころとして使うために + 1 する。
	DiceValue = Random(6) + 1;

	// Memo1 の先頭にさいころの値を差し込む。
	// DiceValue は数値型(int) だけど、
	// 表示の際は文字列型にする必要があるので IntToStr で型変換する。
	Memo1->Lines->Insert(0,IntToStr(DiceValue));
}

 

[演習2] 疑似さいころ(1~6の値を得る)に条件分岐を追加する。

演習2では、さいころの値が1のときに表示内容を変える処理を実装しました。

すでに作成済みの処理に対して以下のコードを追加すると、1の目が出たときに追加のメッセージを表示させることができます。

Delphi

//
  // if文による条件分岐
  if DiceValue = 1 then
    Memo1.Lines.Insert(0,'1の目が出ました');

C++

//
	// if文による条件分岐
	// Delphiとは違い、then を記述する必要はない
	if (DiceValue == 1)
		Memo1->Lines->Insert(0,L"1の目が出ました");
}

Delphi と C++ では if文での値比較の式が若干異なることに注意してください。Delphi では値の代入用には := を使用し、値の比較に = を使用しています。これは数学の等式の書き方と類似しています。しかし C++ では代入用に = を使うルールのため、値の比較では == と書くルールです。

 

ただし C++ で "if DiceValue = 1" と書いても文法エラーにはなりません。この if 文は、「DiceValue の値を 1 にする」という処理が成功したかどうか、を判定する処理になっています。そしてこの処理は基本的に必ず成功しますから、成功した場合の処理だけが必ず動作することになります。


なお、上記の例では実行する処理が1つだけでしたが、複数の処理をまとめて行う場合や、条件を満たさない場合の処理を行う場合は以下のように記述できます。

Delphi

//
  // if文による条件分岐
  if DiceValue = 1 then
  begin
    処理1;
    処理2;
    処理3;
  end;

  // if文による条件分岐
  if DiceValue = 1 then
  begin
    処理1;
    処理2;
    処理3;
  end
  else
  begin
    処理4;
    処理5;
    処理6;
  end;

C++

//
  // if文による条件分岐
  if DiceValue == 1 {
    処理1;
    処理2;
    処理3;
  }

  // if文による条件分岐
  if DiceValue == 1 {
    処理1;
    処理2;
    処理3;
  }
  else
  {
    処理4;
    処理5;
    処理6;
  }

Delphi の場合は一連の処理の終わりにセミコロンをつけるルールですが、if 文の場合は条件判定がOKだった場合、NGだった場合を含めて一続きの処理ですから、if 文の最後の end にだけセミコロンが付きます。ただし begin - end の中に記述する処理では個別にセミコロンをつけます。

 

C++ の場合は処理のブロックの開始と終了を { と } で囲むルールです。これは Delphi の begin - end に対応していますが、これらのブロック記号に対してセミコロンをつける必要はありません。

 

なお、ある変数に対して値の場合分けを3つ以上作成したい場合に if 文を使うと冗長な書き方になってしまう場合があります。このような場合は case 文というものを用いると処理がシンプルに記述できることがあります。case 文については今回のセッションで紹介していませんが、次回以降で使用例が出てきます。また docwiki には以下のドキュメントがあります。

http://docwiki.embarcadero.com/RADStudio/Berlin/ja/%E5%AE%A3%E8%A8%80%E3%81%A8%E6%96%87%EF%BC%88Delphi%EF%BC%89#case_.E6.96.87

http://docwiki.embarcadero.com/RADStudio/Berlin/ja/Case

 

[演習3] 特定の値が出るまで繰り返す条件分岐を入れてみる。

演習2の処理ではif文による条件判定が1回だけ行われるコードでしたが「条件を満たす間は同じ処理を実行し続ける」という処理を書くこともできます。

この処理は while ループ、until ループという方法で書けます。

while (Delphi++)

//
procedure TForm1.ButtonLoopWhileClick(Sender: TObject);
var
  DiceValue: Integer;
begin
  Memo1.Lines.Insert(0,'');

  // 初回のサイコロ振り
  DiceValue := Random(6) + 1;

  // while ループは、条件を満たす間は処理を実行する。
  // 「1が出るまで繰り返す」という処理は「1が出ない間は繰り返す」と
  // 表現することができる。
  // なお、whileループは最初に継続条件の判定を行うため、
  // while の前に値が設定されていることが必要。
  // ここでは「1が出るまで = 1が出ない間」はループを続ける。
  while (DiceValue <> 1) do
  begin
    Memo1.Lines.Insert(0,DiceValue.toString + ' はずれ' );

    // ループの中でサイコロを振る処理
    DiceValue := Random(6) + 1;
  end;

  // この処理は while の後にあるので、
  // 今回の実装では while の条件が不成立となった後に
  // かならず実行される処理となる。
  Memo1.Lines.Insert(0,DiceValue.toString + ' キター');
end;

While (C++)

//
void __fastcall TForm2::ButtonLoopWhileClick(TObject *Sender)
{
	// 変数 DiceValue の定義
	int DiceValue;

	Memo1->Lines->Insert(0,L"");

	// 初回のサイコロ振り
	DiceValue = Random(6) + 1;

	// while ループは、条件を満たす間は処理を実行する。
	// 「1が出るまで繰り返す」という処理は「1が出ない間は繰り返す」と
	// 表現することができる。
	// なお、whileループは最初に継続条件の判定を行うため、
	// while の前に値が設定されていることが必要。
	// ここでは「1が出るまで = 1が出ない間」はループを続ける。

	// Delphi では <> で比較したが、C++ では != を用いる
	while (DiceValue != 1) {
		Memo1->Lines->Insert(0,IntToStr(DiceValue) + L" はずれ" );

		// ループの中でサイコロを振る処理
		DiceValue = Random(6) + 1;
	}
	// この処理は while の後にあるので、
	// 今回の実装では while の条件が不成立となった後に
	// かならず実行される処理となる。
	Memo1->Lines->Insert(0,IntToStr(DiceValue) + L" キター" );
}

Until (Delphi)

//
procedure TForm1.ButtonLoopUntilClick(Sender: TObject);
var
  DiceValue: Integer;
begin
  Memo1.Lines.Insert(0,'');

  // repeat 〜 until は「ループ処理を少なくとも1回実行し、
  // 条件が満たされるまでループを繰り返す」ように動作する。
  repeat
    // さいころを振る。
    DiceValue := Random(6) + 1;

    // while ループと同様に 'はずれ' の文字を出すためには
    // ループの中で値を検証せねばならない。
    if (DiceValue <> 6) then
      Memo1.LInes.Insert(0,DiceValue.toString + ' はずれ');

  // ここまでのループの内容を 6 の目が出るまで続ける。
  until (DiceValue = 6);

  // この処理は until の後にあるので、
  // 今回の実装では until の終了条件が満たされた後に
  // かならず実行される処理となる。
  Memo1.Lines.Insert(0,DiceValue.toString + ' キター');
end;

C++ には Until ループはありませんが、代わりに do - while という書き方ができます。

//
void __fastcall TForm2::ButtonLoopUntilClick(TObject *Sender)
{
	// 変数 DiceValue の定義
	int DiceValue;

	Memo1->Lines->Insert(0,L"");

	// C/C++ には repeat 〜 until は無く、
	// 代わりに do 〜 while という形で while ループを書く。
	// while なので記述すべき条件はループの継続条件である。
	// (repeat はループの終了条件)
	do {
		// さいころを振る。
		DiceValue = Random(6) + 1;

		// 前述の while ループと同様に 'はずれ' の文字を出すためには
		// ループの中で値を検証せねばならない。
		if (DiceValue != 6)
		  Memo1->Lines->Insert(0,IntToStr(DiceValue) + L" はずれ");

	}
	// ここまでのループの内容を 6 の目が出るまで続ける。
	while (DiceValue != 6);

	// Delphi の repeat と同じロジックで判定式を書きたい場合は
	// 以下のように判定式全体に ! をつければよい(あまり意味がないが)
	// while ( !(DiceValue == 6) );


	// この処理は until の後にあるので、
	// 今回の実装では until の終了条件が満たされた後に
	// かならず実行される処理となる。
	Memo1->Lines->Insert(0,IntToStr(DiceValue) + L" キター" );
}



なお、while, until ともに条件判定を行いながら繰り返す処理ですが、もっと単純なループ(変数の値を所定の範囲で変えながら繰り返す)には for という処理を用います。for ループはここでは紹介しませんが、docwiki のURLを例示します。

http://docwiki.embarcadero.com/RADStudio/Berlin/ja/%E5%AE%A3%E8%A8%80%E3%81%A8%E6%96%87%EF%BC%88Delphi%EF%BC%89#for_.E6.96.87

http://docwiki.embarcadero.com/RADStudio/Berlin/ja/For%EF%BC%88C%2B%2B%EF%BC%89

 

[応用] ミニゲームを作る。

ここまでの内容でご説明した機能を組み合わせることで、ちょっとしたゲームを作ることができます。チュートリアルセッションでは「表示された数と同じ数を選ぶ早押しゲーム」的なものをご紹介しました。

このゲームで取り扱うイベントはたった2つだけです。いままでの演習に比べるとコード量は若干増えていますが、理解するのが難しい処理は無いと思います。

このゲームのサンプルコードは、先にご案内したURLからダウンロードしたアーカイブ内の MiniGme フォルダにありますので、これを実際に開いて動かしながら中身を読んで頂くのがよいかと思います。

 

■次回は12月5日(月)17:00より  “シューティングゲーム キャラクターを貼り付けよう そして動かそう„

 をお送りします。ここからは4週間にわたってシューティングゲーム作りを進めていきます。 

それでは、また来週!

<< 第5回 ‟イベントに合わせて動かしてみよう„ 

>> 第7回 ‟シューティングゲーム キャラクターを貼り付けよう そして動かそう„ 



About
Gold User, No rank,
Sales Consultant at Embarcadero Technologies, in Japan.

Comments

Check out more tips and tricks in this development video: