ゲーム開発演習:画面遷移、タイマー処理 など
テーマ20 画面遷移【再掲載+α】
・タイトル画面とプレイ画面とゲームオーバー画面のように、画面が移り変わることを画面遷移という
・画面ごとに違うプログラムを用意して呼び出すこともあるが、プログラム内で「今はどの画面なのか」を変数で保持すればシンプルに記述できる
・この変数をゲームモードやシーン等と呼び、プログラム内で共有することで、画面遷移を見やすく記述できる
・ゲームモードやシーンに用いる値は列挙型(p.66)にすると意味がわかりやすくなる
・構造例:
class クラス名 : Form { //Formクラスを継承
enum { TITLE, PLAY, OVER }; //タイトル画面、プレイ画面、ゲームオーバー画面
int ゲームモード = TITLE;
その他の初期化処理を記述
protected override void OnPaint(PaintEventArgs e) { //描画処理のオーバライド
全画面に共通の描画処理
ゲームモードがTITLEならばタイトル画面を描画
ゲームモードがPLAYならばプレイ画面を描画
ゲームモードがOVERならばゲームオーバー画面を描画
}
public static void Main() { //実行用メソッド
:
}
クラス名() { //コンストラクタ
:
}
キー押し下げ時に実行されるメソッド {
共通処理(Escキーが押し下げられたら終了)
ゲームモードがTITLEの時にEnterやSpaceキーが押し下げられたらゲームモードをPLAYに
ゲームモードがOVERの時にEnterキーが押し下げられたらゲームモードをTITLEやPLAYに
}
}
テーマ21 画面再描画【再掲載】
・プログラム側で画面を書き換えても、そのままでは実画面には反映しない ・そのため、画面再描画をシステムに依頼する必要がある(OnPaintの直接実行は不可) ・画面再描画をシステムに依頼するにはContolクラスの静的メソッドInvalidateを引数なしで呼べばよい(「Contol.」は省略可) ・書式:Invalidate(); //画面再描画を依頼
演習21 タイトル画面の表示とプレイ画面への遷移
・テーマ20「画面遷移」の「構造例」を実装してタイトル画面の表示とプレイ画面への遷移を確認しよう ・タイトル画面は中央に「GAME」、中央下に「Hit Enter Key」と適当なフォントで表示 ・プレイ画面は中央に「START」と適当なフォントで表示 ※チェス盤、機体などの描画はいったん削除 ・エンターキー(キーコード:return)が押されたら、プレイ画面に遷移する ・この時、画面がチラつくが、対処は次のテーマで扱う
作成例
//演習21 タイトル画面の表示とプレイ画面への遷移
using System; //C#標準クラス用
using System.Windows.Forms; //Application、Formクラス用
using System.Drawing; //Size、Graphics、Image、Penクラス、Color構造体用
class Program : Form { //Formクラスの派生クラス
enum mode { TITLE, PLAY, OVER }; //タイトル画面、プレイ画面、ゲームオーバー画面
mode scene = mode.TITLE; //シーンをタイトル画面とする
Image backi; //画像ファイル用変数
protected override void OnPaint(PaintEventArgs e) { //描画処理のオーバライド
//全画面に共通の描画処理
base.OnPaint(e); //元のメソッドの内容を呼び出す
e.Graphics.DrawImage(backi, 0, 0); //画像をフォーム左上に配置
Font fm24 = new Font("メイリオ", 24, FontStyle.Bold); //フォントを生成
Brush bcyan = new SolidBrush(Color.Cyan); //シアン色のソリッドブラシを生成
switch (scene) { //シーンにより分岐
case mode.TITLE: //タイトル画面?
e.Graphics.DrawString("GAME", fm24, bcyan, 250, 200); //文字列を描画
e.Graphics.DrawString("Hit Enter Key", fm24, bcyan, 200, 400); //文字列を描画
break;
case mode.PLAY: //プレイ画面?
e.Graphics.DrawString("START", fm24, bcyan, 250, 200); //文字列を描画
break;
}
}
public Program() { //コンストラクタ
try { //例外処理対象
backi = Image.FromFile("backb2.bmp"); //背景画像ファイルを読み込む
} catch (Exception e) { //例外処理内容
MessageBox.Show(e.ToString()); //内容をメッセージボックスに表示
}
KeyDown += new KeyEventHandler(OnKeyDown); //キー押し下げ時のメソッドを登録
}
void OnKeyDown(object o, KeyEventArgs e){ //キー押し下げ時に呼ばれるメソッド
if (e.KeyCode.ToString() == "Escape") { //押されたキーのコードを文字列化したらEscape?
Close(); //フォームアプリケーションを終了
}
if (e.KeyCode.ToString() == "Return") { //押されたキーのコードを文字列化したらReturn?
scene = mode.PLAY; //シーンをプレイ画面とする
}
Invalidate(); //画面再描画を依頼
}
static void Main() { //実行用メソッド(publicはなくてOK)
Program f = new Program(); //自クラスのインスタンスを生成
f.Text = "Game"; //Form名を設定
f.StartPosition = FormStartPosition.Manual; //「手動設定」を設定
Point p = new Point(300,0); //X座標とY座標のPointインスタンスを生成
f.Location = p; //インスタンスプロパティで初期位置を設定
f.FormBorderStyle = FormBorderStyle.FixedSingle; //フォームサイズの固定化
f.ControlBox = false; //コントールボックスの非表示
f.ClientSize = new Size(640, 480); //クライアントサイズの幅と高さを指定
Application.Run(f); //生成済のインスタンスを実行
}
}
テーマ22 タイマー処理
・画面を自動的に変更したり、一定時間おきに何かを行いたい場合、タイマーを用いることが出来る ・タイマーは、System.Windows.Forms.Timerクラスのデフォルトコンストラクタで生成できる ・このTickイベントに、タイマーに呼び出して欲しいメソッドを登録する ・登録には、EventHandlerデリゲートを用いる 例: Timer t = new Timer(); t.Tick += new EventHandler(タイマーに呼び出して欲しいメソッド名); ・そして、インスタンスプロパティIntervalに、動作間隔をミリ秒で指定する 例:t.Interval = 20; ・それから、インスタンスメソッドStart()を呼ぶことでタイマーが動作する ・なお、タイマーに呼び出して欲しいメソッドは、戻り値型はvoid、引数型は(object, EventArgs)とすること ※ 通常、メソッド内でこの2引数を用いることはないが指定は必須
作成例
//演習22 画面に秒数を表示
using System; //C#標準クラス用
using System.Windows.Forms; //Application、Formクラス用
using System.Drawing; //Size、Graphics、Image、Penクラス、Color構造体用
class Program : Form { //Formクラスの派生クラス
enum mode { TITLE, PLAY, OVER }; //タイトル画面、プレイ画面、ゲームオーバー画面
mode scene = mode.TITLE; //シーンをタイトル画面とする
Image backi; //画像ファイル用変数
Timer t = new Timer(); //【追加】タイマーを生成
int sec = 0; //【追加】経過秒数
protected override void OnPaint(PaintEventArgs e) { //描画処理のオーバライド
//全画面に共通の描画処理
base.OnPaint(e); //元のメソッドの内容を呼び出す
e.Graphics.DrawImage(backi, 0, 0); //画像をフォーム左上に配置
Font fm24 = new Font("メイリオ", 24, FontStyle.Bold); //フォントを生成
Brush bcyan = new SolidBrush(Color.Cyan); //シアン色のソリッドブラシを生成
switch (scene) { //シーンにより分岐
case mode.TITLE: //タイトル画面?
e.Graphics.DrawString("GAME", fm24, bcyan, 250, 200); //文字列を描画
e.Graphics.DrawString("Hit Enter Key", fm24, bcyan, 200, 400); //文字列を描画
break;
case mode.PLAY: //タイトル画面?
e.Graphics.DrawString("START", fm24, bcyan, 250, 200); //文字列を描画
break;
}
e.Graphics.DrawString("" + sec , fm24, bcyan, 0, 0); //【追加】経過秒数を描画
}
public Program() { //コンストラクタ
try { //例外処理対象
backi = Image.FromFile("backb2.bmp"); //背景画像ファイルを読み込む
} catch (Exception e) { //例外処理内容
MessageBox.Show(e.ToString()); //内容をメッセージボックスに表示
}
KeyDown += new KeyEventHandler(OnKeyDown); //キー押し下げ時のメソッドを登録
t.Tick += new EventHandler(OnTimer); //【追加】タイマーイベント時のメソッドを登録
t.Interval = 1000; //【追加】タイマー動作間隔を1000ミリ秒指定
t.Start(); //【追加】タイマー開始
}
void OnKeyDown(object o, KeyEventArgs e){ //キー押し下げ時に呼ばれるメソッド
if (e.KeyCode.ToString() == "Escape") { //押されたキーのコードを文字列化したらEscape?
Close(); //フォームアプリケーションを終了
}
if (e.KeyCode.ToString() == "Return") { //押されたキーのコードを文字列化したらReturn?
scene = mode.PLAY; //シーンをプレイ画面とする
}
Invalidate(); //画面再描画を依頼
}
void OnTimer(object o, EventArgs e){ //【追加】タイマーイベント時に呼ばれるメソッド
sec++; //【追加】経過秒数インクリメント
Invalidate(); //【追加】画面再描画を依頼
}
static void Main() { //実行用メソッド(publicはなくてOK)
Program f = new Program(); //自クラスのインスタンスを生成
f.Text = "Game"; //Form名を設定
f.StartPosition = FormStartPosition.Manual; //「手動設定」を設定
Point p = new Point(300,0); //X座標とY座標のPointインスタンスを生成
f.Location = p; //インスタンスプロパティで初期位置を設定
f.FormBorderStyle = FormBorderStyle.FixedSingle; //フォームサイズの固定化
f.ControlBox = false; //コントールボックスの非表示
f.ClientSize = new Size(640, 480); //クライアントサイズの幅と高さを指定
Application.Run(f); //生成済のインスタンスを実行
}
}
テーマ23 ダブルバッファリング
・画面に動きがあるアプリケーションでは、画面の書き換えのタイミングのずれによりチラつきが起きる ・これを防ぐには、画像メモリを2重化し、描画を終えた画像メモリを表示用の画像メモリに一気に高速転送する ・この仕組みをダブルバッファリングという ・C#の.NETフレームワーク4.6以降のGDI+では(現行のVisualStudioでは)、 System.Windows.Forms.FormクラスのDoubleBufferedプロパティをtrueにすれば良い
演習23 プレイ画面に秒数を表示
・起動と同時ではなく、プレイ画面への遷移時にタイマーを起動し、画面左上に経過秒数を表示しよう ・ダブルバッファリングで画面のちらつきに対処しよう
作成例
//演習23 プレイ画面に秒数を表示
using System; //C#標準クラス用
using System.Windows.Forms; //Application、Formクラス用
using System.Drawing; //Size、Graphics、Image、Penクラス、Color構造体用
class Program : Form { //Formクラスの派生クラス
enum mode { TITLE, PLAY, OVER }; //タイトル画面、プレイ画面、ゲームオーバー画面
mode scene = mode.TITLE; //シーンをタイトル画面とする
Image backi; //画像ファイル用変数
Timer t = new Timer(); //タイマーを生成
int sec = 0; //経過秒数
protected override void OnPaint(PaintEventArgs e) { //描画処理のオーバライド
//全画面に共通の描画処理
base.OnPaint(e); //元のメソッドの内容を呼び出す
e.Graphics.DrawImage(backi, 0, 0); //画像をフォーム左上に配置
Font fm24 = new Font("メイリオ", 24, FontStyle.Bold); //フォントを生成
Brush bcyan = new SolidBrush(Color.Cyan); //シアン色のソリッドブラシを生成
switch (scene) { //シーンにより分岐
case mode.TITLE: //タイトル画面?
e.Graphics.DrawString("GAME", fm24, bcyan, 250, 200); //文字列を描画
e.Graphics.DrawString("Hit Enter Key", fm24, bcyan, 200, 400); //文字列を描画
break;
case mode.PLAY: //プレイ画面?
e.Graphics.DrawString("START", fm24, bcyan, 250, 200); //文字列を描画
e.Graphics.DrawString("" + sec , fm24, bcyan, 0, 0); //【移動】経過秒数を描画
break;
}
}
public Program() { //コンストラクタ
DoubleBuffered = true; //【追加】ダブルバッファリングを行う
try { //例外処理対象
backi = Image.FromFile("backb2.bmp"); //背景画像ファイルを読み込む
} catch (Exception e) { //例外処理内容
MessageBox.Show(e.ToString()); //内容をメッセージボックスに表示
}
KeyDown += new KeyEventHandler(OnKeyDown); //キー押し下げ時のメソッドを登録
t.Tick += new EventHandler(OnTimer); //タイマーイベント時のメソッドを登録
t.Interval = 1000; //タイマー動作間隔を1000ミリ秒指定
}
void OnKeyDown(object o, KeyEventArgs e){ //キー押し下げ時に呼ばれるメソッド
if (e.KeyCode.ToString() == "Escape") { //押されたキーのコードを文字列化したらEscape?
Close(); //フォームアプリケーションを終了
}
if (e.KeyCode.ToString() == "Return") { //押されたキーのコードを文字列化したらReturn?
scene = mode.PLAY; //シーンをプレイ画面とする
sec = 0; //【追加】経過秒数をクリア
t.Start(); //【移動】タイマー開始
}
Invalidate(); //画面再描画を依頼
}
void OnTimer(object o, EventArgs e){ //タイマーイベント時に呼ばれるメソッド
sec++; //経過秒数インクリメント
Invalidate(); //画面再描画を依頼
}
static void Main() { //実行用メソッド(publicはなくてOK)
Program f = new Program(); //自クラスのインスタンスを生成
f.Text = "Game"; //Form名を設定
f.StartPosition = FormStartPosition.Manual; //「手動設定」を設定
Point p = new Point(300,0); //X座標とY座標のPointインスタンスを生成
f.Location = p; //インスタンスプロパティで初期位置を設定
f.FormBorderStyle = FormBorderStyle.FixedSingle; //フォームサイズの固定化
f.ControlBox = false; //コントールボックスの非表示
f.ClientSize = new Size(640, 480); //クライアントサイズの幅と高さを指定
Application.Run(f); //生成済のインスタンスを実行
}
}
テーマ24 背景画像のスクロール
・背景画像を2枚用意して、1枚目の描画開始位置を少しずつずらし、空いた部分にもう1枚をつながるように描画すれば、 背景画像のスクロールが可能 ・なお、C#のGDI+では描画位置を示す座標を負の数や描画範囲外にできる。これにより、画像の一部を表示することが可能 ・画像全体が描画範囲の外になったら、描画対象から外すか、描画位置を変えると良い ・背景画像の縦スクロールの場合、描画開始位置が下端を超えたら0に戻すと良い ・背景画像は2枚になるが、画像オブジェクトは1つで良く、2つの参照変数で扱えば良い
提出:演習24 背景画像の縦スクロール