講義メモ

テキスト篇:第11章「構造体」
ゲーム開発演習:imageオブジェクトのプロパティ、画像の重ね合わせなど

参考:魔導士クラスの改良例(静的メンバの活用による引数付きコンストラクタの廃止・一本化)

//オブジェクト指向演習 RPG演習6・改
using System;
class Madoshi { //魔導士を表すクラス
    //継承するデータメンバ
    protected int mp; //MP(カプセル化(p.166)のためにpublicにしない)
    protected int hp; //HP(〃)
    protected static Random rnd = new Random(); //乱数用のRandomクラスのインスタンスを生成
    //継承しないデータメンバ
    int id; //魔導士番号
    static int num = 0; //魔導士の数
    //デフォルトコンストラクタ
    public Madoshi() { 
        mp = rnd.Next(10); //0~9の間のランダムな整数を得てMPの初期値とする
        hp = 10 + rnd.Next(10); //10~19の間のランダムな整数を得てHPの初期値とする
        num++; //【追加】魔導士の数をカウントアップ
        id = num; //【変更】魔導士の数で魔導士番号を決める
    }
    //プロパティ
    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(),new Madoshi(),new Madoshi() }; //【変更】全魔導士を生成
        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; //ゲーム終了
            }
        }
    }
}

p.275 構造体とは

・C言語の構造体は複数の変数や配列を1まとめにしたデータ構造
・対して、C#の構造体は軽量クラス=クラスの仕様を一部除いて軽くしたもので、同時にC言語からの移植にも対応する
・よって「なにが除かれたか」に着目すると良い
・定義書式: struct 構造体名 {…}
・構造体のメンバはデータメンバ(クラスと同様)、メソッド、プロパティ、インデクサ等
・インターフェイスを実装できる
・実装の書式: struct 構造体名 : インターフェイス名,… {…}
・構造体の制限事項:
 ・他のクラスを継承できない
 ・データメンバの初期化ができない(利用する側で行うこと)
 ・引数のないコンストラクタは自動生成のみ(自前で記述は禁止)
 ・オブジェクトの扱いが参照型ではなく値型なので、代入するとコピーされる

p.276 struct01.cs

//p.276 struct01.cs
using System;
struct MyStruct { //構造体の定義
    public int x; //宣言のみ可能で初期化はできない
    public void show()
    {
        Console.WriteLine("x = {0}", x);
    }
}
class struct01
{
    public static void Main()
    {
        MyStruct ms; //構造体変数の宣言と構造体オブジェクトの生成(new不要)
        ms.x = 10; //構造体変数.メンバ名で利用可
        ms.show(); //構造体変数.メソッド名(…)で利用可
    }
}

p.277 静的メンバを持つ構造体

・クラスの場合と全く同様
・「構造体名.メンバ名」で利用できる
・また、静的メンバであれば初期化可能
・よって、配列の静的メンバであれば要素の生成も可能

p.277 struct02.cs

//p.277 struct02.cs
using System;
struct MyStruct
{
    public static int x = 10; //静的メンバであれば初期化可能
    static int[] myarray = new int[10]; //静的メンバなので初期化(要素の生成)可能
    public static void show() { //静的メソッド
        Console.WriteLine("x = {0}", x);
    }
}
class struct02
{
    public static void Main()
    {
        MyStruct.show(); //構造体オブジェクトを生成せずに構造体名.メンバ名で利用可
        MyStruct.x = 20; //同上
        MyStruct.show(); //同上
    }
}

アレンジ演習:p.277 struct02.cs

・利用していないmyarrayの要素をMainメソッドで利用してみよう
・その為にインデクサを追記しよう
・構造体に静的でない配列データメンバyourarrayを追加し、要素の生成はMainメソッドで行うようにしよう

作成例

//アレンジ演習:p.277 struct02.cs
using System;
struct MyStruct
{
    public static int x = 10; //静的メンバであれば初期化可能
    static int[] myarray = new int[10]; //静的メンバなので初期化(要素の生成)可能
    public static void show() { //静的メソッド
        Console.WriteLine("x = {0}", x);
    }
    public int this[int n] { //【追加】インデクサ
        get { return myarray[n]; } set { myarray[n] = value; }
    }
    public int[] yourarray; //【追加】静的メンバではないので配列は宣言のみ
}
class struct02
{
    public static void Main()
    {
        MyStruct.show(); //構造体オブジェクトを生成せずに構造体名.メンバ名で利用可
        MyStruct.x = 20; //同上
        MyStruct.show(); //同上
        MyStruct a = new MyStruct(); //【追加】インデクサ用の構造体オブジェクトを生成
        a[0] = 100; //【追加】インデクサ経由で代入
        Console.WriteLine("a[0] = {0}", a[0]); //【追加】インデクサ経由で利用
        a.yourarray = new int[3]; //【追加】要素の生成は利用側で行う
        a.yourarray[0] = 200; //【追加】代入
        Console.WriteLine("a.yourarray[0] = {0}", a.yourarray[0]); //【追加】利用
    }
}

p.278 コンストラクタを持つ構造体

・引数のないコンストラクタは自動生成のみ(自前で記述は禁止)
・なお、オブジェクトの扱いが参照型ではなく値型なので、代入するとコピーされる

p.278 struct03.cs

//p.278 struct03.cs
using System;
struct MyStruct {
    public int x;
    //public MyStruct() { //引数のないコンストラクタは定義不可(自動生成のみ)
    //    x = 0;
    //}
    public MyStruct(int a) { //引数のあるコンストラクタは定義可能
        x = a;
    }
}
class struct03 {
    public static void Main() {
        MyStruct ms = new MyStruct(); //自動生成の引数のないコンストラクタを呼ぶ
        Console.WriteLine("ms.x = {0}", ms.x); //ms.xは0で初期化されている
        MyStruct ms2 = new MyStruct(100); //自前の引数のあるコンストラクタを呼ぶ
        Console.WriteLine("ms2.x = {0}", ms2.x); //ms2.xは100で初期化されている
        MyStruct ms3; //MyStruct型変数ms3を宣言(new不要)
        ms3 = ms2; // ms2の値をms3に代入(値型なのでオブジェクトのコピーになる)
        Console.WriteLine("ms3.x = {0}", ms3.x); // 当然ms3.xの値は100
        ms3.x = 50; //ms3.xの値を50に変更
        // ms3.xの値を50に変更してもms2.xは影響を受けない(コピーなので)
        Console.WriteLine("ms2.x = {0}", ms2.x);
    }
}

p.280 struct04.cs

//p.280 struct04.cs
using System;
struct MyStruct { //構造体の定義
    public int x;
}
class MyClass { //クラスの定義
    public int x;
}
class struct04 {
    public static void Main() {
        MyStruct ms1 = new MyStruct(); //構造体オブジェクトの生成
        MyStruct ms2; //構造体変数の宣言
        ms1.x = 20;
        ms2 = ms1; //構造体オブジェクトをコピー(よってms1とms2は別のオブジェクトを指す)
        Console.WriteLine("ms2.x = {0}", ms2.x); //コピーしたので20
        ms2.x = 10; //コピー結果を書き換えても
        Console.WriteLine("ms1.x = {0}", ms1.x); //コピーの元なので変わらず20
        MyClass mc1 = new MyClass(); //クラスオブジェクトの生成
        MyClass mc2; //参照変数の宣言
        mc1.x = 20;
        mc2 = mc1; //参照のコピーなのでmc1とmc2は同じオブジェクトを指す
        Console.WriteLine("mc2.x = {0}", mc2.x); //同じオブジェクトなので20
        mc2.x = 10; //参照経由で書き換えたので
        Console.WriteLine("mc1.x = {0}", mc1.x); //元のオブジェクトの値が書き変わる
    }
}

コメントを残す

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