:演習24、自機の表示と左右移動、自機のアニメーション など
テーマ28 キーボードの状態を得る(再掲載)
・キー入力イベントを用いる手法は「キーを押している間、〇〇する」には向かない
・代わりにタイマーイベントを用いてキーボードの状態を得る処理を呼び出してもらうと良い
・これを実現するには、WindowsAPIを提供するDLL(動的リンクライブラリ)の一つである「user32.dll」を直接インポートする
・インポートの書式: [System.Runtime.InteropServices.DllImport("user32.dll")] ※セミコロン不要
・すると、これに含まれるGetKeyStateメソッドを外部定義指定により利用可能になる
・外部定義指定の書式: private static extern short GetKeyState(int nVirtKey);
・これでGetKeyStateメソッドに引数としてKey列挙子をint型にキャストして与えると、そのキーが押されていれば負の数が返される
・インポートと外部定義指定はクラス定義の先頭で行うこと
演習24 上矢印キーが押されていたらスコアアップ(再掲載)
・上矢印キーのKey列挙子はKeys.Up ・これを用いて、Playメソッド内で、上矢印キーが押されているかチェックし、押されていたらスコアをインクリメントしよう
作成例
//演習24 上矢印キーが押されていたらスコアアップ
using System; //フォームアプリケーションに必須
using System.Windows.Forms; //フォームアプリケーションに必須
using System.Drawing; //Image用
class Program : Form { //Formクラスを継承
[System.Runtime.InteropServices.DllImport("user32.dll")] //【追加】外部DLLのインポート
private static extern short GetKeyState(int nVirtKey); //【追加】メソッドの外部定義指定
Image backb = Image.FromFile("backb.bmp"); //背景画像の読込
Image enemy = Image.FromFile("enemy.gif"); //アイテム画像の読込
Image burn = Image.FromFile("burn.gif"); //効果用画像の読込
Pen mypen = new Pen(Color.Red, 2); //ペンを生成(初期値は赤、2)
Brush bred = new SolidBrush(Color.FromArgb(63, 255, 0, 0)); //ブラシを生成(赤の透明色)
Brush byel = new SolidBrush(Color.Yellow); //ブラシを生成(黄色)
Font f20 = new Font("メイリオ", 20, FontStyle.Bold); //フォントを生成(メイリオ20P太字)
Font f80 = new Font("メイリオ", 80, FontStyle.Bold); //フォントを生成(メイリオ80P太字)
int gamemode = 0; //0:スタート画面、1:プレイ画面
int score = 0; //スコア
int backy = 0; //背景画像のつなぎ目のY座標
Timer timer = new Timer(); //タイマーを生成
void DrawCircle(PaintEventArgs e, Pen p, int x, int y, int r) { //ペン、中心座標、半径を指定して円を描画
e.Graphics.DrawEllipse(p, x - r, y - r, 2 * r, 2 * r); //円の描画を呼ぶ
}
protected override void OnPaint(PaintEventArgs e) { //Formのメソッドをオーバーライド
base.OnPaint(e); //まず、Formクラスにおけるメソッドの内容(基本再描画処理)を実行
e.Graphics.DrawImage(backb, 0, backy); //背景画像を(0,つなぎ目)から描画
e.Graphics.DrawImage(backb, 0, backy - backb.Height); //背景画像をその上に描画
if (gamemode == 0) { //スタート画面
e.Graphics.DrawString("GAME1", f80, byel, 100, 100); //タイトルの描画
e.Graphics.DrawString("Hit Enter to Start", f20, byel, 200, 300); //メッセージの描画
} else if (gamemode == 1) { //プレイ画面
e.Graphics.FillRectangle(bred, 78, 411, 485, 64); //判定エリアを透明赤で塗りつぶす
e.Graphics.DrawRectangle(mypen, 78, 411, 485, 64); //判定エリアの描画
string s = String.Format("SCORE:{0:000,000}", score); //スコア文字列を編集
e.Graphics.DrawString(s, f20, byel, 400, 10); //スコア文字列の描画
}
}
void OnKeyDown(object o, KeyEventArgs e) { //キーボードが押された時に呼ばれるメソッド
if(e.KeyCode.ToString() == "Escape") { //Escキーが押されたら
Close(); //フォームアプリケーション終了
}
if(gamemode == 0 && e.KeyCode.ToString() == "Return") { //スタート画面でEnterキーが押されたら
gamemode = 1; //プレイ画面に切り替える
timer.Start(); //タイマー開始
Invalidate(); //画面再描画を依頼する
}
}
void Play(object o, EventArgs e) { //タイマーから呼ばれるメソッド
//score++; //【削除】スコアアップ
backy = (backy < backb.Height) ? backy + 1 : 0; //背景画像のつなぎ目を下げる
if (GetKeyState((int)Keys.Up) < 0) { //【以下追加】↑キーが押されている?
score++; //スコアアップ
}
Invalidate(); //画面再描画を依頼する
}
Program() { //コンストラクタ
DoubleBuffered = true; //ダブルバッファリングの導入
KeyDown += new KeyEventHandler(OnKeyDown); //キーボードが押された時に呼ばれるメソッドを登録
timer.Tick += new EventHandler(Play); //タイマーから呼ばれるメソッドを登録
timer.Interval = 10; //タイマーの間隔
}
static void Main() { //publicの指定は任意
Program p = new Program(); //継承したフォームのインスタンスを生成
p.Width = 660; //インスタンスのWidthプロパティに幅を代入
p.Height = 520; //インスタンスのHeightプロパティに高さを代入
p.Text = "Game"; //インスタンスのTextプロパティにフォーム名を代入
p.ControlBox = false; //ControlBoxを非表示にする
p.FormBorderStyle = FormBorderStyle.Fixed3D; //フォームサイズ変更を禁止
Application.Run(p); //インスタンスを画面に出す
}
}
演習25 自機を表示し左右に動かす
・自機の通常画像(player.gif)をダウンロードして配置・自機通常画像を初期位置(300,390)から描画 ・左矢印キー(Keys.Left)が押されていたら左へ10ドット、右(Keys.Right)なら右へ10ドット移動する ・赤い枠とスコアアップは削除
作成例
//演習25 自機を表示し左右に動かす
using System; //フォームアプリケーションに必須
using System.Windows.Forms; //フォームアプリケーションに必須
using System.Drawing; //Image用
class Program : Form { //Formクラスを継承
[System.Runtime.InteropServices.DllImport("user32.dll")] //外部DLLのインポート
private static extern short GetKeyState(int nVirtKey); //メソッドの外部定義指定
Image backb = Image.FromFile("backb.bmp"); //背景画像の読込
Image enemy = Image.FromFile("enemy.gif"); //アイテム画像の読込
Image burn = Image.FromFile("burn.gif"); //効果用画像の読込
Image playeri = Image.FromFile("player.gif"); //【追加】自機画像の読込
//Pen mypen = new Pen(Color.Red, 2); //【削除】ペンを生成(初期値は赤、2)
//Brush bred = new SolidBrush(Color.FromArgb(63, 255, 0, 0)); //【削除】ブラシを生成(赤の透明色)
Brush byel = new SolidBrush(Color.Yellow); //ブラシを生成(黄色)
Font f20 = new Font("メイリオ", 20, FontStyle.Bold); //フォントを生成(メイリオ20P太字)
Font f80 = new Font("メイリオ", 80, FontStyle.Bold); //フォントを生成(メイリオ80P太字)
int gamemode = 0; //0:スタート画面、1:プレイ画面
int score = 0; //スコア
int backy = 0; //背景画像のつなぎ目のY座標
int playerx = 300; //【追加】自機のX座標
Timer timer = new Timer(); //タイマーを生成
void DrawCircle(PaintEventArgs e, Pen p, int x, int y, int r) { //ペン、中心座標、半径を指定して円を描画
e.Graphics.DrawEllipse(p, x - r, y - r, 2 * r, 2 * r); //円の描画を呼ぶ
}
protected override void OnPaint(PaintEventArgs e) { //Formのメソッドをオーバーライド
base.OnPaint(e); //まず、Formクラスにおけるメソッドの内容(基本再描画処理)を実行
e.Graphics.DrawImage(backb, 0, backy); //背景画像を(0,つなぎ目)から描画
e.Graphics.DrawImage(backb, 0, backy - backb.Height); //背景画像をその上に描画
if (gamemode == 0) { //スタート画面
e.Graphics.DrawString("GAME1", f80, byel, 100, 100); //タイトルの描画
e.Graphics.DrawString("Hit Enter to Start", f20, byel, 200, 300); //メッセージの描画
} else if (gamemode == 1) { //プレイ画面
//e.Graphics.FillRectangle(bred, 78, 411, 485, 64); //【削除】判定エリアを透明赤で塗りつぶす
//e.Graphics.DrawRectangle(mypen, 78, 411, 485, 64); //【削除】判定エリアの描画
string s = String.Format("SCORE:{0:000,000}", score); //スコア文字列を編集
e.Graphics.DrawString(s, f20, byel, 400, 10); //スコア文字列の描画
e.Graphics.DrawImage(playeri, playerx, 390); //【追加】自機画像を描画
}
}
void OnKeyDown(object o, KeyEventArgs e) { //キーボードが押された時に呼ばれるメソッド
if(e.KeyCode.ToString() == "Escape") { //Escキーが押されたら
Close(); //フォームアプリケーション終了
}
if(gamemode == 0 && e.KeyCode.ToString() == "Return") { //スタート画面でEnterキーが押されたら
gamemode = 1; //プレイ画面に切り替える
timer.Start(); //タイマー開始
Invalidate(); //画面再描画を依頼する
}
}
void Play(object o, EventArgs e) { //タイマーから呼ばれるメソッド
backy = (backy < backb.Height) ? backy + 1 : 0; //背景画像のつなぎ目を下げる
//if (GetKeyState((int)Keys.Up) < 0) { //【以下削除】↑キーが押されている?
// score++; //スコアアップ
//}
if (gamemode == 1) { //【以下追加】プレイ画面
if (GetKeyState((int)Keys.Left) < 0) { //←キーが押されている?
playerx -= 10; //自機を左へ
}
if (GetKeyState((int)Keys.Right) < 0) { //→キーが押されている?
playerx += 10; //自機を右へ
}
}
Invalidate(); //画面再描画を依頼する
}
Program() { //コンストラクタ
DoubleBuffered = true; //ダブルバッファリングの導入
KeyDown += new KeyEventHandler(OnKeyDown); //キーボードが押された時に呼ばれるメソッドを登録
timer.Tick += new EventHandler(Play); //タイマーから呼ばれるメソッドを登録
timer.Interval = 10; //タイマーの間隔
}
static void Main() { //publicの指定は任意
Program p = new Program(); //継承したフォームのインスタンスを生成
p.Width = 660; //インスタンスのWidthプロパティに幅を代入
p.Height = 520; //インスタンスのHeightプロパティに高さを代入
p.Text = "Game"; //インスタンスのTextプロパティにフォーム名を代入
p.ControlBox = false; //ControlBoxを非表示にする
p.FormBorderStyle = FormBorderStyle.Fixed3D; //フォームサイズ変更を禁止
Application.Run(p); //インスタンスを画面に出す
}
}
演習25の続き 自機移動範囲の制限と画像変更
・←が押されているとき、自機左寄画像 playerl.gif を表示しよう・→が押されているとき、自機右寄画像 playerr.gif を表示しよう
・そして、自機が左端・右端から出ないように制限しよう ・なお、背景、自機の幅は.Widthを用いること ・つまり、左は0チェックで良いが、右はX座標が「背景.Width-自機.Width」を超えるとはみ出す可能性がある(マージンによって調整する) ヒント ・←と→が押されているとき、違う画像を表示するには、自機がどちらに向かっているのかを示す変数playervを用いると良い ・playerv(0:停止,-1:左,1:右)を、playメソッドの先頭で0にしておき、左や右が押されていたら、対応する値にする ・そして、onPaintメソッドでplayervの値に応じて描画する画像を選べば良い
作成例
//演習25 自機を表示し左右に動かす
using System; //フォームアプリケーションに必須
using System.Windows.Forms; //フォームアプリケーションに必須
using System.Drawing; //Image用
class Program : Form { //Formクラスを継承
[System.Runtime.InteropServices.DllImport("user32.dll")] //外部DLLのインポート
private static extern short GetKeyState(int nVirtKey); //メソッドの外部定義指定
Image backb = Image.FromFile("backb.bmp"); //背景画像の読込
Image enemy = Image.FromFile("enemy.gif"); //アイテム画像の読込
Image burn = Image.FromFile("burn.gif"); //効果用画像の読込
Image playeri = Image.FromFile("player.gif"); //【追加】自機画像の読込
Image playerl = Image.FromFile("playerl.gif"); //【追加】自機左寄画像の読込
Image playerr = Image.FromFile("playerr.gif"); //【追加】自機右寄画像の読込
Brush byel = new SolidBrush(Color.Yellow); //ブラシを生成(黄色)
Font f20 = new Font("メイリオ", 20, FontStyle.Bold); //フォントを生成(メイリオ20P太字)
Font f80 = new Font("メイリオ", 80, FontStyle.Bold); //フォントを生成(メイリオ80P太字)
int gamemode = 0; //0:スタート画面、1:プレイ画面
int score = 0; //スコア
int backy = 0; //背景画像のつなぎ目のY座標
int playerx = 300; //【追加】自機のX座標
int playerv = 0; //【追加】自機の左右移動(0:停止,-1:左,1:右)
Timer timer = new Timer(); //タイマーを生成
protected override void OnPaint(PaintEventArgs e) { //Formのメソッドをオーバーライド
base.OnPaint(e); //まず、Formクラスにおけるメソッドの内容(基本再描画処理)を実行
e.Graphics.DrawImage(backb, 0, backy); //背景画像を(0,つなぎ目)から描画
e.Graphics.DrawImage(backb, 0, backy - backb.Height); //背景画像をその上に描画
if (gamemode == 0) { //スタート画面
e.Graphics.DrawString("GAME1", f80, byel, 100, 100); //タイトルの描画
e.Graphics.DrawString("Hit Enter to Start", f20, byel, 200, 300); //メッセージの描画
} else if (gamemode == 1) { //プレイ画面
string s = String.Format("SCORE:{0:000,000}", score); //スコア文字列を編集
e.Graphics.DrawString(s, f20, byel, 400, 10); //スコア文字列の描画
switch (playerv) { //【以下追加】動きに合わせた自機画像を描画
case 0: e.Graphics.DrawImage(playeri, playerx, 390); break;
case 1: e.Graphics.DrawImage(playerr, playerx, 390); break;
case -1: e.Graphics.DrawImage(playerl, playerx, 390); break;
}
}
}
void OnKeyDown(object o, KeyEventArgs e) { //キーボードが押された時に呼ばれるメソッド
if(e.KeyCode.ToString() == "Escape") { //Escキーが押されたら
Close(); //フォームアプリケーション終了
}
if(gamemode == 0 && e.KeyCode.ToString() == "Return") { //スタート画面でEnterキーが押されたら
gamemode = 1; //プレイ画面に切り替える
timer.Start(); //タイマー開始
Invalidate(); //画面再描画を依頼する
}
}
void Play(object o, EventArgs e) { //タイマーから呼ばれるメソッド
backy = (backy < backb.Height) ? backy + 1 : 0; //背景画像のつなぎ目を下げる
if (gamemode == 1) { //【以下追加】プレイ画面
playerv = 0; //仮に自機は停止中とする
if (playerx > 0 && GetKeyState((int)Keys.Left) < 0) { //左端ではなく←キーが押されている?
playerx -= 10; //自機を左へ
playerv = -1; //左に移動中
}
if (playerx < backb.Width - playeri.Width && GetKeyState((int)Keys.Right) < 0) { //右端ではなく→キーが押されている?
playerx += 10; //自機を右へ
playerv = 1; //右に移動中
}
}
Invalidate(); //画面再描画を依頼する
}
Program() { //コンストラクタ
DoubleBuffered = true; //ダブルバッファリングの導入
KeyDown += new KeyEventHandler(OnKeyDown); //キーボードが押された時に呼ばれるメソッドを登録
timer.Tick += new EventHandler(Play); //タイマーから呼ばれるメソッドを登録
timer.Interval = 10; //タイマーの間隔
}
static void Main() { //publicの指定は任意
Program p = new Program(); //継承したフォームのインスタンスを生成
p.Width = 660; //インスタンスのWidthプロパティに幅を代入
p.Height = 520; //インスタンスのHeightプロパティに高さを代入
p.Text = "Game"; //インスタンスのTextプロパティにフォーム名を代入
p.ControlBox = false; //ControlBoxを非表示にする
p.FormBorderStyle = FormBorderStyle.Fixed3D; //フォームサイズ変更を禁止
Application.Run(p); //インスタンスを画面に出す
}
}
提出:演習25 自機を表示し左右に動かす(追加分は含んでもOK)
・自機通常画像を初期位置(300,390)から描画
・左矢印キー(Keys.Left)が押されていたら左へ10ドット、右(Keys.Right)なら右へ10ドット移動する
・赤い枠とスコアアップは削除
・→が押されているとき、自機右寄画像 playerr.gif を表示しよう
・そして、自機が左端・右端から出ないように制限しよう
・なお、背景、自機の幅は.Widthを用いること
・つまり、左は0チェックで良いが、右はX座標が「背景.Width-自機.Width」を超えるとはみ出す可能性がある(マージンによって調整する)
ヒント
・←と→が押されているとき、違う画像を表示するには、自機がどちらに向かっているのかを示す変数playervを用いると良い
・playerv(0:停止,-1:左,1:右)を、playメソッドの先頭で0にしておき、左や右が押されていたら、対応する値にする
・そして、onPaintメソッドでplayervの値に応じて描画する画像を選べば良い