透明コントロール

もう4年半ぐらい、ゲームプログラミングしてません。
そろそろ、復帰しようかなと思っています。
 
というわけで、使ってなかったはてなダイアリーのスペースを有効活用しようと思います。
書いてる内容は、その場で考えての行き当たりバッタリなので、結構いい加減かもしれません。

D言語の愚痴

D言語でのゲームプログラミングも考えていましたが、yaneSDK4Dを今のD言語コンパイラコンパイルするとエラーが山ほど出ます。
ほとんどがキャストエラーなので、「 *(ushort*)&data[i] 」みたいなのを「 *cast(ushort*)&data[i] 」とするだけで良い箇所ばかりですけど……「 *cast(ushort*)&data[i] 」って、なんかあまりにも見た目汚いですよね。「 *(ushort*)&data[i] 」の方がまだキレイだ。
キャストしてるのに、キャストしてるように見えないじゃん!? ushort*って引数でcastって関数を呼び出して、その戻り値の挿す先の値とdata[i]とでビット積取ってる様にも見える。マジ、おかしいよ、D言語!! 「 *cast(ushort*, &data[i]) 」の方が、まだ見やすいよ!!
……と、D言語はもぉ見るのがイヤになりました。
 
そもそもキャストなんて、極力使わないようにプログラミングすべきなのでしょうけれども……でも僕らC/C++ネイティブプログラマにはキャストは非常に当たり前の構文なので……すごくがっかりしました。
 
というわけで、yaneSDK.NETが開発されているC#でゲームを作ってみることにしました。
 
ていうか、C#使うなら、別にyaneSDK使わなくても作れるよ。
(複雑なゲームは作らないしね)
 
……というわけで、.NET Frameworkのみをメインに使ってのゲームプログラミングをちょっとやってみることにしました。

透明コントロール

…………透明コントロール、無いじゃん!?
致命的すぎます、.NET Framework! 透明コントロールなしでゲーム書こうと思ったら、コントロールなしでゲーム書かなきゃいけないようなものです!!
でも、GUIでコントロール配置したりできるあの環境は捨てがたい。
 
そこで、擬似透明コントロールを作ることにしました。
UserControlの継承で作りますが…とりあえず、次のようなコードを埋め込みます。

        protected Point MyPoint(int x, int y)
        {
            if (Visible)
            {
                return new Point(x, y);
            }
            else
            {
                return new Point(x + this.Left, y + this.Top);
            }
        }
        protected Rectangle MyRect(int x, int y, int width, int height)
        {
            if (Visible)
            {
                return new Rectangle(x, y, width, height);
            }
            else
            {
                return new Rectangle(x + this.Left, y + this.Top, width, height);
            }
        }
        private void MyTransparentOnMouseDown(object sender, MouseEventArgs e)
        {
            this.OnMouseDown(new MouseEventArgs(e.Button, e.Clicks, e.X - this.Left, e.Y - this.Top, e.Delta));
        }
        private void MyTransparentOnMouseUp(object sender, MouseEventArgs e)
        {
            this.OnMouseUp(new MouseEventArgs(e.Button, e.Clicks, e.X - this.Left, e.Y - this.Top, e.Delta));
        }
        private void MyTransparentOnMouseClick(object sender, MouseEventArgs e)
        {
            this.OnMouseClick(new MouseEventArgs(e.Button, e.Clicks, e.X - this.Left, e.Y - this.Top, e.Delta));
        }
        private void MyTransparentOnMouseMove(object sender, MouseEventArgs e)
        {
            this.OnMouseMove(new MouseEventArgs(e.Button, e.Clicks, e.X - this.Left, e.Y - this.Top, e.Delta));
        }
        public void MyTransparentRegist(UserControl uc)
        {
            this.Visible = false;
            uc.Paint += new PaintEventHandler(MyTransparentOnPaint);
            uc.MouseDown += new MouseEventHandler(MyTransparentOnMouseDown);
            uc.MouseUp += new MouseEventHandler(MyTransparentOnMouseUp);
            uc.MouseClick += new MouseEventHandler(MyTransparentOnMouseClick);
            uc.MouseMove += new MouseEventHandler(MyTransparentOnMouseMove);
        }
        private void MyTransparentOnPaint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            g.DrawEllipse(Pens.Red, MyRect(0, 0, Width, Height));
        }

自分自身はinvisibleにして、親コントロールのイベントに寄生します。
座標はMyPointやMyRectを使うことで、自動的に計算してもらいます。
なんか無駄な処理が多い気がしますが、いまどきのコンピュータは速いので問題ないのではないでしょうか。
 
マウス入力処理周辺は、親コントロールに発生した入力処理が全ての子コントロールに飛ばされます。
それぞれの子コントロールは、自分の受け持ち範囲内であるかどうかをチェックしてから、入力処理を行うべきでしょう。
(受け持ち範囲は、矩形とは限らないので、ちゃんと自分自身で範囲チェックするのがよさそうです)
 
これで、親コントロールの方で自在にコントロール配置が行えます。
背景は、親コントロールのBackgroundImageを変更するだけで楽々です。
子コントロールはWidthやHeightの値を参考に、伸び縮み自由なコントロールとしても書けます。
 
一つ問題点は、子コントロールは、更に子コントロールを持つのがスムーズでないということですね。
まぁ、MyTransparentRegistの中で子コントロールのMyTransparentRegistを呼び出してやれば、子コントロールが更に子コントロールを持つことも可能ではありますか。(このとき、子コントロールのxとyを補正する必要あり)

シーン管理

シーン管理もコントロールでやっちゃいます。
というか、シーン管理はコントロールでやるべきです。
階層構造でのシーン管理もやりやすいし、RPGのおしゃべり窓のようなサブシーンとかも、できなければならないはずです。
 
この時、サブシーンは親シーンの入力は奪わなければならなくて、しかし描画処理は、親シーンのも呼び出し続ける必要があります。
 
えー、つまり、どういうことよ?
 
親シーンの描画の上に自らの書込みをしなきゃいけないということは、サブシーンは透明コントロール的に実装する必要があります。
上まで書いた行き当たりバッタリの設計だと、各透明コントロールは親のマウス入力を全て受け取って処理するので、サブシーンの割り込みが入った時だけはマウス入力を遮断する必要がある、ということです。
また、メインシーンは単独コントロールでよいのですが、サブシーンはメインシーンに所有される形で書く必要があるのかもしれません。
ともかく、マウス入力周辺を、単なるデリゲート乗っ取りだけじゃなく、なんかテキトーに、まともな処理を書く必要がありそうです。
(なんか、お手軽じゃなくなってきたな…今回、サブシーンを使うようなゲームは作らないし、この辺はまた次のゲームを組むときの課題ということにしておこうかね)