前回のコメント

・今回習ったデリゲートは、インスタンス生成と似ていて書き方は覚えやすいなと感じました。

 何よりです。

・注意点に気を付けながら理解していきたいなと思いました。
 この先も聞いたことがないカタカナが出てくるので混乱状態にならないように頑張りま!

 是非。応援します。

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

矩形や円の描画、文字の描画、画面遷移 など

テーマ17 矩形の描画

・矩形(四角形)は、DrawRectangleメソッドにペン、左上X座標、左上Y座標、幅、高さを指定すると描画できる

演習14 判定エリアの描画

・赤色、太さ2で矩形の判定エリアを描画しよう
・開始座標は(78, 411)、幅は485、高さは64とする

作成例

//演習14 判定エリアの描画
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)
    protected override void OnPaint(PaintEventArgs e) { //Formのメソッドをオーバーライド
        base.OnPaint(e); //まず、Formクラスにおけるメソッドの内容(基本再描画処理)を実行
        e.Graphics.DrawImage(backb, 0, 0); //背景画像を(0,0)から描画
        //e.Graphics.DrawLine(mypen, 0, 0, backb.Width - 1, backb.Height - 1); //【削除】左上から右下へ赤色10の線を描画
        //mypen.Color = Color.Yellow; //【削除】ペン色を黄色に
        //mypen.Width = 20;  //【削除】ペン幅を20に
        //e.Graphics.DrawLine(mypen, backb.Width - 1, 0, 0, backb.Height - 1); //【削除】左上から右下へ黄色20の線を描画 
        e.Graphics.DrawRectangle(mypen, 78, 411, 485, 64); //【追加】判定エリアの描画
        int mx = backb.Width / 2, my = backb.Height / 2; //中央座標を得る
        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); //インスタンスを画面に出す
    }
}

テーマ18 円や楕円の描画

・円または楕円は、DrawEllipseメソッドにペン、左上X座標、左上Y座標、幅、高さを指定すると描画できる
・これは矩形を想定してその中に内接する楕円を描くイメージになる
・よって、幅と高さを同じ値にすれば円になり、その値が直径になる

演習15 同心円の描画

・領域の中央に、黄色い幅10のペンで、半径15の円を描こう
・その外側に、同じペンで半径30、45、60の円を描くことで同心円にしょう
・ヒント: すでに得ている中央座標(mx, my)を用いて、下記をi = 1から4までについて繰返すと良い
 左上X座標 = mx - 15 * i、左上Y座標 = my - 15 * i、幅 = 30 * i、高さ = 30 * i で円を描く

提出:演習15

講義メモ

テキスト篇:p.295「デリゲートとは」から
ゲーム開発演習:矩形や円の描画、文字の描画、画面遷移 など

p.295 デリゲートとは

・メソッドへの参照を保持しておいて、これを用いてメソッドを呼び出せる仕掛け
・つまり、デリゲートを通じてのメソッドの呼び出しが可能
・C/C++における「関数へのポインタ」と同じ考え方を洗練したもの
・イベント処理などで必須のテクニック
・宣言書式: delegate メソッドの戻り値型 デリゲート名(メソッドの引数リスト);
・例:bool answer(int x){…}メソッド用のデリゲートmdならば、delegate bool md(int w) とする
・利用にはデリゲートオブジェクトの生成が必要で、この時に呼び出したいメソッドの指定もできる
・生成書式: デリゲート名 参照変数 = new デリゲート名(メソッド名);
・例:bool answer(int x){…}メソッド用のデリゲートmdならば、md mymd = new md(answer);
・デリゲート経由で指定済のメソッドを呼び出すには、参照変数を別名のように利用できる
・例: bool b = mymd(5); // answer(5)が呼ばれ戻り値が返される

p.297 delegate01.cs

//p.297 delegate01.cs
using System;
delegate void MyDelegate(); //デリゲートの宣言(戻り値無し、引数無し))
class delegate01 {
    public static void show() { //静的メソッド(Mainから直接呼び出せるように)
        Console.WriteLine("呼ばれました");
    }
    public static void Main() {
        //直接showメソッドを呼び出す
        show();
        //デリゲートの作成
        MyDelegate md = new MyDelegate(show); //(同一クラスなので)メソッド名のみ指定でOK
        //デリゲートを通してshowメソッドを実行
        md();
    }
}

p.298(別のクラスにあるメソッドをデリゲート経由で呼び出す)

・別のクラスにあるメソッドもデリゲート経由で呼び出すことができる
・インスタンスメソッドであれば、そのクラスのインスタンスを生成して、用いると良い
・生成書式例: デリゲート名 参照変数 = new デリゲート名(インスタンス名.メソッド名);

p.298 delegate02.cs

//p.298 delegate02.cs
using System;
delegate void MyDelegate(); //デリゲートの宣言(戻り値無し、引数無し))
class MyClass {
    public void show() { //Mainとは異なるクラスにあるメソッド
        Console.WriteLine("呼ばれました");
    }
}
class delegate02 {
    public static void Main() {
        MyClass mc = new MyClass(); //呼び出したいメソッドのあるクラスのインスタンスを生成
        mc.show(); //インスタンス名経由で直接showメソッドを呼び出す
        MyDelegate m = new MyDelegate(mc.show);  //インスタンス名.メソッド名でデリゲートを生成
        m(); //インスタンス名は不要で、メソッドを呼び出せる
    }
}

p.298(別のクラスにある静的メソッドをデリゲート経由で呼び出す)

・別のクラスにある静的メソッドもデリゲート経由で呼び出すことができる
・静的メソッドであれば、そのクラスのインスタンスを生成したくても良い
・生成書式例: デリゲート名 参照変数 = new デリゲート名(クラス名.メソッド名);

p.299 delegate03.cs

//p.299 delegate03.cs
using System;
delegate string MyDelegate(string a, string b); //デリゲートの宣言(戻り値有り、引数有り))
class MyClass {
    public static string show(string s1, string s2) { //Mainとは異なるクラスにある静的メソッド
        return s1 + "は" + s2 + "です";
    }
}
class delegate03 {
    public static void Main() {
        Console.WriteLine(MyClass.show("猫", "ほ乳類")); //クラス名経由で直接showメソッドを呼び出す
        MyDelegate md = new MyDelegate(MyClass.show); //クラス名.メソッド名でデリゲートを生成
        Console.WriteLine(md("C#", "おもしろい")); //クラス名は不要で、メソッドを呼び出せる
    }
}

p.299(デリゲートの動的な変更)

・デリゲートの生成に用いた参照変数を再利用することで、呼び出されるメソッドを動的に変更できる
 ※ ただし、戻り値型と引数リストが同じであること
・これにより、メソッドを呼び出している部分は変更せずに、呼び出されるメソッドを変えてしまうことが可能
・例:
 delegate bool md(int w); //デリゲートの宣言(戻り値有り、引数有り))
 bool myanswer(int x){…}   //静的メソッド①(Mainから直接呼び出せるように)
 bool youranswer(int x){…} //静的メソッド②(Mainから直接呼び出せるように)
 md mymd; //デリゲート用変数を宣言
 mymd = new md(answer); //デリゲートを作成(myanswer用)し、mymdとする
 bool b1 = mymd(5); // myanswer(5)が呼ばれ戻り値が返される
 mymd = new md(youranswer); //デリゲートを作成(youranswer用)し、mymdを上書きする
 bool b2 = mymd(5); // youranswer(5)が呼ばれ戻り値が返される

p.300 delegate04.cs

//p.300 delegate04.cs
using System;
delegate DateTime MyDelegate(DateTime dt, int n); //デリゲートの宣言(戻り値有り、引数有り))
class MyClass1 {
    public DateTime Calc(DateTime d, int n) { //Mainとは異なるクラス①にあるインスタンスメソッド①
        return d.AddDays(n); //dのn日後の日付時刻オブジェクトを生成して返す
    }
}
class Myclass2 {
    public DateTime Calc(DateTime d, int n) { //Mainとは異なるクラス②にあるインスタンスメソッド②
        return d.AddHours(n); //dのn時間後の日付時刻オブジェクトを生成して返す
    }
}
class delegate04 {
    public static void Main() {
        MyClass1 mc1 = new MyClass1(); //インスタンスメソッド①のあるクラスのインスタンスを生成
        Myclass2 mc2 = new Myclass2(); //インスタンスメソッド②のあるクラスのインスタンスを生成
        MyDelegate md = new MyDelegate(mc1.Calc); //インスタンスメソッド①のデリゲートを生成しmdとする
        DateTime dt = DateTime.Now; //現在日付時刻の日付時刻オブジェクトを得る
        DateTime mydate; //受け取り用の変数の宣言
        mydate = md(dt, 100); //インスタンスメソッド①に日付時刻オブジェクトを渡して100日後を得る
        Console.WriteLine("今日から100日後は、{0}です", mydate.ToShortDateString());
        md = new MyDelegate(mc2.Calc); //インスタンスメソッド②のデリゲートを生成しmdを上書きする
        mydate = md(dt, 100); //インスタンスメソッド②に日付時刻オブジェクトを渡して100時間後を得る
        Console.WriteLine("今から100時間後は、{0}です",  mydate);
    }
}

アレンジ演習:p.300 delegate04.cs

・「何日後ですか? 何時間後ですか?(1:何日後 2:何時間後 0:終了)」を入力できるようにしよう
・「1:何日後」が選ばれたら「何日後ですか:」を入力できるようにして、結果を表示しよう
・「2:何時間後」が選ばれたら「何時間後ですか:」を入力できるようにして、結果を表示しよう
・「0:終了」が選ばれるまで何度もくりかえそう

作成例

//アレンジ演習:p.300 delegate04.cs
using System;
delegate DateTime MyDelegate(DateTime dt, int n); //デリゲートの宣言(戻り値有り、引数有り))
class MyClass1 {
    public DateTime Calc(DateTime d, int n) { //Mainとは異なるクラス①にあるインスタンスメソッド①
        return d.AddDays(n); //dのn日後の日付時刻オブジェクトを生成して返す
    }
}
class Myclass2 {
    public DateTime Calc(DateTime d, int n) { //Mainとは異なるクラス②にあるインスタンスメソッド②
        return d.AddHours(n); //dのn時間後の日付時刻オブジェクトを生成して返す
    }
}
class delegate04 {
    public static void Main() {
        MyClass1 mc1 = new MyClass1(); //インスタンスメソッド①のあるクラスのインスタンスを生成
        Myclass2 mc2 = new Myclass2(); //インスタンスメソッド②のあるクラスのインスタンスを生成
        MyDelegate md; //【変更】デリゲート用変数を宣言
        DateTime dt; //【変更】現在日付時刻用の変数の宣言
        DateTime mydate; //受け取り用の変数の宣言
        string ans = ""; //【追加】入力用 
        do { //【追加】繰り返し開始
            Console.Write("何日後ですか? 何時間後ですか?(1:何日後 2:何時間後 0:終了):");  //【追加】
            ans = Console.ReadLine(); //【追加】
            dt = DateTime.Now; //【移動】現在日付時刻の日付時刻オブジェクトを得る
            if (ans == "1") { //【追加】何日後?
                Console.Write("何日後:"); int n = int.Parse(Console.ReadLine()); //【追加】
                md = new MyDelegate(mc1.Calc); //【変更】インスタンスメソッド①のデリゲートを生成しmdとする
                mydate = md(dt, n); //【変更】インスタンスメソッド①に日付時刻オブジェクトを渡してn日後を得る
                Console.WriteLine("今日から{0}日後は、{1}です", n, mydate.ToShortDateString()); //【変更】
            } else if (ans == "2") { //【追加】何時間後?
                Console.Write("何時間後:"); int n = int.Parse(Console.ReadLine()); //【追加】
                md = new MyDelegate(mc2.Calc); //インスタンスメソッド②のデリゲートを生成しmdとする
                mydate = md(dt, n); //【変更】インスタンスメソッド②に日付時刻オブジェクトを渡してn時間後を得る
                Console.WriteLine("今から{0}時間後は、{1}です", n, mydate); //【変更】
            }
        } while(ans != "0"); //【追加】0:終了が選ばれなければ繰返す
    }
}

参考:別解(できるだけ共通化してみよう)

//アレンジ演習:p.300 delegate04.cs
using System;
delegate DateTime MyDelegate(DateTime dt, int n); //デリゲートの宣言(戻り値有り、引数有り))
class MyClass1 {
    public DateTime Calc(DateTime d, int n) { //Mainとは異なるクラス①にあるインスタンスメソッド①
        return d.AddDays(n); //dのn日後の日付時刻オブジェクトを生成して返す
    }
}
class Myclass2 {
    public DateTime Calc(DateTime d, int n) { //Mainとは異なるクラス②にあるインスタンスメソッド②
        return d.AddHours(n); //dのn時間後の日付時刻オブジェクトを生成して返す
    }
}
class delegate04 {
    public static void Main() {
        MyClass1 mc1 = new MyClass1(); //インスタンスメソッド①のあるクラスのインスタンスを生成
        Myclass2 mc2 = new Myclass2(); //インスタンスメソッド②のあるクラスのインスタンスを生成
        MyDelegate md = null; //【変更】デリゲート用変数を宣言
        DateTime dt; //【変更】現在日付時刻用の変数の宣言
        DateTime mydate; //受け取り用の変数の宣言
        string ans = ""; //【追加】入力用 
        string[] dh = {"日", "時間" }; //【追加】表示用
        while(true) { //【追加】繰り返し開始
            Console.Write("何日後ですか? 何時間後ですか?(1:何日後 2:何時間後 0:終了):");  //【追加】
            ans = Console.ReadLine(); //【追加】
            if (ans == "0") break; //【追加】0:終了が選ばれたら抜ける
            dt = DateTime.Now; //【移動】現在日付時刻の日付時刻オブジェクトを得る
            int i = int.Parse(ans) - 1; //【追加】添字にする
            Console.Write("何{0}後:", dh[i]); int n = int.Parse(Console.ReadLine()); //【追加】
            if (i == 0) { //【追加】何日後?
                md = new MyDelegate(mc1.Calc); //【変更】インスタンスメソッド①のデリゲートを生成しmdとする
            } else  { //【追加】何時間後?
                md = new MyDelegate(mc2.Calc); //インスタンスメソッド②のデリゲートを生成しmdとする
            }
            mydate = md(dt, n); //【変更】インスタンスメソッド①か②に日付時刻オブジェクトを渡して得る
            Console.WriteLine("今日から{0}{1}後は、{2}です", n, dh[i], mydate); //【変更】
        }
    }
}

今週の話題

販売本数ランキング 今回トップも「桃太郎電鉄ワールド ~地球は希望でまわってる!~(Switch)」GO!
「イケメンシリーズ」のアエリアが業績予想を下方修正―大規模な再編・改革が必要か?【ゲーム企業の決算を読む】GO!
「実写×Live2D」の映像作品がグランプリ受賞―「Live2D Creative Awards 2023」結果発表 GO!
「Live2D」は海外ユーザーの需要が急増中―AI研究も語られた「alive 2023」基調講演レポート GO!
『ブルーアーカイブ』の世界累計収益4億ドル突破―その75%を日本市場が占める GO!
ゲーム内に「自分の声」を実装できるかも? EAが“プレイヤーの声から生成したボイス”導入技術の特許を申請中 GO!
Epic Games Japan主催の勉強会「EOS / UE5 Deep Dive 2023」秋葉原で開催…一般枠の抽選申込み受付中 12/14・15 GO!

『Dave the Diver』はインディーゲームなのか?ジェフ・キーリー氏が自らの考えを明かす GO!
AI向けに改造されたGeForce RTX 4090の写真が中国で公開。米国による輸出禁止前の駆け込み需要か GO!
今年は乱入対策もばっちりか?2022年開催時不審者騒ぎのあった「The Game Awards」警備を強化予定 GO!

前回のコメント

・consoleクラスのメソッドや型、プロパティなどたくさんあるんだなと同時に
 ややこしいなと感じました笑

 過剰なサービスに見えるかもしれませんが、
 必要になる時が来たら「あら、便利♪」ですので、使いこなしてみてください。

・Titleなど文字を見ただけで「こんな感じに使える」とわかるのですがきちんと
 型なども意識して使っていこうと思いました。

 是非。
 特にクラスオブジェクトを返すメソッドやプロパティをしっかりと。

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

:線の描画(続き)、矩形や円の描画、文字の描画、画面遷移 など

提出フォロー:演習12 対角線を描く

・画面上に左上から右下へ赤色太さ10の線を描く
・画面上に右上から左下へ黄色太さ20の線を描く

作成例

//演習12 対角線を描く
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 red10 = new Pen(Color.Red, 10); //【追加】ペン(赤、10)を生成
    Pen yel20 = new Pen(Color.Yellow, 20); //【追加】ペン(黄、20)を生成
    protected override void OnPaint(PaintEventArgs e) { //Formのメソッドをオーバーライド
        base.OnPaint(e); //まず、Formクラスにおけるメソッドの内容(基本再描画処理)を実行
        e.Graphics.DrawImage(backb, 0, 0); //背景画像を(0,0)から描画
        e.Graphics.DrawLine(red10, 0, 0, backb.Width - 1, backb.Height - 1); //【追加】左上から右下へ赤色10の線を描画
        e.Graphics.DrawLine(yel20, backb.Width - 1, 0, 0, backb.Height - 1); //【追加】左上から右下へ黄色20の線を描画
        int mx = backb.Width / 2, my = backb.Height / 2; //中央座標を得る
        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); //インスタンスを画面に出す
    }
}

テーマ15 Penクラスのプロパティ

・生成したPenオブジェクトは、Colorプロパティで色を変更できる
・また、Widthプロパティで太さを変更できる
・よって、生成済みのPenオブジェクトは使いまわし可能

演習13 対角線を描く・改

・Penオブジェクトを1つのみにしよう

作成例

//演習13 対角線を描く・改
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, 10); //【変更】ペンを生成(初期値は赤、10)
    protected override void OnPaint(PaintEventArgs e) { //Formのメソッドをオーバーライド
        base.OnPaint(e); //まず、Formクラスにおけるメソッドの内容(基本再描画処理)を実行
        e.Graphics.DrawImage(backb, 0, 0); //背景画像を(0,0)から描画
        e.Graphics.DrawLine(mypen, 0, 0, backb.Width - 1, backb.Height - 1); //【変更】左上から右下へ赤色10の線を描画
        mypen.Color = Color.Yellow; //【追加】ペン色を黄色に
        mypen.Width = 20;  //【追加】ペン幅を20に
        e.Graphics.DrawLine(mypen, backb.Width - 1, 0, 0, backb.Height - 1); //【変更】左上から右下へ黄色20の線を描画
        int mx = backb.Width / 2, my = backb.Height / 2; //中央座標を得る
        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); //インスタンスを画面に出す
    }
}

提出:演習13 対角線を描く・改

講義メモ

テキスト篇:p.291「clock01.cs」から
ゲーム開発演習:線の描画(続き)、矩形や円の描画、文字の描画、画面遷移 など

p.292(Consoleクラスの静的プロパティと静的メソッド)

・すでに学習した静的メソッドWrite、WriteLine、ReadLineに加えて便利な静的メソッド、静的プロパティが多数提供されており、
 コンソールを疑似的な実行ウインドウにすることができる
・bool CursorVisible静的プロパティ:falseを設定するとカーソルが見えなくなる。チラつき防止に便利。
・string Title静的プロパティ:コンソール名を設定できる。
・void SetWindowsSize(int,int)静的メソッド:ウィンドウの幅と高さを文字数で指定できる
・ConsoleColor BackgroundColor静的プロパティ:ConsoleColor列挙型で背景色を指定できる。例:ConsoleColor.Yellow
・ConsoleColor ForegroundColor静的プロパティ:ConsoleColor列挙型で文字色を指定できる。
・void Clear()静的メソッド:再描画して設定を反映する(※実行すると文字色・背景色の変更が有効になる)
・void SetCursolPosition(int,int)静的メソッド:カーソルの位置を変える。移動先は文字数。
・bool KeyAvailable静的プロパティ:キーが押されているとtrueになる

p.291 clock01.cs

//p.291 clock01.cs
using System;
class clock01 {
    public static void Main() {
        int oldsecond = 0; //秒が変わった時に書き換えたいので現在の秒を残しておく変数
        Console.CursorVisible = false; //カーソルを消す
        Console.Title = "時計"; //コンソールのタイトルを指定
        Console.SetWindowSize(12, 3); //表示ウィンドウの大きさを指定
        Console.BackgroundColor = ConsoleColor.Yellow; //背景色を黄色に
        Console.ForegroundColor = ConsoleColor.Black; //文字色を黒に
        Console.Clear(); //背景色を反映
        DateTime mt; //日付時刻構造体の生成
        while (true) { //無限ループ
            mt = DateTime.Now; //現在の日付時刻を得る
            if (mt.Second == oldsecond) { //秒が変わっていない?
                continue; //以下をスキップして繰り返しを続行
            } else {
                oldsecond = mt.Second; //秒が変わっているので更新
            }
            Console.SetCursorPosition(2, 1); //カーソル位置(表示開始位置)を変更
            Console.Write("{0:00}:{1:00}:{2:00}", mt.Hour, mt.Minute, mt.Second); //時分秒を表示
            if (Console.KeyAvailable) { //なにかキーが押されていたら
                break; //ループを抜ける
            }
        }
    }
}

アレンジ演習:p.291 clock01.cs

・ストップウォッチにしよう
・実行すると「00:00:00」を表示してカウントを開始するようにしたい
・現在時刻ではなく、時刻を表す1000万分の1秒刻みのカウンタを返すDateTime構造体ののTicksプロパティ(long型)を用いよう
・起動時のTicksを確保しておいて、最新のTicksとの差を用いる
・この差を1000万倍すると秒になるので、さらに3600で割ると時間、60で割って60で割ったあまりで分、60で割った余りで秒が得られる

作成例

//アレンジ演習:p.291 clock01.cs
using System;
class clock01 {
    public static void Main() {
        int oldsecond = 0; //秒が変わった時に書き換えたいので現在の秒を残しておく変数
        Console.CursorVisible = false; //カーソルを消す
        Console.Title = "時計"; //コンソールのタイトルを指定
        Console.SetWindowSize(12, 3); //表示ウィンドウの大きさを指定
        Console.BackgroundColor = ConsoleColor.Yellow; //背景色を黄色に
        Console.ForegroundColor = ConsoleColor.Black; //文字色を黒に
        Console.Clear(); //背景色を反映
        DateTime st = DateTime.Now; //【追加】日付時刻構造体を生成し開始時の時刻を確保
        DateTime mt; //日付時刻構造体の生成
        while (true) { //無限ループ
            mt = DateTime.Now; //現在の日付時刻を得る
            int lap = (int)((mt.Ticks - st.Ticks) / 10000000L); //【追加】経過時間を得て秒単位にする
            int Hour = lap / 3600; //【追加】時を得る 
            int Minute = lap / 60 % 60; //【追加】分を得る 
            int Second = lap % 60; //【追加】秒を得る
            if (Second == oldsecond) { //【変更】秒が変わっていない?
                continue; //以下をスキップして繰り返しを続行
            } else {
                oldsecond = Second; //【変更】秒が変わっているので更新
            }
            Console.SetCursorPosition(2, 1); //カーソル位置(表示開始位置)を変更
            Console.Write("{0:00}:{1:00}:{2:00}", Hour, Minute, Second); //【変更】時分秒を表示
            if (Console.KeyAvailable) { //なにかキーが押されていたら
                break; //ループを抜ける
            }
        }
    }
}

アレンジ演習:p.291 clock01.cs 続き

・ミリ秒までのストップウォッチにしよう
・実行すると「00:00:00.000」を表示してカウントを開始するようにしたい
・起動時のTicksとTicksとの差を1万倍するとミリ秒になるので、さらに3600000で割ると時間、60000で割って60で割った余りで分、
 1000で割って60で割った余りで秒が、1000で割った余りでミリ秒が得られる

作成例

//アレンジ演習:p.291 clock01.cs
using System;
class clock01 {
    public static void Main() {
        int oldmsecond = 0; //【変更】ミリ秒が変わった時に書き換えたいので現在のミリ秒を残しておく変数
        Console.CursorVisible = false; //カーソルを消す
        Console.Title = "時計"; //コンソールのタイトルを指定
        Console.SetWindowSize(16, 3); //【変更】表示ウィンドウの大きさを指定
        Console.BackgroundColor = ConsoleColor.Yellow; //背景色を黄色に
        Console.ForegroundColor = ConsoleColor.Black; //文字色を黒に
        Console.Clear(); //背景色を反映
        DateTime st = DateTime.Now; //日付時刻構造体を生成し開始時の時刻を確保
        DateTime mt; //日付時刻構造体の生成
        while (true) { //無限ループ
            mt = DateTime.Now; //現在の日付時刻を得る
            int lap = (int)((mt.Ticks - st.Ticks) / 10000L); //【変更】経過時間を得てミリ秒単位にする
            int Hour = lap / 3600000; //【変更】時を得る 
            int Minute = lap / 60000 % 60; //【変更】分を得る
            int Second = lap / 1000 % 60; //【変更】秒を得る
            int MSecond = lap % 1000; //【追加】ミリ秒を得る
            if (MSecond == oldmsecond) { //【変更】ミリ秒が変わっていない?
                continue; //以下をスキップして繰り返しを続行
            } else {
                oldmsecond = MSecond; //【変更】ミリ秒が変わっているので更新
            }
            Console.SetCursorPosition(2, 1); //カーソル位置(表示開始位置)を変更
            Console.Write("{0:00}:{1:00}:{2:00}.{3:000}", Hour, Minute, Second, MSecond); //【変更】時分秒ミリ秒を表示
            if (Console.KeyAvailable) { //なにかキーが押されていたら
                break; //ループを抜ける
            }
        }
    }
}

p.294 練習問題 ヒント

・プロパティによる制限なので、クラス内でのエラー表示は不要(するならmain側で)
・偶数は正の整数なので、構造体のデータメンバの型をuint型にすると良い
・偶数しか保持できないようにするには、データメンバへの直接アクセスを禁止するためprivateにする
・このデータメンバを扱うプロパティを定義し、setにおいてvalueの値が偶数かチェックしてから代入する
・getは通常形式で良い

作成例

//p.294 練習問題
using System;
struct MyStruct { //構造体の定義
    private uint x; //偶数しか保持できないようにするために直接利用を禁止
    public uint X { //プロパティ
        get { return x; }
        set { if (value % 2 == 0) x = value; } //偶数なら格納できる
    }
}
class ex11 {
    public static void Main() {
        MyStruct ms = new MyStruct(); //構造体変数の宣言と構造体オブジェクトの生成(new不要)
        Console.Write("正の数:"); uint n = uint.Parse(Console.ReadLine());
        ms.X = n; //プロパティ経由で代入
        Console.WriteLine((n == ms.X) ? "格納できました" : "格納できません"); //プロパティ経由で得て比較
    }
}

今週の話題

販売本数ランキング 今回トップは「桃太郎電鉄ワールド ~地球は希望でまわってる!~(Switch)」GO!
「ハイカジ」へのピボットで業績好調のカヤック、eスポーツ事業は次のステージへ【ゲーム企業の決算を読む】GO!
『UNDERTALE』『ホットライン・マイアミ』のゲームエンジン「Game Maker」非商用利用に限り無償化―1回限りのライセンス購入も可能に GO!
『アクションゲームツクールMV』プレーヤーアプリをオープンソース化―カスタマイズしてゲーム制作の自由度向上へ GO!
マーベラスがインディーゲームクリエイターを支援する「iGi indie Game incubator」の第4期生募集を12月15日より開始―12月19日に説明会も開催 GO!
VR映画「機動戦士ガンダム: 銀灰の幻影」制作決定 フランスの制作会社と共同で GO!

スクエニインサイダー取引事件で有罪判決を受けた社員の知人が490万円の課徴金…社外の人物も不正に取引 GO!
バンダイナムコでまた内部不正 廃棄庫から商品持ち出し、8700万円の利益 8年間気付かず GO!