SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine(コードジン) DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

.NET Remotingによる対戦型のテニスゲームの作成

Visual Studio 2005における.NET Remotingの利用手順

イベントハンドラの作成

 以下の機能を実装します。

  • Form1のコンストラクタ(生成時)の処理
  • Form1のLoadイベント(初期表示時)の処理
  • backgroundWorker1のDoWorkイベント(別スレッドでのサーバ開始)の処理
  • timer1のTickイベント(タイマによる指定間隔ごと)の処理
  • pictureBox1のPaint(画面描画)の処理
  • pictureBox1のMouseMove(マウス移動)の処理
  • button1のClickイベント([Restart]ボタンを押したとき)の処理

Form1のコンストラクタ(生成時)の処理

 ソリューションエクスプローラで「Form1.cs」を右クリックして、[コードの表示]を行ってください。Form1クラスを次のように修正します。

Form1クラス
public partial class Form1 : Form
{
    private const int portno = 8102;
    private const int mgn = 50;
    internal const float barRate = 0.2F;
    internal const float boardRate = 0.3F;
    private Server svr;
    private bool bFirst = true;
    public Form1()
    {
        InitializeComponent();
        SetStyle(ControlStyles.ResizeRedraw, true);
    }
}

 portnoは、TCP/IPで利用するポート番号です。もし、後述の実行がうまくいかない場合、適当な違う値に変更してみてください。

Form1のLoadイベント(初期表示時)の処理

 デザイン画面でForm1のタイトルをダブルクリックしてください。開いたコード画面のイベントハンドラ(Form1_Load)を次のように修正してください。

Form1_Loadメソッド
private void Form1_Load(object sender, EventArgs e)
{
    try
    {
        System.Runtime.Remoting.Channels.ChannelServices.RegisterChannel(
            new System.Runtime.Remoting.Channels.Tcp.TcpChannel(portno),
            false);
        backgroundWorker1.RunWorkerAsync();
        System.Threading.Thread.Sleep(100);
    }
    catch { bFirst = false; }
    svr = (Server)Activator.GetObject(typeof(Server), "tcp://" +
        Properties.Settings.Default.Server + ":" + portno + "/TennisGame");
    if (!bFirst) svr.IsWaiting = false;
}

 tryの中では、サーバオブジェクトの登録とサーバの起動を行っています。既にサーバが起動されていれば、例外が発生し、bFirst = falseとなります。「svr =」では、クライアント側で、サーバオブジェクトのプロクシを生成しています。プロクシの見た目はServer型ですが、実際は異なります。ここの処理には、サーバ側の処理とクライアント側の処理が含まれますが、通常は、サーバ処理とクライアント処理は別のアプリケーションで作成されるべきです。

backgroundWorker1のDoWorkイベント(別スレッドでのサーバ開始)の処理

 デザイン画面でbackgroundWorker1をダブルクリックしてください。開いたコード画面のイベントハンドラ(backgroundWorker1_DoWork)を次のように修正してください。

backgroundWorker1_DoWorkメソッド
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    System.Runtime.Remoting.RemotingConfiguration
        .RegisterWellKnownServiceType(
        typeof(Server), "TennisGame",
        System.Runtime.Remoting.WellKnownObjectMode.Singleton);
}

 BackgroundWorkerにより別スレッドで、サーバの登録処理を行います。ここでは、ボールの位置などの情報を共有するために、シングルトンとしています。

timer1のTickイベント(タイマによる指定間隔ごと)の処理

 デザイン画面でtimer1をダブルクリックしてください。開いたコード画面のイベントハンドラ(timer1_Tick)を次のように修正してください。

timer1_Tickメソッド
private void timer1_Tick(object sender, EventArgs e)
{
    timer1.Stop();
    try
    {
        if (!(button1.Visible = !svr.IsRunning) && bFirst) svr.Do();
        pictureBox1.Refresh();
        timer1.Start();
    }
    catch { }
}

 サーバでボールの位置を移動するメソッドDoを呼んで再描画しています。本アプリケーションでは、ボールの位置などのデータはサーバで管理し、クライアントから定期的にその値を取得して表示するようになっています。呼び出し関係が、一方向になり、プログラムの構造が簡潔になります。

pictureBox1のPaint(画面描画)の処理

 デザイン画面でpictureBox1を選択してください。

 プロパティ画面のカミナリマークをクリックして、Paintプロパティをダブルクリックしてください。

 開いたコード画面のイベントハンドラ(pictureBox1_Paint)を次のように修正してください。

pictureBox1_Paintメソッド
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
    try
    {
        Graphics g = e.Graphics;
        int w = pictureBox1.Width - 2 * mgn,
            h = pictureBox1.Height - 2 * mgn;
        float x = svr.DiscPos.X, y = bFirst ?
            svr.DiscPos.Y : 1 - svr.DiscPos.Y,
            r = boardRate, tx = mgn + r / 2 * w;
        float dx = (1 - r * y) * Server.Size * w;
        float dy = (1 - r * y) * (1 - r * y) * Server.Size * h;
        float z = (1 - (1 - r * y) * (1 - r * y)) / r / (2 - r);
        float cx = mgn + x * w - r * z * w * (x - 0.5F);
        float cy = pictureBox1.Height - mgn - h * z;
        float x1 = mgn + svr.BarPos(bFirst) * w;
        float x2 = mgn + svr.BarPos(!bFirst) * (1 - r) * w + w * r / 2;
        float bar1 = w * barRate;
        float bar2 = w * barRate * (1 - r);
        g.FillPolygon(Brushes.Green, new PointF[]
            {
                new PointF(tx, mgn),
                new PointF(pictureBox1.Width - tx, mgn),
                new PointF(pictureBox1.Width - mgn,
                           pictureBox1.Height - mgn),
                new PointF(mgn, pictureBox1.Height - mgn),
            });
        g.DrawLine(new Pen(bFirst ? Color.Red : Color.Yellow, 6),
            x1, pictureBox1.Height - mgn, x1 + bar1,
            pictureBox1.Height - mgn);
        g.DrawLine(new Pen(bFirst ? Color.Yellow : Color.Red, 3),
            x2, mgn, x2 + bar2, mgn);
        g.FillEllipse(Brushes.White, cx - dx, cy - dy, dx * 2, dy * 2);
        g.DrawEllipse(Pens.Black, cx - dx, cy - dy, dx * 2, dy * 2);
        Font fnt = new Font(Font.FontFamily, 16);
        g.DrawString(string.Format("({0}-{1})",
            svr.Score(bFirst), svr.Score(!bFirst)),
            fnt, Brushes.LightBlue, 10, 10);
        if (!svr.IsRunning)
            g.DrawString(svr.Score(bFirst) == Server.WinPoint ?
                "You win !" : "You loss",
                fnt, Brushes.LightBlue, Width / 2 - 40, Height / 2);
    }
    catch { }
}

 ここでは、ボールやバーなどを3次元的に描画しています。ボールの位置やバーの位置は、サーバで管理しています。従って、別のPCであっても(上下は逆になりますが)同じ情報を表示することができます。

pictureBox1のMouseMove(マウス移動)の処理

 デザイン画面でpictureBox1を選択してください。

 プロパティ画面のカミナリマークをクリックして、MouseMoveプロパティをダブルクリックしてください。

 開いたコード画面のイベントハンドラ(pictureBox1_MouseMove)を次のように修正してください。

pictureBox1_MouseMoveメソッド
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
    if (Math.Abs(e.Y - pictureBox1.Height + mgn) > 30) return;
    float x = e.X - mgn - (svr.BarPos(bFirst) + barRate / 2)
        * (pictureBox1.Width - 2 * mgn);
    if (x < -10) svr.Move(bFirst, true);
    else if (x > 10) svr.Move(bFirst, false);
}

 マウス操作で、バーを移動できるようにしています。

button1のClickイベント(「Restart」ボタンを押したとき)の処理

 デザイン画面でbutton1をダブルクリックしてください。開いたコード画面のイベントハンドラ(button1_Click)を次のように修正してください。

button1_Clickメソッド
private void button1_Click(object sender, EventArgs e)
{
    try
    {
        svr.Reset();
        button1.Visible = false;
    }
    catch { }
}

 ゲームの再実行ができます。

実行

  1. [ビルド]メニューの[ソリューションのビルド]を行います。
  2. コンパイルでエラーが出るときは、上記の内容と同じかどうか確認してみましょう。
  3. 実行してみましょう。[デバッグ]メニューの[デバッグなしで開始]を選んで実行してください。
  4. ファイアウォールを実行していると、ブロックするかどうか聞かれるので、ブロックを解除してください。
  5. 画面を動かして、もう1つ起動させましょう。ゲームが開始されます。マウスで操作してください。3点先取すると勝ちです。
  6. TennisGame.exe.configの「localhost」をコンピュータ名もしくはIPアドレスにすれば、ネットワーク上で対戦もできます。

まとめ

 対戦型テニスゲームの作成を通して、.NET Remotingの利用方法を見ました。

参考サイト

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
特集記事連載記事一覧

もっと読む

この記事の著者

斉藤 努(サイトウ ツトム)

C#プログラマ

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/530 2007/04/18 09:07

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー