テトリス

コピーしてやってみた

前の記事(Singleton的にやってみた)で、Singleton的にクラスを作ったが、別にクラス1つにつき1つのインスタンスに制限しなくてもいいし、1つに制限すると逆に面倒くさいことになりそう。
重要なのは生成時に毎回ミノを構成するブロックを作成処理が動かないことだ。
ってことで今度はコピーする方法でやってみる。

□Minoクラス(抽象クラス)
・IDプロパティ(抽象)
・ミノを構成するブロック情報変数
・「ミノを構成するブロック情報」を生成するメソッド(抽象)
・位置情報
・方向番号
・落下とか回転とか移動の処理
・コンストラクタ
「ミノを構成するブロック情報」を生成するメソッドを呼んで、
それをミノを構成するブロック情報変数に代入する
・浅いコピー処理(シャローコピー)
Minoクラスの浅いコピー処理を実行する。

↓(継承)

□Mino0~Mino6(棒ミノや四角ミノの種類ごとに1つ)
・IDプロパティの実装
棒ミノを表すmino0クラスなら、return 0;を返す 等

・「ミノを構成するブロック情報」を生成するメソッドの実装
棒ミノを表すmino0クラスなら、
方向0・・・ (-1,0 0,0 1,0 2,0)
方向1・・・ (0,-1 0,0 0,1 0,2)
方向2・・・ (-2,0 -1,0 0,0 1,0)
方向3・・・ (0,-2 0,-1 0,0 0,1)
といったようなデータを返す


□TetrominoGeneratorRandomクラス
・Mino0~Mino6までのインスタンスを保持しておく変数

・初期化
Mino0~Mino6までのインスタンスを生成する

・GetNewMino()
ランダムにMino0~Mino6までのインスタンスの浅いコピー(シャローコピー)を返す。



Minoとそれを継承したMino0~Mino6は、前の記事(XNAテトリスで悩んでること)とほぼ同じだ。
ブロックの保持とミノの回転・移動・落下などの各種処理を行っている。
コンストラクタでブロックを生成するメソッドを呼び出して、実際の生成は継承したクラスのメソッドが行っている。

前と違うところは浅いコピー(シャローコピー)を行うメソッドを追加したところだ。
インスタンスのコピーとは、インスタンスを新しく生成して、フィールドの値を複製することだ。
その手法に、浅いコピー(シャローコピー)と深いコピー(ディープコピー)がある。

浅いコピーは、値型のフィールドはそのままコピーされるが、参照型のフィールドは参照だけがコピーされる。つまり中身のコピーを行わない。これは、コピー先でも同じ参照型のインスタンスを使うわけだ。

例えば、Aというクラスの中にBというクラスの参照型のフィールドがあったとする。
AのインスタンスA'の中にBのインスタンスB’があるとき、
インスタンスA'の浅いコピーを行うと、新しくインスタンスA''ができるが、その中のBのインスタンスはA'の中にあったB'の参照がそのままコピーされる。B''が新しく生成されてコピーされるわけではない。
A''の中のB'をいろいろいじると、A'のなかのB’も変わってしまうわけだ。同じものだから。

深いコピーは、値型のフィールドはそのままコピーされ、参照型のフィールドもインスタンスが新しく生成されて、中身がコピーされる。
先ほどの浅いコピーの例で言えば、A''の中に新しくB’のコピーB''が生成される。
A''の中のB''をいじっても、A’の中のB'は変更されない。


浅いコピーはTetrominoGeneratorRandomクラスで利用している。
このクラスは、GetNewMinoメソッドを呼び出すとランダムでMino0~Mino6までのインスタンスを返してくれる生成クラスだ。

このクラスでは、初期化時にあらかじめMino0~Mino6までのインスタンスを生成して保持している。
Mino0~Mino6は生成時にミノを構成するブロックの情報も作成する。
GetNewMinoメソッドでは、この保持されたMino0~Mino6のインスタンスの浅いコピーを返している。

なぜこのようなことをするかというと、ミノを構成するブロック情報の生成をやりたくないからだ。
浅いコピーなら参照型のフィールドは参照だけがコピーされる。
ミノを構成するブロック情報は参照型なので、この参照がコピーされるわけだ。

つまり最初にMino0~Mino6を生成したときにだけブロック情報が構築され、あとはコピーコピーでやっていけるので、コストが安いというわけ。
ついでに参照だけコピーなのでメモリ消費も少なくてすむ。

浅いコピー処理を作ったのにはもう1つ理由がある。
テトリスの落下地点を表示するガイドミノの表示のためだ。
ガイドミノの位置は、現在落ちているミノの落下地点にあるので、Minoクラスの落下処理がそのまま使える。
現在落ちているミノを落下させるわけにはいかないので、コピーしたものを落下させて、それを表示させている。


コードを簡単に張っておく。
 TetrominoGeneratorRandomクラス

       private Mino[] MinoInstances = 
        {
            new Mino0(),
            new Mino1(),
            new Mino2(),
            new Mino3(),
            new Mino4(),
            new Mino5(),
            new Mino6(),
        };
  
      public Mino GetNewMino()
        {   //ランダム
            int minoNumber = randomForTetromino.Next(MinoInstances.Length);
     // 浅いコピーしたものを返す
            Mino newMino = MinoInstances[minoNumber].ShallowCopy();

            return newMino;
        }

Minoクラス
        public abstract int ID { get; }
        public IList<IList<MinoBlock>> MinoBlocks { get; private set; }

        private int _minoAngleNumber = 0;
        public int MinoAngleNumber
        {
            get
            {
                return _minoAngleNumber;
            }
            private set
            {
                _minoAngleNumber = value;

                if (_minoAngleNumber < 0)
                {
                    _minoAngleNumber = MinoBlocks.Count - 1;
                }
                else
                {
                    _minoAngleNumber %= MinoBlocks.Count;
                }
            }
        }

        private Point _location;
        public Point Location
        {
            get
            {
                return _location;
            }
            set
            {
                _location = value;
            }
        }
        
        public TetrisField TetrisField { get; set; }

        private float fallSpeedSum = 0;
        public float FallSpeed { get; set; }

        public bool Finished { get; private set; }

        public Mino()
        {
            MinoBlocks = CreateMinoBlocks();
        }

       // ミノを構成するブロック情報の保持変数
        protected abstract IList<IList<MinoBlock>> CreateMinoBlocks();
        public IList<MinoBlock> CurrentMinoBlock
        {
            get
            {
                if (MinoBlocks.Count >= 1)
                {
                    return MinoBlocks[_minoAngleNumber];
                }
                else
                {
                    return null;
                }
            }
        }
   // 浅いコピー
        public Mino ShallowCopy()
        {
            return (Mino)MemberwiseClone();
        }

        // 以下回転とか移動とか

Mino0クラス

        public override int ID
        {
            get { return 0; }
        }
        protected override IList<IList<MinoBlock>> CreateMinoBlocks()
        {
            IList<IList<MinoBlock>> minoBlocks = new List<IList<MinoBlock>> 
            { 
                new List<MinoBlock> 
                { 
                    new MinoBlock(new Point(0, 0), 0),
                    new MinoBlock(new Point(-1, 0), 0),
                    new MinoBlock(new Point(1, 0), 0),
                    new MinoBlock(new Point(2, 0), 0)
                },
                new List<MinoBlock> 
                { 
                    new MinoBlock(new Point(0, 0), 0),
                    new MinoBlock(new Point(0, -1), 0),
                    new MinoBlock(new Point(0, 1), 0),
                    new MinoBlock(new Point(0, 2), 0)
                },
                new List<MinoBlock> 
                { 
                    new MinoBlock(new Point(0, 0), 0),
                    new MinoBlock(new Point(-1, 0), 0),
                    new MinoBlock(new Point(-2, 0), 0),
                    new MinoBlock(new Point(1, 0), 0)
                },
                new List<MinoBlock> 
                { 
                    new MinoBlock(new Point(0, 0), 0),
                    new MinoBlock(new Point(0, -1), 0),
                    new MinoBlock(new Point(0, -2), 0),
                    new MinoBlock(new Point(0, 1), 0) 
                },
            };

            return minoBlocks;
        }


以上。

Singleton的にやってみた

前回の記事(XNAテトリスで悩んでること)の解決策をSingleton的にやってみた。
いや、~~的であってパターンそのものじゃないと思うので、ご容赦願いたい。

Minoクラス
・IMinoInfoインターフェースを継承したクラスをセットする変
・ミノの位置
・ミノの回転方向
・ミノの回転・移動・落下などの処理
IMinoInfo.MinoBlocksプロパティを参照して処理を行う


IMinoInfoインターフェース
・IDプロパティ
ミノの種類ごとの番号
・MinoBlocksプロパティ
ミノを構成するブロック情報が入っているリスト

↓(継承)

Mino0クラス~Mino6クラス
・IDプロパティ
Mino0なら return 0; のように返す
・_minoBlocks
ミノを構成するブロック情報が入っているはず
・MinoBlocksプロパティ
_miniBlockの参照を返す

Mino0クラス~Mino6クラスはSingletonっぽいのを使って、それぞれのクラスが1つのインスタンスしか生成されないようにしている。

このようにMinoは、ミノの位置、方向、それぞれの処理を担当して、テトリミノ自体はIMinoInfoインターフェースを継承したインスタンスが1つしか生成できないクラスで表現している。
Minoクラス生成時にIMinoInfoをセットすることで、色々なミノに対する処理ができる。

ガイドミノ表示の際は、新しいMinoクラスを生成して、
落ちているMinoクラスのIMinoInfoをセットして、処理させればいけそうな。

コードにするとこんな感じ。

続きを読む »

XNAテトリスで悩んでること

今、XNA(マイクロソフト製のゲームフレームワーク)でテトリスを作っている。
で、テトリミノの表現と関連処理の実装のために次のようなクラスを作っている。

Minoクラス
・IDプロパティ(抽象)
・ミノを構成するブロック情報
・「ミノを構成するブロック情報」を生成するメソッド(抽象)
・位置情報
・方向番号
・落下とか回転とか移動の処理
・コンストラクタ
↑の「ミノを構成するブロック情報」を生成するメソッドを呼んで、
それをミノを構成するブロック情報に代入する

↓(継承)

Mino0クラス~Mino6クラス(棒ミノや四角ミノの種類ごとに1つ)

・IDプロパティの実装
棒ミノを表すmino0クラスなら、return 0;を返す 等

・「ミノを構成するブロック情報」を生成するメソッドの実装
棒ミノを表すmino0クラスなら、
方向0・・・ (-1,0 0,0 1,0 2,0)
方向1・・・ (0,-1 0,0 0,1 0,2)
方向2・・・ (-2,0 -1,0 0,0 1,0)
方向3・・・ (0,-2 0,-1 0,0 0,1)
といったようなデータを返す


といったような感じです。(自分だけ分かればいいやー(テキトー
落下ミノとしてmino0~mino6をランダムに生成して、それを落下させています。

で、ここでミノが落下する地点を半透明で表示する「落下ガイド」を実装したくなったので、
すでにあるこのminoクラスの落下処理を利用したいと思いました。

落下しているminoクラスをコピーして落下ガイド用minoクラスを作ればいいのかな?
と思ったんだけど、それでいいのか?と悩んでるところ。

なぜか。
「ミノを構成するブロック情報」を回転や移動させるたびにコピーする必要がある。
参照だけコピーしとけば軽いと思うけど、それならそもそも落下するミノを生成する時点での
「ミノを構成するブロック情報」生成処理もどっかから参照してくればいいんじゃね?
とか設計ダメジャンと考えてるなう。

(そもそもコンストラクタでブロック情報生成のメソッド呼んでるから、
 今のままじゃコピーといっても毎回生成しちゃうんじゃ・・)

そんな今日この頃。
分かりやすく書いてなくてすいません。頭の中がそのまま吐き出されてます。

とりあえず悩んでることだけでもまとめといたほうがいいと思って。
たぶん、これからの記事もそうなっていくかと・・。
記事検索
livedoor プロフィール
QRコード
QRコード

トップに戻る