前回のコメント

・今回の大魔道作成で継承が出てきました。なんとなく作りは理解できましたが、まだまだ使いこなすのは
難しいなと思いました(;’∀’)

 やっと、基礎を抜けたところですので、焦らずにステップアップしましょう。

・コードにもいろいろなテクニックがあってとても勉強になりました。

 何よりです。

講義メモ

オブジェクト指向演習:「3人の魔導士」を仕上げて「大魔道出現」へ
ゲーム開発演習:imageオブジェクトのプロパティ、画像の重ね合わせなど

RPG演習5 「3人の魔導士」仕様の検討(つづき)

「3人の魔導士」の問題点を解決しよう=第3段階

① 3人の魔導士のHP、MPの初期値が異なるようにしたい
・合わせて、Randomオブジェクトを1個にして効率を上げるとよい
・MadoshiクラスのRandomオブジェクトを3人の魔導士が順に使えば良い
・方法は、MadoshiのRandomオブジェクトをstaticにしよう

② HPが尽きた魔導士は攻撃できないようにしたい
・MadoshiクラスのFight()に「hpが0以下なら0を返す」を追記

③ HPが尽きた魔導士を攻撃すると「魔導士●はもういない」と表示して無視し、魔導士の攻撃に進もう
・minimadouクラスで魔道士を攻撃する前にその魔道士のHPが0超であることをチェックしよう
・0以下の場合は「魔導士●はもういない」と表示する

④ 魔導士の攻撃中にプレイヤーのHPが0以下になったら、そこで終わらせたい
・魔道士の攻撃を全魔導士について繰返す条件に、プレイヤーのHPが0超であることを加えれば良い
・逃げられなくて、魔道士の攻撃を全魔導士について繰返す場合も同様

⑤ 3人を倒したら「3人の魔導士を倒した!」と表示しよう
・「あなたは死にました。」の表示後にreturnしよう(Mainメソッドから抜ける=終了)
・メインループの終了後に(逃げた場合を覗いて)「3人の魔導士を倒した!」と表示
・この判断のために、入力用のansをnullで初期化しておこう

作成例

//オブジェクト指向演習 RPG演習5(5)
using System;
class Madoshi { //魔導士を表すクラス
    int mp; //MP(カプセル化(p.166)のためにpublicにしない)
    int hp; //HP(〃)
    int id; //魔導士番号
    static Random rnd = new Random(); //【変更】乱数用のRandomクラスのインスタンスを生成
    public Madoshi(int id) { //コンストラクタ(p.167)で魔導士番号を受け取る
        mp = rnd.Next(10); //0~9の間のランダムな整数を得てMPの初期値とする
        hp = 10 + rnd.Next(10); //10~19の間のランダムな整数を得てHPの初期値とする
        this.id = id; //引数から魔導士番号を受け取る
    }
    public int HP { //HPを返すプロパティ
        get { return hp; } 
    }
    public void DispInfo() { //魔導士の情報を表示する
        Console.Write("魔導士{0}(HP={1} MP={2})", id, hp, mp); //情報表示(改行しない)
    }
    public int Fight() { //魔導士があなたを攻撃し、あなたのダメージ値を返す
        if (hp <= 0) return 0; //【追加】死んでいる場合は0を返す
        if (rnd.Next(2) == 1) { //確率1/2で魔法攻撃かどうかを選ぶ
            int j = rnd.Next(5) + 1; //消費MPは1~5の乱数
            DispInfo(); // 魔導士の情報を表示する
            Console.WriteLine("は消費MP" + j + "の魔法を唱えた!");
            if (mp < j) { // MP不足?
                Console.WriteLine("MPが足りない!");
                return 0; // あなたのダメージ=0を返す(攻撃終了)
            } else { // MP充足?
                Console.WriteLine("あなたは" + j * 4 + "のダメージを受けた!");
                mp -= j; //MP消費
                return j * 4; //あなたのダメージ=消費MPの4倍を返す(攻撃終了)
            }
        } else { //通常攻撃?
            int k = rnd.Next(5) + 1; //攻撃力は1~5の乱数
            DispInfo(); //魔導士の情報を表示する
            Console.WriteLine("の攻撃!");
            Console.WriteLine("あなたは" + k + "のダメージを受けた!");
            return k; //あなたのダメージ=1~50を返す(攻撃終了)
        }
    }
    public void Damage() { //魔導士があなたからダメージをくらう
        int k = 5 + rnd.Next(5); //あなたの攻撃力は5~9の乱数
        Console.WriteLine("あなたの攻撃!");
        DispInfo(); //魔導士の情報を表示する(攻撃を受けた後の魔導士のHPを表示)
        Console.WriteLine("に" + k + "のダメージを与えた!");
        hp -= k; //魔導士のHPをダウン
    }
}
class minimadou { //ゲーム本体のクラス
    public static void Main() { //ゲームを進行するメソッド
        Random rnd = new Random(); //乱数用のRandomクラスのインスタンスを生成
        string ans = null; //【変更】入力用
        // 戦闘開始
        int myhp = 40 + rnd.Next(21); //あなたのHPを40+0から40+20にする
        Madoshi[] m = { new Madoshi(1),new Madoshi(2),new Madoshi(3) }; //全魔導士を生成
        foreach (var w in m) { //全魔導士ついて繰返す
            w.DispInfo(); //魔導士の情報を表示する
            Console.WriteLine("が現れた。");
        }
        // 戦闘中(あなたが死ぬか,魔導士が死ぬまで繰り返す)
        while (myhp > 0 && (m[0].HP > 0 || m[1].HP > 0 || m[2].HP > 0)) { //あなたとどれかの魔導士のHPがある間
            Console.Write("残りHPは{0}です。どれを攻撃しますか?(1/2/3:魔導士1,2,3 0:やめる):", myhp); 
            ans = Console.ReadLine(); //回答を得る
            int ansi = int.Parse(ans) - 1; //整数変換して-1し魔導士の添字0~2を得る
            if (ansi == 0 || ansi == 1 || ansi == 2) { //魔導士を攻撃?
                if (m[ansi].HP > 0) { //【追加】活きている魔導士?
                    m[ansi].Damage(); //魔導士を攻撃する
                    if(m[ansi].HP <= 0) { //魔導士[]を倒した?
                        Console.WriteLine("魔導士{0}を倒した!", ans);
                    }
                } else { //【以下追加】
                    Console.WriteLine("魔導士{0}はもういない!", ans);
                }
                for (int i = 0; i < 3 && myhp > 0; i++) { //【変更】全魔導士について繰返す
                    myhp -= m[i].Fight(); //魔導士の攻撃!あなたのHPをマイナス
                }
            } else { // やめる?
                if (rnd.Next(2) == 0) { //確率50%で
                    Console.WriteLine("逃げることができた!");
                    break; // 繰り返し終了(p.125)
                } else {
                    Console.WriteLine("逃げられない!");
                    for (int i = 0; i < 3 && myhp > 0; i++) { //【変更】全魔導士について繰返す
                        myhp -= m[i].Fight(); //魔導士の攻撃!あなたのHPをマイナス
                    }
                }
            }
            if (myhp <= 0) { //あなたのHPがもうない?
                Console.WriteLine("あなたは死にました。");
                return; //【追加】ゲーム終了
            }
        }
        if (ans != "0") { //【以下追加】逃げたのでなければ
            Console.WriteLine("3人の魔導士を倒した!");
        }
    }
}

RPG演習6 「大魔導出現」仕様の検討

・大魔導はDaimadoクラス(Madoshiクラスの派生クラス)で実装しよう
・Fight()、Damage()メソッド、HPを返すプロパティは継承
・DispInfo()メソッドはオーバーライドして「大魔道(HP={0} MP={1})」表示にしよう
・データメンバはそのまま(魔導士番号は使わない)
・コンストラクタは引数不要でMPの初期値を40~49、HPの初期値を40~49にしよう(idは用いない)
・minimadouクラスでは「3人の魔導士を倒した!」の後に「大魔導が現れた!」に変更(つまり、逃げると大魔導)
・その後で「戦闘中(あなたが死ぬか,魔導士が死ぬまで繰り返す)」と同等の内容を「あなたか,大魔導が死ぬまで繰り返す」で
 行えば良い
・「どれを攻撃しますか?(1/2/3:魔導士1,2,3 0:やめる)」を「攻撃しますか?(1:する 0:やめる)」にしよう
・「整数変換して-1し魔導士の添字0~2を得る」は不要
・「魔導士を攻撃?」は「1ならば」でOK
・「活きている魔導士?」チェックは不要
・「魔導士{0}を倒した!」は「大魔導を倒した!」にして、returnしよう(Mainメソッドから抜ける=終了)
・「魔導士{0}はもういない!」は不要
・「全魔導士について繰返す」必要はない
・「逃げられない」のあとも、「全魔導士について繰返す」必要はない
・「3人の魔導士を倒した!」は不要
・大魔導との戦闘開始前にプレイヤーのHPを40~49に回復する(現HPより上回る場合のみ)

作成例

//オブジェクト指向演習 RPG演習6
using System;
class Madoshi { //魔導士を表すクラス
    protected int mp; //【変更】MP(カプセル化(p.166)のためにpublicにしない)
    protected int hp; //【変更】HP(〃)
    int id; //魔導士番号
    protected static Random rnd = new Random(); //【変更】乱数用のRandomクラスのインスタンスを生成
    public Madoshi() { } //【追加】デフォルトコンストラクタ(継承用)
    public Madoshi(int id) { //コンストラクタ(p.167)で魔導士番号を受け取る
        mp = rnd.Next(10); //0~9の間のランダムな整数を得てMPの初期値とする
        hp = 10 + rnd.Next(10); //10~19の間のランダムな整数を得てHPの初期値とする
        this.id = id; //引数から魔導士番号を受け取る
    }
    public int HP { //HPを返すプロパティ
        get { return hp; } 
    }
    public virtual void DispInfo() { //【変更】魔導士の情報を表示する
        Console.Write("魔導士{0}(HP={1} MP={2})", id, hp, mp); //情報表示(改行しない)
    }
    public int Fight() { //魔導士があなたを攻撃し、あなたのダメージ値を返す
        if (hp <= 0) return 0; //死んでいる場合は0を返す
        if (rnd.Next(2) == 1) { //確率1/2で魔法攻撃かどうかを選ぶ
            int j = rnd.Next(5) + 1; //消費MPは1~5の乱数
            DispInfo(); // 魔導士の情報を表示する
            Console.WriteLine("は消費MP" + j + "の魔法を唱えた!");
            if (mp < j) { // MP不足?
                Console.WriteLine("MPが足りない!");
                return 0; // あなたのダメージ=0を返す(攻撃終了)
            } else { // MP充足?
                Console.WriteLine("あなたは" + j * 4 + "のダメージを受けた!");
                mp -= j; //MP消費
                return j * 4; //あなたのダメージ=消費MPの4倍を返す(攻撃終了)
            }
        } else { //通常攻撃?
            int k = rnd.Next(5) + 1; //攻撃力は1~5の乱数
            DispInfo(); //魔導士の情報を表示する
            Console.WriteLine("の攻撃!");
            Console.WriteLine("あなたは" + k + "のダメージを受けた!");
            return k; //あなたのダメージ=1~50を返す(攻撃終了)
        }
    }
    public void Damage() { //魔導士があなたからダメージをくらう
        int k = 5 + rnd.Next(5); //あなたの攻撃力は5~9の乱数
        Console.WriteLine("あなたの攻撃!");
        DispInfo(); //魔導士の情報を表示する(攻撃を受けた後の魔導士のHPを表示)
        Console.WriteLine("に" + k + "のダメージを与えた!");
        hp -= k; //魔導士のHPをダウン
    }
}
class Daimado : Madoshi { //【以下追加】大魔導クラス(魔導士クラスの派生クラス)
    public Daimado() { //コンストラクタ
        mp = 40 + rnd.Next(10); //40~49の間のランダムな整数を得てMPの初期値とする
        hp = 40 + rnd.Next(10); //40~49の間のランダムな整数を得てHPの初期値とする
    }
    public override void DispInfo() { //大魔導の情報を表示する
        Console.Write("大魔導(HP={0} MP={1})", hp, mp); //情報表示(改行しない)
    }
}
class minimadou { //ゲーム本体のクラス
    public static void Main() { //ゲームを進行するメソッド
        Random rnd = new Random(); //乱数用のRandomクラスのインスタンスを生成
        string ans = null; //入力用
        // 戦闘開始
        int myhp = 40 + rnd.Next(21); //あなたのHPを40+0から40+20にする
        Madoshi[] m = { new Madoshi(1),new Madoshi(2),new Madoshi(3) }; //全魔導士を生成
        foreach (var w in m) { //全魔導士ついて繰返す
            w.DispInfo(); //魔導士の情報を表示する
            Console.WriteLine("が現れた。");
        }
        // 戦闘中(あなたが死ぬか,魔導士が死ぬまで繰り返す)
        while (myhp > 0 && (m[0].HP > 0 || m[1].HP > 0 || m[2].HP > 0)) { //あなたとどれかの魔導士のHPがある間
            Console.Write("残りHPは{0}です。どれを攻撃しますか?(1/2/3:魔導士1,2,3 0:やめる):", myhp); 
            ans = Console.ReadLine(); //回答を得る
            int ansi = int.Parse(ans) - 1; //整数変換して-1し魔導士の添字0~2を得る
            if (ansi == 0 || ansi == 1 || ansi == 2) { //魔導士を攻撃?
                if (m[ansi].HP > 0) { //活きている魔導士?
                    m[ansi].Damage(); //魔導士を攻撃する
                    if(m[ansi].HP <= 0) { //魔導士[]を倒した?
                        Console.WriteLine("魔導士{0}を倒した!", ans);
                    }
                } else { 
                    Console.WriteLine("魔導士{0}はもういない!", ans);
                }
                for (int i = 0; i < 3 && myhp > 0; i++) { //全魔導士について繰返す
                    myhp -= m[i].Fight(); //魔導士の攻撃!あなたのHPをマイナス
                }
            } else { // やめる?
                if (rnd.Next(2) == 0) { //確率50%で
                    Console.WriteLine("逃げることができた!");
                    break; // 繰り返し終了(p.125)
                } else {
                    Console.WriteLine("逃げられない!");
                    for (int i = 0; i < 3 && myhp > 0; i++) { //全魔導士について繰返す
                        myhp -= m[i].Fight(); //魔導士の攻撃!あなたのHPをマイナス
                    }
                }
            }
            if (myhp <= 0) { //あなたのHPがもうない?
                Console.WriteLine("あなたは死にました。");
                return; //ゲーム終了
            }
        }
        if (ans != "0") { //逃げたのでなければ
            Console.WriteLine("3人の魔導士を倒した!");
        }
        //【以下追加】ボス戦開始
        Daimado d = new Daimado(); //大魔導を生成
        d.DispInfo(); //大魔導の情報を表示する
        Console.WriteLine("が現れた。");
        int newhp = 40 + rnd.Next(10); //新しいHPを40から49で決める
        if (myhp < newhp) { //回復する?
            myhp = newhp; //HPを回復
            Console.WriteLine("HPを回復!");
        }
        // 戦闘中(あなたが死ぬか,大魔導が死ぬまで繰り返す)
        while (myhp > 0 && d.HP > 0) { //あなたと大魔導のHPがある間
            Console.Write("残りHPは{0}です。攻撃しますか?(1:攻撃する 0:やめる):", myhp); 
            ans = Console.ReadLine(); //回答を得る
            if (ans == "1") { //大魔導を攻撃?
                d.Damage(); //大魔導を攻撃する
                if(d.HP <= 0) { //大魔導を倒した?
                    Console.WriteLine("大魔導を倒した!");
                    break; //ゲーム終了
                }
                myhp -= d.Fight(); //大魔導の攻撃!あなたのHPをマイナス
            } else { // やめる?
                if (rnd.Next(2) == 0) { //確率50%で
                    Console.WriteLine("逃げることができた!");
                    break; //ゲーム終了
                } else {
                    Console.WriteLine("逃げられない!");
                    myhp -= d.Fight(); //大魔導の攻撃!あなたのHPをマイナス
                }
            }
            if (myhp <= 0) { //あなたのHPがもうない?
                Console.WriteLine("あなたは死にました。");
                break; //ゲーム終了
            }
        }
    }
}

提出:「3人の魔導士・大魔道出現」

今週の話題

販売本数ランキング 今回トップも「スーパーマリオブラザーズ ワンダー(Switch)」GO!
12月17日開催「Indie Developers Conference 2023」チケット販売開始―インディーゲーム開発にまつわる様々なノウハウを共有 GO!
Gaijin社の新たなる試み?11周年迎えた『War Thunder』などで使用される独自のゲームエンジン「DagorEngine」がオープンソースに GO!
日テレグループYouTube MCN「C+」、カプコンとの著作物使用許諾契約書を締結―ゲーム利用のコンテンツ投稿/収益化が可能に GO!

サイバーエージェント通期業績、大幅減益―『ウマ娘』の反動大きく GO!
人間が苦手なステージはAIも苦手―「AAAゲームバランス調整の次元の呪い」セッションレポート【CEDEC2023】GO!
『テラビット』へ集まった批判―サイバーステップ2割超の減収、MSワラントによる調達へ GO!
「あおぎり高校」が誹謗中傷対策委員会を設置…「ぶいすぽっ!」運営との連携で誹謗中傷への対策、抑止、根絶に挑む GO!

前回のコメント

・今回、コードをよくよく読んでみるっというのを意識しながらやってみることによって流れを理解しやすくなったなと実感できました。

 何よりです。

・たまに躓くと勢いよく置かれますね笑

 いつでも「ちょっと待って!」「もう一回!」と声をかけてください。

講義メモ ゲーム開発演習

ゲーム開発演習:描画処理の実装、背景画像の描画など

テーマ5 描画処理の基礎【再掲載】

・ウィンドウを持つアプリケーションでは、プログラム側からの描画と、システム側からの必要に応じた描画がある
・この両方に対応するために、C#のフォームアプリケーションでは、Formクラスでvoid OnPaint(PaintEventArgs)メソッドを提供
 している
・この引数のPaintEventArgsはシステム側が提供する「画面を描くための情報」
・フォームアプリケーションではこのメソッドをオーバーライドして、描画処理を記述する(アクセス修飾子はprotecedにすること)
・この時、フォームの基本再描画処理がFormクラスで与えられるので、オーバーライドの冒頭において、FormクラスのOnPaintメソッドの
 内容を実行すること
・この実行には、PaintEventArgsを引数として渡す
・書式例:
 protected override void OnPaint(PaintEventArgs e) { //Formのメソッドをオーバーライド
  base.OnPaint(e); //まず、Formクラスにおけるメソッドの内容(基本再描画処理)を実行
  :自前の描画処理を書くところ
 }

演習5 OnPaintメソッドの動作確認

・描画処理の実装の準備として、OnPaintメソッドのオーバーライドを追加しよう
・自前の描画処理を書くところにメッセージボックスの表示を記述して、このオーバーライドがいつ動作するのかを確認しよう

作成例

//演習5 OnPaintメソッドの動作確認
using System; //フォームアプリケーションに必須
using System.Windows.Forms; //フォームアプリケーションに必須
class Program : Form { //Formクラスを継承
    protected override void OnPaint(PaintEventArgs e) { //【以下追加】Formのメソッドをオーバーライド
        base.OnPaint(e); //まず、Formクラスにおけるメソッドの内容(基本再描画処理)を実行
        MessageBox.Show("OK"); //メッセージボックスを表示
    }
    static void Main() { //publicの指定は任意
        Program p = new Program(); //継承したフォームのインスタンスを生成
        p.Width = 660; //インスタンスのWidthプロパティに幅を代入
        p.Height = 520; //インスタンスのHeightプロパティに高さを代入
        p.Text = "Game"; //インスタンスのTextプロパティにフォーム名を代入
        p.ControlBox = false; //ControlBoxを非表示にする
        Application.Run(p); //インスタンスを画面に出す
    }
}

テーマ6 リソースの配置

・プログラムで用いる外部ファイルは、デバッグ用と本番用のそれぞれの配置場所がある
・デバッグ用の配置場所は「プロジェクトのフォルダ\bin\Debug」
・ここには「デバッグなしで実行」用のexeファイルが生成されているので、ファイル名のみでアクセス可能
・このフォルダに、画像、音声、動画、テキストデータなどのファイルを配置してプログラム側から読み込める

テーマ7 フォーム上への画像の描画

・静止画像ファイルとしては、主にBMP形式、GIF形式、PNG形式の画像ファイルを用いる
 BMP=ビットマップの略でWindows用画像形式。無圧縮または低圧縮で、色数は自由
 GIF=グラフィック・インターチェンジ・フォーマットの略で汎用画像形式。低圧縮。色数は128色までだが、
 透過色を手軽に指定でき、処理が軽い
 PNG=ポータブル・ネットワーク・グラフィクス略で汎用画像形式。高圧縮。色数はフルカラーで高品質だが、処理が重くなりやすい
・このファイル名をSystem.Drawing.ImageクラスのImage FromFile()静的メソッドに文字列として渡すと、
 画像を含むImageオブジェクトが生成され、参照が得られる。
 例: Image myimage = System.Drawing.Image.FromFile("XEVIUS.bmp");
・描画には、GraphicsクラスのDrawImage()メソッドを用いる。
・この実行に必要なGraphicsオブジェクトを得るためにOnPaintメソッドの引数「PaintEventArgs e」を用いる
 ※ よって、描画処理にはOnPaintメソッドのオーバーライドが必要
・PaintEventArgsクラスにあるGraphicsプロパティを呼ぶと、DrawImage用のGraphicsオブジェクトが得られる
・DrawImage()メソッドの引数はImageオブジェクトの参照、X座標、Y座標
・なお、X座標、Y座標はウィンドウの左上隅を(0,0)として定める
 例: e.Graphics.DrawImage(myimage, 0, 0);

提出:演習6 背景画像の描画

・テスト用画像を下記からダウンロードしてリソースのフォルダに格納
 https://ha221.rundog.org/wp-content/uploads/back.bmp
・この画像を表示しよう

講義メモ

・オブジェクト指向演習:「3人の魔導士」
・ゲーム開発演習:描画処理の実装、背景画像の描画など

RPG演習5 「3人の魔導士」

・Madoshiクラスからインスタンスを3つ生成し3人の魔導士としよう
・3人の魔導士は「魔導士1」「魔導士2」「魔導士3」と表現しよう
・開始時点で魔導士の情報を3人分表示しよう
例:
 魔道士1(HP=13,MP=5)が現れた。
 魔道士2(HP=13,MP=5)が現れた。
 魔道士3(HP=13,MP=5)が現れた。
・「攻撃しますか?(1:はい 0:やめて逃げる):」を
 「どれを攻撃しますか?(1/2/3:魔導士を攻撃 0:逃げる):」に変更しよう
・ここで0/1/2/3以外が入力されたら、入力しなおしとしよう
・ゲームの終了条件を「あなたが死ぬか魔導士が全員死ぬまで」に変更しよう
・魔道士の攻撃では、その時点で生きている魔導士が1,2,3の順で連続攻撃する。
 その途中ででもあなたのHPが0以下になったら「あなたは死にました。」と表示して終了しよう。
・あなたのHPを40~60の乱数にしよう

RPG演習5 「3人の魔導士」仕様の検討

・出来る限り、最小限の変更にとどめよう
【Madoshiクラス】
・魔導士番号を示す「int id」をインスタンス変数に追加しよう
・コンストラクタにint型の引数idを追加し、これを用いて魔導士番号を設定しよう
・DispInfoメソッドで魔導士番号も表示しよう

RPG演習5(1) Madoushiクラスの改造(まだ実行しない)

//オブジェクト指向演習 RPG演習5(1)
using System;
class Madoshi { //魔導士を表すクラス
    int mp; //MP(カプセル化(p.166)のためにpublicにしない)
    int hp; //HP(〃)
    int id; //【追加】魔導士番号
    Random rnd = new Random(); //乱数用のRandomクラスのインスタンスを生成
    public Madoshi(int id) { //【変更】コンストラクタ(p.167)で魔導士番号を受け取る
        mp = rnd.Next(10); //0~9の間のランダムな整数を得てMPの初期値とする
        hp = 10 + rnd.Next(10); //10~19の間のランダムな整数を得てHPの初期値とする
        this.id = id; //【追加】引数から魔導士番号を受け取る
    }
    public int HP { //HPを返すプロパティ
        get { return hp; } 
    }
    public void DispInfo() { //魔導士の情報を表示する
        Console.Write("魔道士{0}(HP={1} MP={2})", id, hp, mp); //【変更】情報表示(改行しない)
    }
    public int Fight() { //魔道士があなたを攻撃し、あなたのダメージ値を返す
        if (rnd.Next(2) == 1) { //確率1/2で魔法攻撃かどうかを選ぶ
            int j = rnd.Next(5) + 1; //消費MPは1~5の乱数
            DispInfo(); // 魔導士の情報を表示する
            Console.WriteLine("は消費MP" + j + "の魔法を唱えた!");
            if (mp < j) { // MP不足?
                Console.WriteLine("MPが足りない!");
                return 0; // あなたのダメージ=0を返す(攻撃終了)
            } else { // MP充足?
                Console.WriteLine("あなたは" + j * 4 + "のダメージを受けた!");
                mp -= j; //MP消費
                return j * 4; //あなたのダメージ=消費MPの4倍を返す(攻撃終了)
            }
        } else { //通常攻撃?
            int k = rnd.Next(5) + 1; //攻撃力は1~5の乱数
            DispInfo(); //魔導士の情報を表示する
            Console.WriteLine("の攻撃!");
            Console.WriteLine("あなたは" + k + "のダメージを受けた!");
            return k; //あなたのダメージ=1~50を返す(攻撃終了)
        }
    }
    public void Damage() { //魔道士があなたからダメージをくらう
        int k = 5 + rnd.Next(5); //あなたの攻撃力は5~9の乱数
        Console.WriteLine("あなたの攻撃!");
        DispInfo(); //魔導士の情報を表示する(攻撃を受けた後の魔道士のHPを表示)
        Console.WriteLine("に" + k + "のダメージを与えた!");
        hp -= k; //魔道士のHPをダウン
    }
}

RPG演習5 「3人の魔導士」仕様の検討(つづき)

・まずは「1人の魔導士」として実行したい
・Madoshiクラスに合わせて、minimadouクラスを書き換えよう
① 魔道士を生成するときに魔導士番号1を渡す
②「攻撃しますか?(1:はい 0:やめる)」を「どれを攻撃しますか?(1:魔導士1 0:やめる)」

RPG演習5(2) minimadouクラスの改造「1人の魔導士」(実行可能)

class minimadou { //ゲーム本体のクラス
    public static void Main() { //ゲームを進行するメソッド
        Random rnd = new Random(); //乱数用のRandomクラスのインスタンスを生成
        string ans; //入力用
        // 戦闘開始
        int myhp = 20; // あなたのHP
        Madoshi m = new Madoshi(1); //【変更】魔道士を生成
        m.DispInfo(); // 魔道士の情報を表示する
        Console.WriteLine("が現れた。");
        // 戦闘中(あなたが死ぬか,魔道士が死ぬまで繰り返す)
        while (myhp > 0 && m.HP > 0) { //あなたのHPがあり、魔導士のHPがある間
            Console.Write("残りHPは{0}です。どれを攻撃しますか?(1:魔導士1 0:やめる):", myhp); //【変更】
            ans = Console.ReadLine(); // 攻撃するかどうかを得る
            if (ans == "1") { // はい?
                m.Damage(); // 魔道士を攻撃する
                if (m.HP > 0) { //魔道士は死んでない?
                    myhp -= m.Fight(); // 魔道士の攻撃!あなたのHPをマイナス
                } else { // 魔道士のHPがもうない?
                    Console.WriteLine("魔道士を倒した!");
                }
            } else { // やめる?
                if (rnd.Next(2) == 0) { //確率50%で
                    Console.WriteLine("逃げることができた!");
                    break; // 繰り返し終了(p.125)
                } else {
                    Console.WriteLine("逃げられない!");
                    myhp -= m.Fight(); // 魔道士の攻撃!あなたのHPをマイナス
                }
            }
            if (myhp <= 0) { //あなたのHPがもうない?
                Console.WriteLine("あなたは死にました。");
            }
        }
    }
}

RPG演習5 「3人の魔導士」仕様の検討(つづき)

・「1人の魔導士」を「3人の魔導士」にしよう=第1段階
・あなたのHPを20固定ではなく、40~60の乱数にしよう
 int myhp = 40 + rnd.Next(21); //あなたのHPを40+0から40+20にする
・魔導士の参照変数mを配列[3]にして、魔導士1~3を生成しよう
 Madoshi[] m = {new Madoshi(1), new Madoshi(2), new Madoshi(3)};
・「魔道士の情報を表示する」を3人分にしよう
 foreach (var w in m) { //全魔導士について繰返す
      w.DispInfo(); // 魔道士の情報を表示する
      Console.WriteLine("が現れた。");
 }
・魔導士mを魔導士m[0]に書き換えよう(まずは「1人の魔導士」のままでOK)

RPG演習5(3) minimadouクラスの改造「3人いるけど1人の魔導士」(実行可能)

//オブジェクト指向演習 RPG演習5(3)
using System;
class Madoshi { //魔導士を表すクラス
    int mp; //MP(カプセル化(p.166)のためにpublicにしない)
    int hp; //HP(〃)
    int id; //【追加】魔導士番号
    Random rnd = new Random(); //乱数用のRandomクラスのインスタンスを生成
    public Madoshi(int id) { //【変更】コンストラクタ(p.167)で魔導士番号を受け取る
        mp = rnd.Next(10); //0~9の間のランダムな整数を得てMPの初期値とする
        hp = 10 + rnd.Next(10); //10~19の間のランダムな整数を得てHPの初期値とする
        this.id = id; //【追加】引数から魔導士番号を受け取る
    }
    public int HP { //HPを返すプロパティ
        get { return hp; } 
    }
    public void DispInfo() { //魔導士の情報を表示する
        Console.Write("魔道士{0}(HP={1} MP={2})", id, hp, mp); //【変更】情報表示(改行しない)
    }
    public int Fight() { //魔道士があなたを攻撃し、あなたのダメージ値を返す
        if (rnd.Next(2) == 1) { //確率1/2で魔法攻撃かどうかを選ぶ
            int j = rnd.Next(5) + 1; //消費MPは1~5の乱数
            DispInfo(); // 魔導士の情報を表示する
            Console.WriteLine("は消費MP" + j + "の魔法を唱えた!");
            if (mp < j) { // MP不足?
                Console.WriteLine("MPが足りない!");
                return 0; // あなたのダメージ=0を返す(攻撃終了)
            } else { // MP充足?
                Console.WriteLine("あなたは" + j * 4 + "のダメージを受けた!");
                mp -= j; //MP消費
                return j * 4; //あなたのダメージ=消費MPの4倍を返す(攻撃終了)
            }
        } else { //通常攻撃?
            int k = rnd.Next(5) + 1; //攻撃力は1~5の乱数
            DispInfo(); //魔導士の情報を表示する
            Console.WriteLine("の攻撃!");
            Console.WriteLine("あなたは" + k + "のダメージを受けた!");
            return k; //あなたのダメージ=1~50を返す(攻撃終了)
        }
    }
    public void Damage() { //魔道士があなたからダメージをくらう
        int k = 5 + rnd.Next(5); //あなたの攻撃力は5~9の乱数
        Console.WriteLine("あなたの攻撃!");
        DispInfo(); //魔導士の情報を表示する(攻撃を受けた後の魔道士のHPを表示)
        Console.WriteLine("に" + k + "のダメージを与えた!");
        hp -= k; //魔道士のHPをダウン
    }
}
class minimadou { //ゲーム本体のクラス
    public static void Main() { //ゲームを進行するメソッド
        Random rnd = new Random(); //乱数用のRandomクラスのインスタンスを生成
        string ans; //入力用
        // 戦闘開始
        int myhp = 40 + rnd.Next(21); //【変更】あなたのHPを40+0から40+20にする
        Madoshi[] m = { new Madoshi(1),new Madoshi(2),new Madoshi(3) }; //【変更】全魔道士を生成
        foreach (var w in m) { //【追加】全魔道士ついて繰返す
            w.DispInfo(); //【変更】魔道士の情報を表示する
            Console.WriteLine("が現れた。");
        } //【追加】
        // 戦闘中(あなたが死ぬか,魔道士が死ぬまで繰り返す)
        while (myhp > 0 && m[0].HP > 0) { //あなたのHPがあり、魔導士のHPがある間
            Console.Write("残りHPは{0}です。どれを攻撃しますか?(1:魔導士1 0:やめる):", myhp); //【変更】
            ans = Console.ReadLine(); // 攻撃するかどうかを得る
            if (ans == "1") { // はい?
                m[0].Damage(); //【変更】魔道士を攻撃する
                if(m[0].HP > 0) { //【変更】魔道士は死んでない?
                    myhp -= m[0].Fight(); // 【変更】魔道士の攻撃!あなたのHPをマイナス
                } else { // 魔道士のHPがもうない?
                    Console.WriteLine("魔道士を倒した!");
                }
            } else { // やめる?
                if (rnd.Next(2) == 0) { //確率50%で
                    Console.WriteLine("逃げることができた!");
                    break; // 繰り返し終了(p.125)
                } else {
                    Console.WriteLine("逃げられない!");
                    myhp -= m[0].Fight(); //【変更】魔道士の攻撃!あなたのHPをマイナス
                }
            }
            if (myhp <= 0) { //あなたのHPがもうない?
                Console.WriteLine("あなたは死にました。");
            }
        }
    }
}

RPG演習5 「3人の魔導士」仕様の検討(つづき)

・「1人の魔導士」を「3人の魔導士」にしよう=第2段階
・繰返し条件を「あなたのHPがあり、全魔導士のHPがある間」にしよう
 while (myhp > 0 && (m[0].HP > 0 || m[1].HP > 0 || m[2].HP > 0)) {…}
・「どれを攻撃しますか?(1/2/3:魔導士1,2,3 0:やめる):」にしよう
・そして受け取ったansを整数変換して1~3ならば-1して魔導士の添字0~2を得よう
 int ansi = int.Parse(ans) - 1;
・魔導士m[0]を魔導士m[ansi]に書き換えよう(これで「3人の魔導士」になる)
・攻撃は全魔導士が順に行うとしよう

RPG演習5(4) minimadouクラスの改造「3人の魔導士:初版」(実行可能:問題あるけど)

//オブジェクト指向演習 RPG演習5(4)
using System;
class Madoshi { //魔導士を表すクラス
    int mp; //MP(カプセル化(p.166)のためにpublicにしない)
    int hp; //HP(〃)
    int id; //魔導士番号
    Random rnd = new Random(); //乱数用のRandomクラスのインスタンスを生成
    public Madoshi(int id) { //コンストラクタ(p.167)で魔導士番号を受け取る
        mp = rnd.Next(10); //0~9の間のランダムな整数を得てMPの初期値とする
        hp = 10 + rnd.Next(10); //10~19の間のランダムな整数を得てHPの初期値とする
        this.id = id; //引数から魔導士番号を受け取る
    }
    public int HP { //HPを返すプロパティ
        get { return hp; } 
    }
    public void DispInfo() { //魔導士の情報を表示する
        Console.Write("魔道士{0}(HP={1} MP={2})", id, hp, mp); //情報表示(改行しない)
    }
    public int Fight() { //魔道士があなたを攻撃し、あなたのダメージ値を返す
        if (rnd.Next(2) == 1) { //確率1/2で魔法攻撃かどうかを選ぶ
            int j = rnd.Next(5) + 1; //消費MPは1~5の乱数
            DispInfo(); // 魔導士の情報を表示する
            Console.WriteLine("は消費MP" + j + "の魔法を唱えた!");
            if (mp < j) { // MP不足?
                Console.WriteLine("MPが足りない!");
                return 0; // あなたのダメージ=0を返す(攻撃終了)
            } else { // MP充足?
                Console.WriteLine("あなたは" + j * 4 + "のダメージを受けた!");
                mp -= j; //MP消費
                return j * 4; //あなたのダメージ=消費MPの4倍を返す(攻撃終了)
            }
        } else { //通常攻撃?
            int k = rnd.Next(5) + 1; //攻撃力は1~5の乱数
            DispInfo(); //魔導士の情報を表示する
            Console.WriteLine("の攻撃!");
            Console.WriteLine("あなたは" + k + "のダメージを受けた!");
            return k; //あなたのダメージ=1~50を返す(攻撃終了)
        }
    }
    public void Damage() { //魔道士があなたからダメージをくらう
        int k = 5 + rnd.Next(5); //あなたの攻撃力は5~9の乱数
        Console.WriteLine("あなたの攻撃!");
        DispInfo(); //魔導士の情報を表示する(攻撃を受けた後の魔道士のHPを表示)
        Console.WriteLine("に" + k + "のダメージを与えた!");
        hp -= k; //魔道士のHPをダウン
    }
}
class minimadou { //ゲーム本体のクラス
    public static void Main() { //ゲームを進行するメソッド
        Random rnd = new Random(); //乱数用のRandomクラスのインスタンスを生成
        string ans; //入力用
        // 戦闘開始
        int myhp = 40 + rnd.Next(21); //あなたのHPを40+0から40+20にする
        Madoshi[] m = { new Madoshi(1),new Madoshi(2),new Madoshi(3) }; //全魔道士を生成
        foreach (var w in m) { //全魔道士ついて繰返す
            w.DispInfo(); //魔道士の情報を表示する
            Console.WriteLine("が現れた。");
        }
        // 戦闘中(あなたが死ぬか,魔道士が死ぬまで繰り返す)
        while (myhp > 0 && (m[0].HP > 0 || m[1].HP > 0 || m[2].HP > 0)) { //【変更】あなたとどれかの魔導士のHPがある間
            Console.Write("残りHPは{0}です。どれを攻撃しますか?(1/2/3:魔導士1,2,3 0:やめる):", myhp); //【変更】
            ans = Console.ReadLine(); //回答を得る
            int ansi = int.Parse(ans) - 1; //【追加】整数変換して-1し魔導士の添字0~2を得る
            if (ansi == 0 || ansi == 1 || ansi == 2) { //【変更】魔導士を攻撃?
                m[ansi].Damage(); //【変更】魔道士を攻撃する
                if(m[ansi].HP <= 0) { //【変更】魔道士[]を倒した?
                    Console.WriteLine("魔道士{0}を倒した!", ans); //【変更】
                }
                for (int i = 0; i < 3; i++) { //【追加】全魔導士について繰返す
                    myhp -= m[i].Fight(); //【変更】魔道士の攻撃!あなたのHPをマイナス
                }
            } else { // やめる?
                if (rnd.Next(2) == 0) { //確率50%で
                    Console.WriteLine("逃げることができた!");
                    break; // 繰り返し終了(p.125)
                } else {
                    Console.WriteLine("逃げられない!");
                    for (int i = 0; i < 3; i++) { //【追加】全魔導士について繰返す
                        myhp -= m[i].Fight(); //【変更】魔道士の攻撃!あなたのHPをマイナス
                    }
                }
            }
            if (myhp <= 0) { //あなたのHPがもうない?
                Console.WriteLine("あなたは死にました。");
            }
        }
    }
}

今週の話題

販売本数ランキング 今回トップは「スーパーマリオブラザーズ ワンダー(Switch)」GO!
『スト6』売上本数250万本間近!カプコンが最新の決算発表―『ロックマンエグゼ』コレクションも140万本突破、『モンハンNow』も人気に GO!
GOTYの栄光を手にするのは?「Golden Joystick Awards 2023」最も栄誉ある賞のノミネート作品が発表! GO!
任天堂が“コミュニティ大会”に関するガイドラインを公開…スポンサー禁止や出場、入場料、出場者数の上限などが細かく設定される GO!
最先端のAI技術でゲーム開発/グローバル展開を強力サポート―Keywords StudiosのCEOが語る世界的ソリューションプロバイダの強みとは GO!

チート・海賊版対策を「内製化」し技術という名の「武器」を手に入れた…カプコンがセキュリティへの取り組みを明かす GO!
ドリコムが赤字転落―新規ゲーム不調で未発表タイトルが開発中止へ GO!
過去にはカプコンも情報流出の被害に…ランサムウェア集団「ラグナロッカー」ついに中心メンバー摘発―国際警察機関の共同捜査が実を結ぶ GO!

前回のコメント

・プログラムをほかの人からいただいて使うときも全体の文を見て内容を理解して
 自分のものにできるようになりたいと思いました。

 是非。応援します。

・今回のRPGのプロジェクトでカプセル化があったりしたのですが、カプセル化をしないまま
 作ったらどういう影響があったりするのかもう一度教えていただけると嬉しいです。

 自分専用の使い捨てプログラムなら影響ありません。
 そうでない場合、作成したクラスを他者が(自分自身も)誤った使い方をしてしまうのを防止できなくなります。