講義メモ:ゲーム開発演習

画像位置の管理、自弾の発射 など

演習26 画像の位置管理を中央座標に

・自機の位置を画像の中央座標で行うように変更しよう
・これに合わせて、画像、X座標、Y座標、左右方向の速度、表示状態を持つ構造体を定義する
  struct Item { 
    public Image i; //画像
    public int x;  //中心X座標
    public int y;  //中心Y座標
    public int hv; //左右方向の速度(左向きは負の数、右向きは正の数)
    public int v;  //表示状態(0:非表示、1以上:表示)
  }
・自機の情報は(今度登場する各アイテムの情報も)この構造体で表そう
・これに合わせて、中心座標を用いて画像を描画するDrawItemメソッドを作り、その内部でe.Graphics.DrawImageメソッドを実行するように
 しよう
  private void DrawItem(PaintEventArgs e, Item it) {
    xからit.i.Widthの半分を差し引いて左上X座標を得る
    yからit.i.Heightの半分を差し引いて左上Y座標を得る
    e.Graphics.DrawImage(it.i, 左上X座標, 左上Y座標);
  }
・合わせてデータメンバを整理しよう
    int gamemode = 0; //0:スタート画面、1:プレイ画面
    int score = 0; //スコア
    Image backi = Image.FromFile("backb.bmp"); //背景画像の読込
    Image playeri = Image.FromFile("player.gif"); //自機画像の読込
    Image playerl = Image.FromFile("playerl.gif"); //自機左寄画像の読込
    Image playerr = Image.FromFile("playerr.gif"); //自機右寄画像の読込
    Font font1 = new Font("メイリオ", 20, FontStyle.Bold); //フォントを生成(メイリオ20P太字)
    Font fontt = new Font("メイリオ", 80, FontStyle.Bold); //フォントを生成(メイリオ80P太字)
    Brush brushs = new SolidBrush(Color.Yellow); //ブラシを生成(黄色)
    Timer timer = new Timer(); //タイマーを生成
    int backy = 0; //背景画像のつなぎ目のY座標
    //int playerx = 300; //【削除】自機のX座標
    //int playerv = 0; //【削除】自機の左右移動量(0:停止,-1:左,1:右)
・合わせてMainメソッドも整理しよう
    Program f = new Program(); //継承したフォームのインスタンスを生成
    f.Size = new Size(660, 520); //インスタンスのSizeプロパティに高さと幅を代入
    f.Text = "Game"; //インスタンスのTextプロパティにフォーム名を代入
    f.ControlBox = false; //ControlBoxを非表示にする
    f.FormBorderStyle = FormBorderStyle.Fixed3D; //フォームサイズ変更を禁止
    Application.Run(f); //インスタンスを画面に出す

作成例

//演習26 画像の位置管理を中央座標に
using System; //フォームアプリケーションに必須
using System.Windows.Forms; //フォームアプリケーションに必須
using System.Drawing; //Image用
struct Item { //【追加】アイテムを表す構造体
    public Image i; //画像
    public int x;  //中心X座標
    public int y;  //中心Y座標
    public int hv; //左右方向の速度(左向きは負の数、右向きは正の数)
    public int v;  //表示状態(0:非表示、1以上:表示)
}
class Program : Form { //Formクラスを継承
    [System.Runtime.InteropServices.DllImport("user32.dll")] //外部DLLのインポート
    private static extern short GetKeyState(int nVirtKey); //メソッドの外部定義指定
    int gamemode = 0; //0:スタート画面、1:プレイ画面
    int score = 0; //スコア
    Image backi = Image.FromFile("backb.bmp"); //背景画像の読込
    Image playeri = Image.FromFile("player.gif"); //自機画像の読込
    Image playerl = Image.FromFile("playerl.gif"); //自機左寄画像の読込
    Image playerr = Image.FromFile("playerr.gif"); //自機右寄画像の読込
    Font font1 = new Font("メイリオ", 20, FontStyle.Bold); //フォントを生成(メイリオ20P太字)
    Font fontt = new Font("メイリオ", 80, FontStyle.Bold); //フォントを生成(メイリオ80P太字)
    Brush brushs = new SolidBrush(Color.Yellow); //ブラシを生成(黄色)
    Timer timer = new Timer(); //タイマーを生成
    int backy = 0; //背景画像のつなぎ目のY座標
    Item player; //【追加】プレイヤーの構造体オブジェクト
    //int playerx = 300; //【削除】自機のX座標
    //int playerv = 0; //【削除】自機の左右移動量(0:停止,-1:左,1:右)
    //【以下追加】中心座標を用いて画像を描画
    private void DrawItem(PaintEventArgs e, Item it) {
        int xx = it.x - it.i.Width / 2; //幅の半分を差し引いて左上X座標を得る
        int yy = it.y - it.i.Height / 2; //高さの半分を差し引いて左上Y座標を得る
        e.Graphics.DrawImage(it.i, xx, yy);
    }
    protected override void OnPaint(PaintEventArgs e) { //Formのメソッドをオーバーライド
        base.OnPaint(e); //まず、Formクラスにおけるメソッドの内容(基本再描画処理)を実行
        e.Graphics.DrawImage(backi, 0, backy); //背景画像を(0,つなぎ目)から描画
        e.Graphics.DrawImage(backi, 0, backy - backi.Height); //背景画像をその上に描画
        if (gamemode == 0) { //スタート画面
            e.Graphics.DrawString("GAME1", fontt, brushs, 100, 100); //タイトルの描画
            e.Graphics.DrawString("Hit Enter to Start", font1, brushs, 200, 300); //メッセージの描画
        } else if (gamemode == 1) { //プレイ画面
            string s = String.Format("SCORE:{0:000,000}", score); //スコア文字列を編集
            e.Graphics.DrawString(s, font1, brushs, 400, 10); //スコア文字列の描画
            switch (player.hv) { //【変更】自機の向きによって分岐
                case  0: player.i = playeri; break; //【変更】通常
                case  1: player.i = playerr; break; //【変更】右寄 
                case -1: player.i = playerl; break; //【変更】左寄
            }
            DrawItem(e, player); //【追加】自機を表示
        }
    }
    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 < backi.Height) ? backy + 1 : 0; //背景画像のつなぎ目を下げる
        if (gamemode == 1) { //プレイ画面
            player.hv = 0; //【変更】自機の向きを通常にする
            if (player.x > playeri.Width / 2 && GetKeyState((int)Keys.Left) < 0)
            { //【変更】左端ではなく←キーが押されている?
                player.x -= 10; //【変更】自機を左へ
                player.hv = -1; //【変更】左に移動中
            }
            if (player.x < backi.Width - playeri.Width / 2 && GetKeyState((int)Keys.Right) < 0) { //【変更】右端ではなく→キーが押されている?
                player.x += 10; //【変更】自機を右へ
                player.hv = 1; //【変更】右に移動中
            }
        }
        Invalidate(); //画面再描画を依頼する
    }
    Program() { //コンストラクタ
        DoubleBuffered = true; //ダブルバッファリングの導入
        KeyDown += new KeyEventHandler(OnKeyDown); //キーボードが押された時に呼ばれるメソッドを登録
        timer.Tick += new EventHandler(Play); //タイマーから呼ばれるメソッドを登録
        timer.Interval = 10; //タイマーの間隔
        player.i = playeri; //【追加】自機の画像
        player.x = 320; //【追加】自機のX座標
        player.y = 410; //【追加】自機のY座標
        player.hv = 0; //【追加】自機の左右方向の速度
    }
    static void Main() { //publicの指定は任意
        Program f = new Program(); //継承したフォームのインスタンスを生成
        f.Size = new Size(660, 520); //インスタンスのSizeプロパティに高さと幅を代入
        f.Text = "Game"; //インスタンスのTextプロパティにフォーム名を代入
        f.ControlBox = false; //ControlBoxを非表示にする
        f.FormBorderStyle = FormBorderStyle.Fixed3D; //フォームサイズ変更を禁止
        Application.Run(f); //インスタンスを画面に出す
    }
}

演習27 自弾の表示(単独バージョン)

・ゲームモードが1(プレイ画面)の時に、スペースキーが押されたら自弾を出そう
・ただし、出現していないときに限る(まずは1個のみとする)
・出現位置は自機の位置で決まり、自機の直上とする
・自機はItem構造体で表す
・画像は下記を利用可能
   bullet.gif 20x20

手順と仕様

① 自弾画像をImage型変数bulletiに読込む
② 自弾の構造体オブジェクトpbを定義
② 描画処理:プレイ画面で自弾があれば自弾を描画
③ タイマーイベント処理:スペースキーが押されていて自弾が非表示なら表示にする。
  また、画像を設定し、X座標は自機と同じに、Y座標は直上に触れる位置にする

作成例

//演習27 自弾の表示(単独バージョン)
using System; //フォームアプリケーションに必須
using System.Windows.Forms; //フォームアプリケーションに必須
using System.Drawing; //Image用
struct Item { //アイテムを表す構造体
    public Image i; //画像
    public int x;  //中心X座標
    public int y;  //中心Y座標
    public int hv; //左右方向の速度(左向きは負の数、右向きは正の数)
    public int v;  //表示状態(0:非表示、1以上:表示)
}
class Program : Form { //Formクラスを継承
    [System.Runtime.InteropServices.DllImport("user32.dll")] //外部DLLのインポート
    private static extern short GetKeyState(int nVirtKey); //メソッドの外部定義指定
    int gamemode = 0; //0:スタート画面、1:プレイ画面
    int score = 0; //スコア
    Image backi = Image.FromFile("backb.bmp"); //背景画像の読込
    Image playeri = Image.FromFile("player.gif"); //自機画像の読込
    Image playerl = Image.FromFile("playerl.gif"); //自機左寄画像の読込
    Image playerr = Image.FromFile("playerr.gif"); //自機右寄画像の読込
    Image bulleti = Image.FromFile("bullet.gif"); //【追加】自弾画像の読込
    Font font1 = new Font("メイリオ", 20, FontStyle.Bold); //フォントを生成(メイリオ20P太字)
    Font fontt = new Font("メイリオ", 80, FontStyle.Bold); //フォントを生成(メイリオ80P太字)
    Brush brushs = new SolidBrush(Color.Yellow); //ブラシを生成(黄色)
    Timer timer = new Timer(); //タイマーを生成
    int backy = 0; //背景画像のつなぎ目のY座標
    Item player; //プレイヤーの構造体オブジェクト
    Item pb; //【追加】自弾の構造体オブジェクト
    //中心座標を用いて画像を描画
    private void DrawItem(PaintEventArgs e, Item it) {
        int xx = it.x - it.i.Width / 2; //幅の半分を差し引いて左上X座標を得る
        int yy = it.y - it.i.Height / 2; //高さの半分を差し引いて左上Y座標を得る
        e.Graphics.DrawImage(it.i, xx, yy);
    }
    protected override void OnPaint(PaintEventArgs e) { //Formのメソッドをオーバーライド
        base.OnPaint(e); //まず、Formクラスにおけるメソッドの内容(基本再描画処理)を実行
        e.Graphics.DrawImage(backi, 0, backy); //背景画像を(0,つなぎ目)から描画
        e.Graphics.DrawImage(backi, 0, backy - backi.Height); //背景画像をその上に描画
        if (gamemode == 0) { //スタート画面
            e.Graphics.DrawString("GAME1", fontt, brushs, 100, 100); //タイトルの描画
            e.Graphics.DrawString("Hit Enter to Start", font1, brushs, 200, 300); //メッセージの描画
        } else if (gamemode == 1) { //プレイ画面
            string s = String.Format("SCORE:{0:000,000}", score); //スコア文字列を編集
            e.Graphics.DrawString(s, font1, brushs, 400, 10); //スコア文字列の描画
            switch (player.hv) { //自機の向きによって分岐
                case  0: player.i = playeri; break; //通常
                case  1: player.i = playerr; break; //右寄 
                case -1: player.i = playerl; break; //左寄
            }
            DrawItem(e, player); //自機を表示
            if (pb.v == 1) { //【以下追加】自弾がある? 
                DrawItem(e, pb); //自弾を表示
            }
        }
    }
    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 < backi.Height) ? backy + 1 : 0; //背景画像のつなぎ目を下げる
        if (gamemode == 1) { //プレイ画面
            player.hv = 0; //自機の向きを通常にする
            if (player.x > playeri.Width / 2 && GetKeyState((int)Keys.Left) < 0)
            { //左端ではなく←キーが押されている?
                player.x -= 10; //自機を左へ
                player.hv = -1; //左に移動中
            }
            if (player.x < backi.Width - playeri.Width / 2 && GetKeyState((int)Keys.Right) < 0) { //右端ではなく→キーが押されている?
                player.x += 10; //自機を右へ
                player.hv = 1; //右に移動中
            }
        }
        if (GetKeyState((int)Keys.Space) < 0) { //【以下追加】スペースキーが押されている?
            if (pb.v == 0) { //自弾が非常時?
                pb.v = 1; //表示にする
                pb.i = bulleti; //画像を設定
                pb.x = player.x; //X座標は自機の中心に合わせる
                pb.y = player.y - player.i.Height / 2 - pb.i.Height / 2; //Y座標は自機の直上に合わせる
            }
        }
        Invalidate(); //画面再描画を依頼する
    }
    Program() { //コンストラクタ
        DoubleBuffered = true; //ダブルバッファリングの導入
        KeyDown += new KeyEventHandler(OnKeyDown); //キーボードが押された時に呼ばれるメソッドを登録
        timer.Tick += new EventHandler(Play); //タイマーから呼ばれるメソッドを登録
        timer.Interval = 10; //タイマーの間隔
        player.i = playeri; //自機の画像
        player.x = 320; //自機のX座標
        player.y = 410; //自機のY座標
        player.hv = 0; //自機の左右方向の速度
    }
    static void Main() { //publicの指定は任意
        Program f = new Program(); //継承したフォームのインスタンスを生成
        f.Size = new Size(660, 520); //インスタンスのSizeプロパティに高さと幅を代入
        f.Text = "Game"; //インスタンスのTextプロパティにフォーム名を代入
        f.ControlBox = false; //ControlBoxを非表示にする
        f.FormBorderStyle = FormBorderStyle.Fixed3D; //フォームサイズ変更を禁止
        Application.Run(f); //インスタンスを画面に出す
    }
}

提出:演習26または27(未完成でも可)

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です