ゲーム

スクウェア・エニックスからのメールにお気をつけて【フィッシング詐欺】

久しぶりの更新です。こんにちは。

最近こんなメールが入ってきたんです。

フィッシング詐欺メール001










Gメールの迷惑メールに振り分けられてました。
一目見ただけで、最近流行りの「フィッシング詐欺か!」と思いましたが、
どうもフィッシング詐欺にしては様子がおかしい。

文面に書かれている
https://secure.square-enix.com/account/app/svc/Login?cont=account
を見るとどうみても公式のアドレスです。

メールの送信者(From:)も一応確認してみると、
autoinfo_jp@account.square-enix.com
と、こちらも公式のドメインの様子。
(ただし、From:は改ざん可能なのであまり意味は無いけど)

公式のアドレスでフィッシング送っても意味あるのか?
それとも公式の正式なメールなんだろうか?登録したほうがいいのか?
などとちょっと不安になってので↓の公式サイトを見てみました。

スクウェア・エニックスアカウント(http://www.jp.square-enix.com/account/)

(Google検索から行った所ですのでアクセスしても大丈夫です)

ありました。一番上に

【重要】フィッシング詐欺サイトへ誘導するメールやメッセージにご注意ください

というお知らせが。

内容を軽く見ていくと・・・

【不審なサイトの見分け方】

リンク先が表示とは別のものになっていないかをご確認ください。
ブラウザやメーラー上で、マウスカーソルを該当のURLにあわせて画面上に表示されるリンク先を確認するなどの方法で、リンク先が別のものになっていないかをご確認ください。

という部分。これですね。

そういえば、Gメールの迷惑メールに振り分けられると、リンクが消えるんでした。
というわけで一時的に迷惑メールの指定を解除してみます。

フィッシング詐欺メール002










はい。確かにアドレスがリンクになってます。
確認してみると、表示は

https://secure.square-enix.com/account/app/svc/Login?cont=account

という公式のアドレスのものですが、リンク先を見てみると・・

http://secure.square-enix.com.a.dqxfi.com/account/app/svc/Login.htm?cont=account

となっていました(アクセスしてはいけません)。

こうなればだれでもわかる間違い探しです。
.a.dqxfi.comなどという訳のわからないアドレスが入っています。

アクセスして何か入力すると漏れ無く情報が盗み取られるわけですね。
これがフィッシング詐欺の手口。
今更ながら勉強になりました。
これが銀行とかだったら怖いですねぇ。

では、今後怪しいメールが来たらどうするか?
やっぱり鉄則として、「無視をする」「アドレスにはアクセスしない」というものでしょう。
お使いのメールサービスで迷惑メールに振り分けられてたら単純に気にしないで無視しましょう。


でもやっぱり私みたいに「ログインしないとまずいんじゃないの?」と不安になってしまった方。
一度、「検索」をしてから公式ページに行ってログインしましょう。
それならばフィッシング詐欺の余地がありません。

というわけでフィッシング詐欺の勉強になった話題でした。
(久しぶりのブログ過ぎて口調などがよくわからなくなってしまいました)

あとフィッシング詐欺についてのサイトをいくつかおいておきます。
フィッシング110番 :警視庁

5分で絶対に分かるフィッシング詐欺 - @IT
Wikipedia:フィッシング (詐欺)

お読みいただきありがとうございました。

友人のブログへのリンクを追加しました!

友人である「鼻毛の枝毛」さんのブログ、【明日はきっと雨】さんへのリンクを右側サイドバーに追加しました。
写真、カメラ、ゲームなどの記事がメインに書かれております。
最近、登山に行って写真をいっぱい撮ってるみたいですね。
興味がある方は行ってみてはいかがでしょうか。

オープンソースでフリーなFPS・Xonoticをコンパイルする on Windows

FPSはFirst Person shooterの略で、一人称視点でプレイするシューティングゲームのことです。
普通はパソコン屋さんとか、ゲーム屋さんとか、ダウンロードサイトなどで購入しますが、
購入いらずで無料でプレイできるのが、Xonoticです。
読み方は・・ゾノティック・・かな?

気になった方は下記サイトを一度ご覧ください。
Xonotic公式サイト

日本語Wikiもあります。
Xonotic Japan Wiki

さらにこのゲーム、オープンソースという形態で開発されています。
ソースが公開されており、それを自由に改良して遊ぶことができます。
また同じくソースを公開することで、再配布してみんなに遊んでもらうことが可能です。

ゲームの内容につきましてはLento氏(@Lento_Qwerty)のつぶやきにお願いすることにして・・(おい)、
ソースをいじれるということに興味が出てきたので、これを機会に導入してみることにしました。

導入方法は以下のページに書いてあります。
http://dev.xonotic.org/projects/xonotic/wiki/Repository_Access

しかし、私はWindowsXP。
一応、以下のページにWindowsでの導入方法が書いてあります。
http://dev.xonotic.org/projects/xonotic/wiki/Compiling_in_Windows

が、いきなりTHIS IS OUTDATED(古い情報です)なんて書いてあります。
ので、この記事でWindowsでの導入方法をまとめてみようと思います。

なお、Xonoticではバージョン管理ソフトとしてGitを使用しておりますが、
Gitの使い方については別に解説サイトなどをご覧ください。


1.Git for Windowsの導入

まずはGitを導入します。
GitはLinux界隈で使われているバージョン管理ソフトですが、
以下のサイトからWindows用のGitシステム「Git for Windows」が入手できます。
Git for windows

右側のDownloadリンクから、最新のインストーラーをダウンロードしてください。
この記事を書いている段階での最新バージョンは、
Git-1.7.10-preview20120409.exe
です。

これをインストールしてください。
この記事では「C:\Program Files\Git」にインストールしたものとして解説をしていきます。


2.MinGW/MSYSの導入

MinGWとMSYSは、Windowsにおいて、Linuxで開発するのと同じような感覚で、
Windowsアプリケーションを開発するためのソフトウェアです。

詳しく言うと、
MinGWは開発のためのツール群(gccといったコンパイラーなど)、
MSYSはLinuxのコマンド関係のツール群(bashといったシェルなど)
がWindows上で動作するようになっています。

MinGWとMSYSは前は別に入手するようになっていたようですが、
今はまとめて入手できます。

まずは以下のサイトに行ってください。
http://www.mingw.org/

この記事を書いた時点では、
左側の下の方にある「Download」リンクを辿り、
「Installer」→「mingw-get-inst」→「mingw-get-inst-20120426」→「mingw-get-inst-20120426.exe
の手順でインストーラーを入手を入手します。

そのままインストールしてください。
途中「Select Components」という画面でインストールするコンポーネントを選択する画面が出てきます。

ここでは
C Compiler」「MSYS basic sytem」「MinGW Developer Toolkit
に必ずチェックを入れてください。

MSYS basic system」にチェックを入れないとコマンドが打てません。
MinGW Developer Toolkit」にチェックを入れないとこの後に出てくるpatchコマンドが実行できません。

この記事では、MinGWを「C:\MinGW」にインストールしたものとして話を進めます。
MSYSは「C:\MinGW\msys\1.0」にインストールされます。


3.Xonpatchの入手

ただし、Xonpatchそのものは使用しません。
以下のXonoticのWindowsへの導入解説ページに書いてあるXonpatchを入手します。
http://dev.xonotic.org/projects/xonotic/wiki/Compiling_in_Windows

探すのが面倒だと思うので以下の直リンクを置いておきます。
xonpatch_1-2.zip

このファイルを解凍して、それらのファイルをどこかに置きます。
この記事では、MSYSでの私のホームディレクトリである「C:\MinGW\msys\1.0\home\deltan」
に、「xonpatch_1-2」ディレクトリを置いて、そのディレクトリ以下に
解凍してできたファイルを置いたとして解説します。

「xonpatch_1-2」には、
「xonpatch.bat」ファイルと「xonpatch」ディレクトリが入っていることになります。


4.環境設定

Windowsのプログラムメニューに「MinGW」というフォルダができていて、
その中に「MinGW Shell」というプログラムがあるはずですので、
クリックするとコマンドが打てる黒い画面のプログラム(シェル)が起動します。
これがLinuxような環境ということになります。

ここからは基本的にこの黒い画面で作業していきます。

まずは以下のコマンドを入力し、先程ダウンロードしてきた
Xonpatchが入っているディレクトリに移動します。
cd xonpatch_1-2/
Windows上ではXonpatchを「C:\MinGW\msys\1.0\home\deltan」に入れましたが、
MinGWではいきなりこのディレクトリ(ホームディレクトリ)を見ている、
と考えると良いでしょう。

次に以下のコマンドを入力します。
patch -V simple -z "~x" -bp0 -i "xonpatch/profile.patch"
このコマンドを実行すると、「/etc/profile」のファイルが書き換わります。
Windows上では、「C:\MinGW\msys\1.0\etc\profile」のファイルです。

次に「/etc/fstab」のファイルを開き次の1行を追加します。
C:\PROGRA~1\Git   /git
/etc/fstab」は、Windows上では「C:\MinGW\msys\1.0\etc\fstab」にあります。
この設定は、Gitが実際に入っているWindows上のパス「C:\Program Files\Git」を
MinGWでは「/git」としてアクセスできるように対応させるものです。

MinGW Shellを再起動してください。
以下のコマンドを打つと
Gitディレクトリの中に移動できることが分かるはずです。
cd /git


5.足りないツールの導入

この段階で、MinGWwgetunzipというソフトが足りません。
ですので導入します。
といっても改めてダウンロードなどをする必要はありません。
以下のコマンドを打って導入します。
mingw-get install msys-wget msys-unzip
これだけ打ってあとは待っているだけで導入が完了します。

ここでエラーが発生した場合は以下のコマンドを打ってから、
再度上のコマンドを打てば直るかもしれません。
mingw-get update

※ここでMEMO:
MinGWでは、今使ったmingw-getを使うことで、
手動でダウンロードしたり、解凍したり、コンパイルしたり、といった手間なく、
様々なプログラムを導入できるようです。

これはLinuxにある「apt-get」のようなものみたいですね。
以下のコマンドがあるようなのでためしてみると良いでしょう。
mingw-get update
mingw-get upgrade
mingw-get install ソフト名?


6.Xonoticの入手。

ここからは以下のサイトに書いてある通りに実行することになります。
http://dev.xonotic.org/projects/xonotic/wiki/Repository_Access

以下のコマンドを、好きなディレクトリに移動して入力します。
git clone git://git.xonotic.org/xonotic/xonotic.git
私はこのコマンドを「Git bash」で打ちました。
Git bash」は、Windowsのエクスプローラーにおいて、
好きなフォルダで右クリックして開くことができるGit用のシェルです。

もちろん今開いている「MinGW Shell」で以下のようにコマンドを打っても良いと思います。
cd /Y/Projects/Xonotic/develop/
git clone git://git.xonotic.org/xonotic/xonotic.git
このコマンドを打つと、
コマンドを打ったディレクトリに「xonotic」ディレクトリというのが作成され、
Xonoticのリポジトリ(ネット上にあるXonoticのデータが入ったサーバー)から、
Xonoticのデータがダウンロードされます。

しかしまだすべてのデータはダウンロード出来ていません。
次に以下のコマンドを入力します。
cd xonotic
./all update -l best
すると、Xonoticの全てのリポジトリからデータがダウンロードされてきます。
データが多く、サーバーが弱いのでダウンロードに時間がかかりますのでしばらくお待ち下さい。


7.Xonoticのコンパイル。

Xonoticの入手Git bashを使っていた方も、ここからはまたMinGW Shellを起動してください。

この記事では「Y:\Projects\Xonotic\develop\xonotic」にXonoticをダウンロードさせたので、
次のコマンドを入力して移動してから、Xonoticのコンパイルを行います。
cd /Y/Projects/Xonotic/develop/xonotic
./all compile
Windowsの「Y:\Projects\...」という絶対パスは、MinGW上においては
/Y/Projects/...」といったように「/ドライブ名/以下ディレクトリ構造」という指定が必要になります。

コンパイル完了まで多少時間がかかりますのでお待ち下さい。


8.Xonoticの実行

ここまでの手順が問題なく進めば、コンパイルがエラーなく完了するはずです。
最後に次のコマンドを入力すればXonoticが動作します。
./all run

9.Xonoticの更新

以下のコマンドを入力すれば最新の更新がダウンロードされてきます。
./all update

10.Xonoticの開発にあたり

Xonoticの開発には、Gitの習得と、ここまでに何気なく使ったallスクリプトの習得が必要そうです。

Xonoticにはサブディレクトリに応じて合計10個程のリポジトリが存在しています。
allスクリプトは全てのリポジトリに対して一括してgitコマンドを実行することができる
便利なスクリプトのようです。

もちろん、それぞれのディレクトリ(リポジトリ)において、
それぞれgitコマンドを実行することもできるみたいなので、
必要に応じて使い分けるのが良いでしょう。


Xonotic自体の開発には、以下のサイトが参考になります。
http://dev.xonotic.org/projects/xonotic/wiki/Repository_Access
http://dev.xonotic.org/projects/xonotic/wiki/Git


それでは、良いXonotic開発ライフをお過ごしください。


コピーしてやってみた

前の記事(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;
        }


以上。

C#のGCについてなんとなくまとめてみる2

前回の続き。

さて、ここまでGCの仕組みについてまとめてみたが、次にゲームにおいてGCをどのような方針で扱っていけばよいのか考えたことをまとめてみる。
とりあえずPC上やWindowsPhone7のMangoで採用されている世代別GCについてまとめる。

とはいえ、世代別GCをつかったゲーム開発の指針は下記のページで有難くまとまっている。
これをもうちょっと自分の頭の中で考えてまとめてみた。
いや、普通の人は上のページだけでも理解できると思うが、自分は考えてみないと分からなかったもので・・。


上のページでは、
GCの発生頻度を1フレーム、すなわち1/60秒よりも遅らせることができるかもしれない。この場合、1フレーム以下の寿命を持つオブジェクトの大多数は、「短寿命オブジェクト」として軽量な第0世代GCで回収されることが期待できる。
とかいてある。

これはどういうことかというと、短命なインスタンスは第0世代のみのGCが発生する前に使い終わりましょう、ってこと・・かな。
GCの仕組みで書いたとおり、第0世代のみを対象にしたGCは軽い。
ある程度大量にインスタンスを生成したとしても、第0世代のみを対象にしたGCが発生する前に使い終われば、GCで素早く回収できて、ゲームの動作にもあまり影響を及ぼさないはず。

逆に短命なのに第0世代のみを対象にしたGCが発生するまでに使い終わらなければ、それらの大量のインスタンスはすべて第1世代に「昇格」してしまう。
第0世代と第1世代を対象にしたGCは、第0世代のみのGCより重いので、ゲームの動作に影響を及ぼしてしまうかもしれない。
あるいは第2世代に「昇格」してしまえば、最も重いGCが動くことになる。

第0世代のみのGCではある程度大量のオブジェクトを素早く破棄できると書いたが、第0世代のみGCは大量のインスタンスを生成するほど発生タイミングが早くなる。
つまり、短命なインスタンスは第0世代のみのGCが発生する前にすべて使い終わろう、というより使い終わることができる程度の量を生成するのが望ましいということだ。

ゲームでは、1フレーム内で各種計算などで用いられて使い終わるインスタンスが大量にある。これらの短命なインスタンスはフレーム内で大量に使われ、フレームが終わる頃には大量に使い終わる。

1フレームより長い間隔で第0世代のみのGCが発生する程度の短命なインスタンスの量なら、第0世代のみのGCで素早く破棄できるということだ。

例えば、1.1フレームごとに第0世代のみのGCを発生するとなれば、1フレーム分の短命なインスタンスは第0世代のみのGCで破棄される。
この例だと0.1フレーム分の短命なインスタンスが第1世代に「昇格」するが、昇格するインスタンスは1フレームごと少量なので、それほど頻繁には第0世代と第1世代のGCは発生しない。

逆に0.9フレームごとに第0世代のみのGCが発生するとなれば、0.9フレーム分の短命なインスタンスがすべて第1世代に「昇格」してしまう。
次の第0世代のみのGCでは、0.8フレーム分の短命なインスタンスが第1世代に「昇格」する。
第1世代のみにどんどん短命なインスタンスができてしまうので、第0世代のみのGCより重い、第0世代と第1世代のGCが頻繁に発生することになる。

って感じだと思う。

どうも調べていくと、各GCの速度的には以下のような感じみたい。
第0世代のみのGC < 第0世代と第1世代のGC <<< 越えられない壁 <<< すべての世代のGC

上のページには
最も停止時間の長い第2世代GCの発生頻度は、どれぐらいの割合でオブジェクトが第2世代にたまっていくかに大きく依存する。第2世代GCの発生間隔を、ゲームの1ステージである18000フレームに比べて十分長くできれば理想的だ。
とある。

これはすべての世代のGCは重くて、ゲーム中に発生してしまうと大変なので、しばらく動作が停止することが許される場所、たとえばゲーム開始前や終了後のロード画面で発生すればいいよね、って感じなんだと思う。
ステージが終わって、次のステージ中ですべての世代のGCが発生したらやはり残念だし。

もしくは各ステージに1回程度のすべての世代GCなら重くても許容されるよね、ってことかな。

すべての世代のGCを発生させないようにするためには、短命なインスタンスは、第0世代のみのGC、または第0世代と第1世代のGCで破棄してしまうことが重要である。なぜならインスタンスが大量に第2世代に「昇格」してしまうことが、すべての世代のGCを発生させる条件だからだ。
長命なインスタンスが少量なら、ゲーム中に生成しても大丈夫かもしれない。
少量なら第2世代になったとしても、すべての世代のGCは発生しないからだ。

長命なインスタンスが大量に生成される場合は、ゲーム中ではなく開始時にあらかじめインスタンスを生成しておいて、使いまわすのがいいのかもしれない。長命なインスタンスはゲーム中に生成すれば、いずれ第2世代になり、すべての世代のGCを発生させてしまう。ゲーム開始時に第2世代としておけば、ゲーム中にすべての世代のGCが発生される可能性は少ない。

短命なインスタンスをゲーム中ではなく開始時にあらかじめインスタンスを生成しておいて、使いまわすことも考えられる。しかし、長命なインスタンスを大量に生成する場合より効果は薄いかもしれない。短命なインスタンスはゲーム中にnewして生成しても、第0世代のみのGCや第0世代と第1世代のGCでも比較的素早く破棄できるからだ。
逆に常に使うか使わないか分からないインスタンスを大量に保持しておくことによるメモリの浪費が問題になるかもしれない。
とはいえ性能がシビアで、大量のインスタンスが生成される大規模なゲームになれば検討することになるかもしれない。


C#には構造体というものが用意されている。
構造体は値型というもので、1万とかの配列にしてもインスタンスは1つとなる。
インスタンスの数が減らせるということだ。

GCが発生したとき、不要かどうかチェックしなければならないインスタンスが減るので、結果GCの実行が軽くなる。構造体には欠点もあるので、適切なところで使わなければGC以外の部分で遅くなってしまう可能性があるので、注意が必要である。


ここまで世代別GCでの方針について考えてまとめてみたが、次にXBOX360のGCについてまとめてみる。
XBOX360のGCは「マークアンドスイープGC」である。
世代別GCのように新しいインスタンスだけチェックするという賢い動作はせず、GCが発生するとすべてのインスタンスについてチェックを行うために、重い。

世代別GCのすべての世代のGCについてのまとめたところが役立つと考えられる。
第0世代のみのGCや第0世代と第1世代のGCについてのところはまったく役に立たない。
そんなものはないのだから。

大量にインスタンスがある場合は、インスタンスの使い回しをして、ゲーム中では一切newをしないという方針を検討する必要があると思う。ゲーム中で大量にインスタンスを生成してしまえば、GCが発生してゲームが止まってしまうからだ。

構造体の配列を検討するのもいいかもしれない。
前述したとおり1万の配列でもインスタンスは1つだからだ。
インスタンスが少なければGCが発生してもその時間は短くなる。
GCの時間が短ければ、ゲーム中にインスタンスを生成しても問題がないという方針をとってもいいかもしれない。



というわけで、ここまでGCについて調べて考えてみたことをまとめてみた。
ここに書かれていることすべてが正しいことではない。
というのも、自分自身まだゲームを作っておらず、XNAでゲームを作る上で必ず必要になると思ったので、まず調べただけだからだ。

最終的にはPCだったりXBOXだったりでツールを使ってパフォーマンスを計測しながら、ゲーム全体を見渡して、インスタンスの扱い方を変えてみたり、構造体を使ってみたりと試行錯誤することになるのが普通だ。

これからゲームを作っていって、ここに書いたことを確認していきたい。

記事検索
livedoor プロフィール
QRコード
QRコード

トップに戻る