前回のコメント

・今日習ったイベントでは「Char.IsDigit」などメソッドやプロパティなどを習いましたがそれぞれ中身でいろいろ
 やっていて情報量が多く頭から煙が上がりそうでした笑
 が、charの構造体の中身などを知ることができて面白かったです(o^―^o)

 何よりです(頭から煙…は、心配ですが(笑))。
 基礎コースでは「これはこうです」という説明でしたが、応用コースでは「それは実はこうだからです」を加えます。
 理解が深まりましたら幸いです。

参考:https://learn.unity.com/

講義メモ ゲーム開発演習

:塗りつぶし、文字の描画、タイトル画面と画面遷移 など

テーマ19 矩形の塗りつぶし

・塗りつぶしではペンの代わりにブラシを用いる
・ブラシ機能を提供するのはSystem.Drawing.Brushクラスだが、これは抽象クラスで、実際の描画には派生クラスを用いる
・最も構造が簡単なのが派生クラスのSystem.Drawing.SolidBrush
・このクラスのコンストラクタにColor列挙体の列挙子を渡すことで、単色用のブラシが生成される
・例: Brush b = new SolidBrush(Color.Red); //赤の単色ブラシを生成
・矩形の塗りつぶしは、DrawRectangleに似た、FillRectangleにブラシ、左上X座標、左上Y座標、幅、高さを指定する
・例: e.Graphics.FillRectangle(mybrush, 78, 411, 485, 64); //判定エリアの塗りつぶし

演習17 判定エリアの塗りつぶし

・判定エリアを赤色で塗りつぶそう
・同心円は削除しよう

作成例

//演習17 判定エリアの塗りつぶし
using System; //フォームアプリケーションに必須
using System.Windows.Forms; //フォームアプリケーションに必須
using System.Drawing; //Image用
class Program : Form { //Formクラスを継承
    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.Red); //【追加】ブラシを生成(赤の単色)
    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, 0); //背景画像を(0,0)から描画
        e.Graphics.FillRectangle(bred, 78, 411, 485, 64); //【変更】判定エリアの描画
        //int mx = backb.Width / 2, my = backb.Height / 2; //【削除】中央座標を得る
        //mypen.Color = Color.Yellow; //【削除】ペン色を黄色に
        //mypen.Width = 10;  //【削除】ペン幅を10に
        //for (int i = 1; i <= 4; i++) { //【以下削除】4回繰返す
        //    DrawCircle(e, mypen, mx, my, 15 * i); //ペン、中心座標、半径を指定して円を描画
        //}
    }
    void OnKeyDown(object o, KeyEventArgs e) { //キーボードが押された時に呼ばれるメソッド
        if(e.KeyCode.ToString() == "Escape") { //Escキーが押されたら
            Close(); //フォームアプリケーション終了
        }
    }
    Program() { //コンストラクタ
        KeyDown += new KeyEventHandler(OnKeyDown); //キーボードが押された時に呼ばれるメソッドを登録
    }
    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); //インスタンスを画面に出す
    }
}

テーマ20 矩形の半透明塗りつぶし

・SolidBrushクラスのコンストラクタにColor列挙体の列挙子を渡す代わりに、αコンポーネント値を持つColorオブジェクトを渡すことで、
 透明度を指定できる
・αコンポーネント値は0が不透明で255まで指定可能なので、127で半透明になる
・例:不透明な赤:(0, 255, 0, 0)、半透明な赤:(127, 255, 0, 0)
・αコンポーネント値を持つColorオブジェクトを生成するには、Color.FromArgb(αコンポーネント値, Red値, Green値, Blue値)を用いる

演習18 判定エリアの半透明塗りつぶし

・赤色で矩形の判定エリアを半透明(αコンポーネント値=63)で塗りつぶそう
・同じサイズで赤色の矩形を描くことで見やすくしよう

作成例

//演習18 判定エリアの半透明塗りつぶし
using System; //フォームアプリケーションに必須
using System.Windows.Forms; //フォームアプリケーションに必須
using System.Drawing; //Image用
class Program : Form { //Formクラスを継承
    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)); //【変更】ブラシを生成(赤の透明色)
    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, 0); //背景画像を(0,0)から描画
        e.Graphics.FillRectangle(bred, 78, 411, 485, 64); //【変更】判定エリアを透明赤で塗りつぶす
        e.Graphics.DrawRectangle(mypen, 78, 411, 485, 64); //【追加】判定エリアの描画
    }
    void OnKeyDown(object o, KeyEventArgs e) { //キーボードが押された時に呼ばれるメソッド
        if(e.KeyCode.ToString() == "Escape") { //Escキーが押されたら
            Close(); //フォームアプリケーション終了
        }
    }
    Program() { //コンストラクタ
        KeyDown += new KeyEventHandler(OnKeyDown); //キーボードが押された時に呼ばれるメソッドを登録
    }
    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); //インスタンスを画面に出す
    }
}

テーマ21 文字の描画(途中まで)

・フォームアプリケーションではフォントや書式を指定して文字列を画面上に描画できる
・この時、フォント、ブラシ、位置を指定できる
・フォントは、System.Drawing.Fontクラスのコンストラクタで生成できる
・このコンストラクタは多数のオーバーロードがあるが、フォント名とポイント数(大きさ)を指定する Font(string, float)が便利
・例: Font f = new Font("メイリオ", 15);
・太字などの書式を指定するにはFont(string, float, FontStyle)を用いると良い
・FontStyleはSystem.Drawing.FontStyle列挙型で、列挙子のBoldで太字にできる
(以下次回)

提出:演習18 判定エリアの半透明塗りつぶし

講義メモ

テキスト篇:p.310「イベント」から
ゲーム開発演習:塗りつぶし、文字の描画、タイトル画面と画面遷移 など

p.310 イベント

・コンソールアプリケーションにおけるConsole.ReadLineのように、起動したプログラムが動的に情報を得る手段がある
・しかし、これは単なる入力待ちであり、プログラム側の制御はできない(OSに制御を渡してしまう)
・そこで、予め「これが起きたらこれをする」という定義ができ、動的に呼び出される仕掛けとして用意されたのがイベント
・キー入力、マウスの操作、通信の受信などの「何かが起きた」ことをイベントの発生といい、イベントに対して処理を実行するものを
 イベントハンドラという。
 ※ イベントの概念や用語は言語により異なり、Java言語ではイベントリスナという(一部のC#系文書でもこの表現で呼ぶことがある)
・C#では主にフォームアプリケーションでイベントを扱うが、コンソールアプリケーションでもデリゲートを用いることでイベント処理の
 実装が可能
・例えば、Enterキー以外のキー入力を即時に受け取る仕掛けを実装できる

p.312 イベントのプログラミング

・コンソールアプリケーションでデリゲートを用いてイベント処理を実装するには、内部的にイベントを発生させ、これを受けて処理する
 メソッドを記述する
・実装としては、まず、イベントの発生を担うクラスを定義し、その中にイベントフィールドと呼ばれるデリゲートと、イベントを発生させる
 メソッドを記述する。
・イベントフィールドは、デリゲートを型とする特殊なpublicメンバで、メンバ名がイベント名になる
・書式: public event デリゲート名 イベント名;
※ つまり、イベントの発生を担うクラスにはメソッドやデータメンバに加えてイベントを記述する
・イベントを発生させるメソッドは、通常「On」+「イベント名」をメソッド名として定義し、その中でイベント名をメソッドと同様に
 呼び出すことでイベントを発生させる。
・書式: public void/戻り値型 Onイベント名() {…} 
・このメソッドの中でイベント名を用いてイベントを発生させるが、イベントフィールドの準備が出来ていない(イベントがnullである状態)の
 時に呼び出してしまわないように留意する
・書式: if (イベント名 != null) { イベント名(); }
・このクラスを用いて、イベント処理を行いたい側のMainメソッドの記述法
 ① イベントの発生を担うクラスのインスタンスを生成
 ② イベントの発生により動作させたいメソッドを準備(他のクラスのメソッドでも良い)
 ③ ①の参照変数.イベントに対して、②をマルチキャストデリゲーション指定することで登録
 ④ ①の参照変数.Onイベント()メソッドを呼び出すことでイベントを発生させる

p.313 event01.cs

//p.313 event01.cs
using System;
delegate void MyDelegate(); //デリゲートの宣言(戻り値なし、引数なし)
class MyEventClass { //イベントの発生を担うクラス
    public event MyDelegate eventname; //イベントフィールドをイベント名evantnameで定義
    public void OnEventname() { //evantnameイベントを発生させるメソッド
        if (eventname != null) //イベントフィールドが無(準備中)でなければ
            eventname(); //イベントを発生させる
    }
}
class MyClass { //イベントの発生により実行したいメソッド①を持つクラス
    public void show() { //イベントの発生により実行したいメソッド①
        Console.WriteLine("show");
    }
}
class MyClass2 { //イベントの発生により実行したいメソッド②を持つクラス
    public void show2() { //イベントの発生により実行したいメソッド②
        Console.WriteLine("show2!!");
    }
}
class event01 {
    public static void Main() {
        MyClass mc = new MyClass(); //イベントの発生により実行したいメソッド①を持つクラスのインスタンスを生成
        MyClass2 mc2 = new MyClass2(); //イベントの発生により実行したいメソッド②を持つクラスのインスタンスを生成
        MyEventClass myevent = new MyEventClass(); //イベントの発生を担うクラスのインスタンスを生成
        myevent.eventname += new MyDelegate(mc.show); //イベントに対して①をマルチキャストデリゲーション指定することで登録
        myevent.eventname += new MyDelegate(mc2.show2); //イベントに対して②をマルチキャストデリゲーション指定することで登録
        myevent.OnEventname(); //イベントを発生させる
    }
}

アレンジ演習:p.313 event01.cs

・マルチキャストデリゲーションをラムダ式で記述しよう

作成例

//アレンジ演習:p.313 event01.cs
using System;
delegate void MyDelegate(); //デリゲートの宣言(戻り値なし、引数なし)
class MyEventClass { //イベントの発生を担うクラス
    public event MyDelegate eventname; //イベントフィールドをイベント名evantnameで定義
    public void OnEventname() { //evantnameイベントを発生させるメソッド
        if (eventname != null) //イベントフィールドが無(準備中)でなければ
            eventname(); //イベントを発生させる
    }
}
class MyClass { //イベントの発生により実行したいメソッド①を持つクラス
    public void show() { //イベントの発生により実行したいメソッド①
        Console.WriteLine("show");
    }
}
class MyClass2 { //イベントの発生により実行したいメソッド②を持つクラス
    public void show2() { //イベントの発生により実行したいメソッド②
        Console.WriteLine("show2!!");
    }
}
class event01 {
    public static void Main() {
        MyClass mc = new MyClass(); //イベントの発生により実行したいメソッド①を持つクラスのインスタンスを生成
        MyClass2 mc2 = new MyClass2(); //イベントの発生により実行したいメソッド②を持つクラスのインスタンスを生成
        MyEventClass myevent = new MyEventClass(); //イベントの発生を担うクラスのインスタンスを生成
        myevent.eventname += () => mc.show(); //イベントに対して①をマルチキャストデリゲーション指定することで登録
        myevent.eventname += () => mc2.show2(); //イベントに対して②をマルチキャストデリゲーション指定することで登録
        myevent.OnEventname(); //イベントを発生させる
    }
}

アレンジ演習:p.313 event01.cs

・マルチキャストデリゲーションを匿名メソッドで記述しよう
・すると、イベントの発生により実行したいメソッドを持つ2クラスが不要になる

作成例

//アレンジ演習:p.313 event01.cs
using System;
delegate void MyDelegate(); //デリゲートの宣言(戻り値なし、引数なし)
class MyEventClass { //イベントの発生を担うクラス
    public event MyDelegate eventname; //イベントフィールドをイベント名evantnameで定義
    public void OnEventname() { //evantnameイベントを発生させるメソッド
        if (eventname != null) //イベントフィールドが無(準備中)でなければ
            eventname(); //イベントを発生させる
    }
}
class event01 {
    public static void Main() {
        MyEventClass myevent = new MyEventClass(); //イベントの発生を担うクラスのインスタンスを生成
        myevent.eventname += () => {Console.WriteLine("show");}; //イベントに対して匿名メソッドをマルチキャストデリゲーション指定する
        myevent.eventname += () => {Console.WriteLine("show2");}; //イベントに対して匿名メソッドをマルチキャストデリゲーション指定する
        myevent.OnEventname(); //イベントを発生させる
    }
}

p.316 Char構造体と静的メソッド

・System.Char構造体はC#が提供する文字処理用の構造体で、便利な静的メソッドを提供する
※ charをプロパティに前置すると.NET型のSyster.Charに書き換えられ、これを担う構造体とみなされる
・bool Char.IsDigit(char):1文字を受け取って10進数字(0~9)であるかどうかを返す
・double Char.GetNumericValue(char):1文字を受け取って実数変換して返す(変換不可時は-1.0を返す)
※ double.Parse("" + char) や Convert.ToDouble("" + char) と類似だが、これらは変換不可時は異常終了

p.317 ConsoleKeyInfo構造体とプロパティ

・System.ConsoleKeyInfo構造体は、押されたキーの情報を返すためのデータ構造と、便利なプロパティを持つ構造体
・Consoleクラスが提供するReadKeyメソッドの戻り値型がConsoleKeyInfo構造体であり、このメソッドを実行すると、押されたキーの情報をこの構造体に格納してくれる
・この時、引数としてキー情報のコンソールのへの非表示をtrueで指示できる
・書式: ConsoleKeyInfo 変数名 = Console.ReadKey(true);
・bool Console.KeyAvailableプロパティ(p.293)で、キー入力があったかどうかが得られるので、これがtrueであれば、ReadKeyメソッドを用いると良い
・そして、ConsoleKeyInfo構造体のchar KeyCharプロパティで、押されていた文字が得られる

p.315 event02.cs

//p.315 event02.cs
using System;
delegate void Handler(char ch); //デリゲートの宣言(戻り値なし、引数有り)
class EventClass { //イベントの発生を担うクラス
    public event Handler KeyHit; //イベントフィールドをイベント名KeyHitで定義
    public void OnKeyHit(char ch) { //KeyHitイベントを発生させるメソッド
        if (KeyHit != null) //イベントフィールドが無(準備中)でなければ
            KeyHit(ch); //イベントを発生させる
    }
}
class Show { //イベントの発生により実行したいメソッドを持つクラス
    int sum = 0; //合計値
    public void keyshow(char ch) { //イベントの発生により実行したいメソッド
        if (Char.IsDigit(ch)) { //文字が10進数字(0~9のキーが押されていたら)?
            int a = (int)Char.GetNumericValue(ch); //実数化&整数化する
            sum += a; //合計に足し込む
            Console.WriteLine("+ {0}", a); //押されたキーの値を表示
            Console.WriteLine("= {0}", sum); //合計値を表示
        } else if (ch == 'c') { //文字がc(cキーが押されていたら)?
            sum = 0; //合計値クリア
            Console.WriteLine("合計がクリアされました");
        } else { //10進数字,'c'以外であれば
            return; //何もしない
        }
    }
}
class event02 {
    public static void Main() {
        ConsoleKeyInfo cki; //キー情報を得るためのデータ構造等を持つ構造体
        EventClass ec = new EventClass(); //イベントの発生を担うクラスのインスタンスを生成
        Show s = new Show(); //イベントの発生により実行したいメソッドを持つクラスのインスタンスを生成
        ec.KeyHit += new Handler(s.keyshow); //イベントにマルチキャストデリゲーション指定することで登録
        while (true) { //無限ループ
            if (Console.KeyAvailable) { //何かキーが押されている?
                cki = Console.ReadKey(true); //そのキーを得る
                if (cki.KeyChar == 'x') { //xキーであれば
                    break; //繰り返し終了
                } else { //xキー以外?
                    ec.OnKeyHit(cki.KeyChar); //イベントを発生させる
                }
            }
        }
    }
}

アレンジ演習:p.315 event02.cs

・p.319下から2行目のとおり、ラムダ式で書き換えよう

作成例

//アレンジ演習:p.315 event02.cs
using System;
delegate void Handler(char ch); //デリゲートの宣言(戻り値なし、引数有り)
class EventClass { //イベントの発生を担うクラス
    public event Handler KeyHit; //イベントフィールドをイベント名KeyHitで定義
    public void OnKeyHit(char ch) { //KeyHitイベントを発生させるメソッド
        if (KeyHit != null) //イベントフィールドが無(準備中)でなければ
            KeyHit(ch); //イベントを発生させる
    }
}
class Show { //イベントの発生により実行したいメソッドを持つクラス
    int sum = 0; //合計値
    public void keyshow(char ch) { //イベントの発生により実行したいメソッド
        if (Char.IsDigit(ch)) { //文字が10進数字(0~9のキーが押されていたら)?
            int a = (int)Char.GetNumericValue(ch); //実数化&整数化する
            sum += a; //合計に足し込む
            Console.WriteLine("+ {0}", a); //押されたキーの値を表示
            Console.WriteLine("= {0}", sum); //合計値を表示
        } else if (ch == 'c') { //文字がc(cキーが押されていたら)?
            sum = 0; //合計値クリア
            Console.WriteLine("合計がクリアされました");
        } else { //10進数字,'c'以外であれば
            return; //何もしない
        }
    }
}
class event02 {
    public static void Main() {
        ConsoleKeyInfo cki; //キー情報を得るためのデータ構造等を持つ構造体
        EventClass ec = new EventClass(); //イベントの発生を担うクラスのインスタンスを生成
        Show s = new Show(); //イベントの発生により実行したいメソッドを持つクラスのインスタンスを生成
        ec.KeyHit += (c) => s.keyshow(c); //【変更】イベントにマルチキャストデリゲーション指定することで登録
        while (true) { //無限ループ
            if (Console.KeyAvailable) { //何かキーが押されている?
                cki = Console.ReadKey(true); //そのキーを得る
                if (cki.KeyChar == 'x') { //xキーであれば
                    break; //繰り返し終了
                } else { //xキー以外?
                    ec.OnKeyHit(cki.KeyChar); //イベントを発生させる
                }
            }
        }
    }
}

今週の話題

販売本数ランキング 今回トップは「スーパーマリオブラザーズ ワンダー(Switch)」GO!
インディーゲーム展示会“東京ゲームダンジョン4”の入場チケットが販売開始。出展する250団体の出展リストを公開 GO!
『スクフェス2』がまさかの大失速―ブシロードはゲーム事業の苦境が鮮明に【ゲーム企業の決算を読む】 GO!
テーマは「脱出」―Unreal Engine使用映像制作コンテスト「UE5ぷちコン 映像編5th」エントリー受付開始 GO!
AI活用でプレイヤーサポート強化を支援―PTW、ゲームパブリッシャー向けソリューション「REACT」を発表 GO!
個性を引き出すモデリングから内製バーチャル広報による社内活性化まで―『このファン』サムザップがLive2D活用事例を紹介【alive 2023】 GO!

『Marvel’s Spider-Man』等開発のInsomniac Gamesにサイバー攻撃―開発中新作の情報流出も? GO!
開発スタジオ閉鎖の『The Day Before』販売も即座に中止…わずか4日間しか買えなかったゲームに、ユーザーからの「売り逃げ」の声も GO!
GooglePlayストア「独占的な力あり」とEpic対Googleの米裁判で陪審判決!具体的な措置命令は1月第2週の公聴会以降に持ち越し GO!

前回のコメント

・今日習った、ラムダ式は家が倒れたみたいな=>を使いプログラムがとてもコンパクトになる
 のが面白いなと感じました。プログラムをパッと見ると何が起きているんだ・・となりそうですが

 「家が倒れたみたい」という表現は初めてです。なるほど。
 
・式をしっかり覚えて使いこなせるようになりたいと思いました。

 是非。

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

同心円の描画のフォロー、塗りつぶし、文字の描画、タイトル画面と画面遷移 など

演習16 同心円の描画・改

・DrawEllipseメソッドは円を描く場合に直感的ではないので、ペンと中心座標と半径を指定して呼び出せるようにブリッジとなる
 DrawCircleメソッドを組み込んみよう
例: void DrawCircle(PaintEventArgs e, Pen p, int x, int y, int r)
・左上X座標は「中心x - 半径r」、左上Y座標は「中心y - 半径r」、高さと幅は「2 * r」となる

作成例

//演習16 同心円の描画・改
using System; //フォームアプリケーションに必須
using System.Windows.Forms; //フォームアプリケーションに必須
using System.Drawing; //Image用
class Program : Form { //Formクラスを継承
    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)
    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, 0); //背景画像を(0,0)から描画
        e.Graphics.DrawRectangle(mypen, 78, 411, 485, 64); //判定エリアの描画
        int mx = backb.Width / 2, my = backb.Height / 2; //中央座標を得る
        mypen.Color = Color.Yellow; //ペン色を黄色に
        mypen.Width = 10;  //ペン幅を10に
        for (int i = 1; i <= 4; i++) { //4回繰返す
            DrawCircle(e, mypen, mx, my, 15 * i); //【変更】ペン、中心座標、半径を指定して円を描画
        }
        //e.Graphics.DrawImage(enemy, mx - enemy.Width / 2, my - enemy.Height / 2); //アイテム画像を中央に描画
        //e.Graphics.DrawImage(burn, mx - burn.Width / 2, my - burn.Height / 2); //アイテム画像を中央に描画
    }
    void OnKeyDown(object o, KeyEventArgs e) { //キーボードが押された時に呼ばれるメソッド
        if(e.KeyCode.ToString() == "Escape") { //Escキーが押されたら
            Close(); //フォームアプリケーション終了
        }
    }
    Program() { //コンストラクタ
        KeyDown += new KeyEventHandler(OnKeyDown); //キーボードが押された時に呼ばれるメソッドを登録
    }
    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); //インスタンスを画面に出す
    }
}

講義メモ

テキスト篇:p.301「マルチキャストデリゲーション」から
ゲーム開発演習:同心円の描画のフォロー、塗りつぶし、文字の描画、タイトル画面と画面遷移 など

p.301 マルチキャストデリゲーション

・デリゲートの登録に「+=」を用いることで、1つのデリゲートに2つ以上のオブジェクトを登録することが可能
・これをマルチキャストデリゲーションといい、呼び出しにより、複数のメソッドが順に動作する
・動作順序の保証がないため、戻り値型がvoidであるデリゲートに限る
・例:
 delegate void デリゲート名(…); //デリゲートの宣言(戻り値無し、引数有り無しどちらもOK))
 :
 デリゲート名 参照変数 = new デリゲート名(メソッド名①); //参照変数で①が呼ばれる
 参照変数 += new デリゲート名(メソッド名②); //参照変数で①②が呼ばれる
 参照変数 += new デリゲート名(メソッド名③); //参照変数で①②③が呼ばれる
・なお「-=」を用いて、登録済みのデリゲートオブジェクトを取り消し可能
・例:
 参照変数 -= new デリゲート名(メソッド名②); //参照変数で①③が呼ばれる

p.302 delegate05.cs

//p.302 delegate05.cs
using System;
delegate void MyDG(); //デリゲートの宣言(戻り値無し、引数無し)
class MyClass {
    public void show1() { //Mainとは異なるクラスにあるインスタンスメソッド①
        Console.WriteLine("show1が呼ばれました");
    }
    public void show2() { //Mainとは異なるクラスにあるインスタンスメソッド②
        Console.WriteLine("show2が呼ばれました");
    }
    public void show3() { //Mainとは異なるクラスにあるインスタンスメソッド③
        Console.WriteLine("show3が呼ばれました");
    }
}
class delegate05 {
    public static void Main() {
        MyClass mc = new MyClass(); //インスタンスメソッド①②③のあるクラスのインスタンスを生成
        MyDG md = new MyDG(mc.show1); //インスタンスメソッド①のデリゲートを生成しmdとする
        Console.WriteLine("1回目のmd()を実行します");
        md(); //①が呼ばれる
        md += new MyDG(mc.show2); //インスタンスメソッド②のデリゲートを生成しmdに追加登録とする
        Console.WriteLine("2回目のmd()を実行します");
        md(); //①②が呼ばれる
        md += new MyDG(mc.show3); //インスタンスメソッド③のデリゲートを生成しmdに追加登録とする
        Console.WriteLine("3回目のmd()を実行します");
        md(); //①②③が呼ばれる
        md -= new MyDG(mc.show1); //インスタンスメソッド①のデリゲートを生成しmdから解除する
        Console.WriteLine("4回目のmd()を実行します");
        md(); //②③が呼ばれる
    }
}

アレンジ演習:p.302 delegate05.cs

・コンソールに3つのメソッド名を順に表示し「実行しますか(y/n)」で選択できるようにしよう
例:
戦闘相手選択①ドラゴンと戦いますか?(y/n):y
戦闘相手選択②スライムと戦いますか?(y/n):n
戦闘相手選択③大魔導士と戦いますか?(y/n):y
ドラゴンを攻撃しました
大魔導士を攻撃しました
戦闘相手選択①ドラゴンと戦いますか?(y/n):

作成例

//アレンジ演習:p.302 delegate05.cs
using System;
delegate void MyDG(); //デリゲートの宣言(戻り値無し、引数無し)
class MyClass {
    public void Dragon() { //Mainとは異なるクラスにあるインスタンスメソッド①
        Console.WriteLine("ドラゴンを攻撃しました");
    }
    public void Slime() { //Mainとは異なるクラスにあるインスタンスメソッド②
        Console.WriteLine("スライムを攻撃しました");
    }
    public void Daimado() { //Mainとは異なるクラスにあるインスタンスメソッド③
        Console.WriteLine("大魔導士を攻撃しました");
    }
}
class delegate05 {
    public static void Main() {
        MyClass mc = new MyClass(); //インスタンスメソッド①②③のあるクラスのインスタンスを生成
        MyDG md; //デリゲートの参照変数を宣言
        string ans = "";
        do {
            md = null; //デリゲートを無しにしておく
            Console.Write("戦闘相手選択①ドラゴンと戦いますか?(y/n):"); ans = Console.ReadLine();
            if (ans == "y") {
                md += new MyDG(mc.Dragon); //インスタンスメソッド①のデリゲートを生成しmdに登録
            }
            Console.Write("戦闘相手選択②スライムと戦いますか?(y/n):"); ans = Console.ReadLine();
            if (ans == "y") {
                md += new MyDG(mc.Slime); //インスタンスメソッド②のデリゲートを生成しmdに登録
            }
            Console.Write("戦闘相手選択③大魔導士と戦いますか?(y/n):"); ans = Console.ReadLine();
            if (ans == "y") {
                md += new MyDG(mc.Daimado); //インスタンスメソッド③のデリゲートを生成しmdに登録
            }
            if (md != null) { //1つでも登録されていたら
                md(); //マルチキャストデリゲートを実行
            }
        } while (md != null);
    }
}

p.304 匿名メソッド

・デリゲートに既存のメソッドを登録する代わりに、登録内容をそのまま記述できる
・書式: デリゲート名 参照変数 = delegate(引数リスト){メソッドの内容};
・例:MyDG md = delegate() { Console.WriteLine("show1が呼ばれました"); };
・こうするとメソッド名がなくなるので、匿名メソッドという
・マルチキャストデリゲーションも可能だが「-=」は不可なので注意

p.304 anomethed01.cs

//p.304 anomethed01.cs
using System;
delegate int MyDelegate(string s); //デリゲートの宣言(戻り値有り、引数有り)
class MyClass {
    public int Show(string s) { //Mainとは異なるクラスにあるインスタンスメソッド
        Console.WriteLine("{0}と入力されました。", s);
        return 0;
    }
}
class AnoMethed01 {
    public static void Main() {
        MyClass mc = new MyClass();
        Console.Write("文字列入力 --- ");
        string x = Console.ReadLine();
        //従来型
        MyDelegate mdg = new MyDelegate(mc.Show);
        mdg(x);
        //匿名メソッド
        MyDelegate mdg2 = delegate (string i) { //引数有り
            Console.WriteLine("{0}と入力されました。", i);
            return 0;
        };
        mdg2(x); //呼び出し方法は同じ
    }
}

アレンジ演習:p.304 anomethed01.cs

・従来型の部分をすべて削除しよう

作成例

//p.304 anomethed01.cs
using System;
delegate int MyDelegate(string s); //デリゲートの宣言(戻り値有り、引数有り)
class AnoMethed01 {
    public static void Main() {
        Console.Write("文字列入力 --- ");
        string x = Console.ReadLine();
        //匿名メソッド
        MyDelegate mdg2 = delegate (string i) { //引数有り
            Console.WriteLine("{0}と入力されました。", i);
            return 0;
        };
        mdg2(x); //呼び出し方法は同じ
    }
}

アレンジ演習:p.302 delegate05.cs(その2)

・3つのデリゲート用メソッドをすべて匿名メソッドにしよう

作成例

//アレンジ演習:p.302 delegate05.cs(その2)
using System;
delegate void MyDG(); //デリゲートの宣言(戻り値無し、引数無し)
class delegate05 {
    public static void Main() {
        MyDG md; //デリゲートの参照変数を宣言
        string ans = "";
        do {
            md = null; //デリゲートを無しにしておく
            Console.Write("戦闘相手選択①ドラゴンと戦いますか?(y/n):"); ans = Console.ReadLine();
            if (ans == "y") {
                md += delegate(){Console.WriteLine("ドラゴンを攻撃しました");}; //匿名メソッドをmdに登録
            }
            Console.Write("戦闘相手選択②スライムと戦いますか?(y/n):"); ans = Console.ReadLine();
            if (ans == "y") {
                md += delegate(){Console.WriteLine("スライムを攻撃しました");}; //匿名メソッドをmdに登録
            }
            Console.Write("戦闘相手選択③大魔導士と戦いますか?(y/n):"); ans = Console.ReadLine();
            if (ans == "y") {
                md += delegate(){Console.WriteLine("大魔導士を攻撃しました");}; //匿名メソッドをmdに登録
            }
            if (md != null) { //1つでも登録されていたら
                md(); //マルチキャストデリゲートを実行
            }
        } while (md != null);
    }
}

p.306 ラムダ式

・元は関数の簡易書式で、多くの仕掛けやプログラム言語に採用されている
・ただし、活用場面によって文法が異なることがあり注意
・C#では主に、匿名メソッドの簡易記述に活用する
・書式: (引数リスト) => 式; //引数リストでは型の記述は不要
・例: (x) => Show(x); //引数xを渡してShowメソッドを呼ぶ
・なお、引数がないメソッドの場合「()」のみ記述する
・例: () => Show(); //引数のないShowメソッドを呼ぶ
・これをデリゲートのメソッド登録に用いると良い
・例:MyDelegate mdg = new MyDelegate(mc.Show);
 ⇒  MyDelegate mdg = () => mc.Show();

p.307 lambda01.cs

//p.307 lambda01.cs
using System;
delegate void MyDelegate(); //デリゲートの宣言(戻り値無し、引数無し)
class lambda01 {
    public static void show() {
        Console.WriteLine("呼ばれました");
    }
    public static void Main() {
        // 直接showメソッドを呼び出す
        show();
        // デリゲートの作成
        MyDelegate md = () => show(); //元は「MyDelegate md = new MyDelegate(show);」
        //デリゲートを通してshowメソッドを実行
        md(); 
    }
}

p.308(匿名メソッドとラムダ式)

・ラムダ式でデリゲートの匿名メソッドを記述できる
・例:MyDelegate md = delegate () { Console.WriteLine("入力されました");};
 ⇒  MyDelegate md = () => { Console.WriteLine("入力されました");};
・例:MyDelegate md = delegate (i) { Console.WriteLine("{0}と入力されました", i);};
 ⇒  MyDelegate md = (i) => { Console.WriteLine("{0}と入力されました", i);};
・引数の型はデリゲートの定義を用いて判断される

p.307 lambda02.cs

//p.307 lambda02.cs
using System;
delegate int MyDelegate(int x, int y);
class lambda02
{
    public static void Main()
    {
        MyDelegate md = (x, y) => { return x + y; }; //匿名メソッドをラムダ式に
        Console.WriteLine("2 + 3 = {0}", md(2, 3));
    }
}

p.308(匿名メソッドとマルチキャストとラムダ式)

・マルチキャストにおいても匿名メソッドをラムダ式で記述して利用できる
・なお「+=」を用いる代わりに、同じデリゲート型の参照変数を複数定義して「参照変数 = 参照変数 + 参照変数」とすることで
 マルチキャストを表現できる
 ⇒ lambda03.csの016行目
・また、ラムダ式の左辺の引数が1個の場合、カッコを省略できる
例:MyDelegate md = (i) => { Console.WriteLine("{0}と入力されました", i);};
⇒  MyDelegate md =  i  => { Console.WriteLine("{0}と入力されました", i);};

p.309 lambda03.cs

//p.309 lambda03.cs
using System;
delegate void MyDelegate(int x);
class lambda03
{
    public static void Main()
    {
        MyDelegate md1 = x => {Console.WriteLine("{0}の2乗は{1}", x, x * x);}; //ラムダ式で記述①
        MyDelegate md2 = x => {Console.WriteLine("{0}の2倍は{1}", x, x * 2);}; //ラムダ式で記述②
        MyDelegate md = md1 + md2; //マルチキャストデリゲーションにする
        md(10); //①②を呼び出す
    }
}

アレンジ演習:p.309 lambda03.cs

・デリゲートの参照変数を3つ用いているのを、1つにしよう

作成例

//アレンジ演習:p.309 lambda03.cs
using System;
delegate void MyDelegate(int x);
class lambda03
{
    public static void Main()
    {
        MyDelegate md = x => {Console.WriteLine("{0}の2乗は{1}", x, x * x);}; //ラムダ式で記述①し登録
        md += x => {Console.WriteLine("{0}の2倍は{1}", x, x * 2);}; //ラムダ式で記述②し追加登録
        md(10); //①②を呼び出す
    }
}

アレンジ演習:p.302 delegate05.cs(その3)

・3つの匿名メソッドをすべてラムダ式にしよう

作成例

//アレンジ演習:p.302 delegate05.cs(その3)
using System;
delegate void MyDG(); //デリゲートの宣言(戻り値無し、引数無し)
class delegate05 {
    public static void Main() {
        MyDG md; //デリゲートの参照変数を宣言
        string ans = "";
        do {
            md = null; //デリゲートを無しにしておく
            Console.Write("戦闘相手選択①ドラゴンと戦いますか?(y/n):"); ans = Console.ReadLine();
            if (ans == "y") {
                md += () => Console.WriteLine("ドラゴンを攻撃しました"); //ラムダ式でmdに登録
            }
            Console.Write("戦闘相手選択②スライムと戦いますか?(y/n):"); ans = Console.ReadLine();
            if (ans == "y") {
                md += () => Console.WriteLine("スライムを攻撃しました"); //ラムダ式でmdに登録
            }
            Console.Write("戦闘相手選択③大魔導士と戦いますか?(y/n):"); ans = Console.ReadLine();
            if (ans == "y") {
                md += () => Console.WriteLine("大魔導士を攻撃しました"); //ラムダ式でmdに登録
            }
            if (md != null) { //1つでも登録されていたら
                md(); //マルチキャストデリゲートを実行
            }
        } while (md != null);
    }
}

今週の話題

販売本数ランキング 今回トップは「ドラゴンクエストモンスターズ3 魔族の王子とエルフの旅(Switch)」GO!
“ゲームアワード 2023”受賞作まとめ。『バルダーズ・ゲート3』がGOTYを始め6冠に。『Alan Wake 2』は3部門で受賞【The Game Awards】GO!
1200作品から選ばれた14作品―「ゲームクリエイター甲子園 2023」総合大賞ファイナリスト14作品発表 GO!
任天堂/ハピネット等新たなスポンサー4社が参加―「Indie Developers Conference 2023」セッション/ライトニングトークのタイムテーブル発表 GO!
Best Esports Gameは『Valorant』が受賞!選手やコーチなどeスポーツ関連一斉発表【TGA2023】GO!

有料Modのサポートを開始した『スカイリム』完全新作のみを取り扱い、AI生成物の使用は禁止に GO!
「Nintendo Live 2024 TOKYO」が執拗な脅迫行為により中止に…『スプラ3』バンカライブや『ゼルダ』コンサートなどが予定されていた GO!
2年半に渡る『Destiny2』不正行為巡るBungieとチート販売業者の訴訟が来週陪審裁判へ―議論の焦点は「著作権」にあり GO!