2009年08月25日

グルー言語のススメ

夏らしい日々が続いていると思っていたら、不意に涼しい日が訪れるようになりました。
8月も下旬です。もうしばらくすれば秋の訪れを実感できそうですね満月
こんにちは、ハラです。

タイトルにあるグルー言語メモ
これは、そういう名前の言語があるわけではなく、
他のツールやコマンドを組み合わせて、一連の機能を生み出す事に適した言語の総称です。

今回は、
  1指定されたフォルダ内のファイルを検索
2見つかったファイルに対してツールを実行
3実行した結果を別のフォルダに移動させる
というパターンをグルー言語の1つであるRubyで試したいと思いますわーい(嬉しい顔)

ということで、上の機能左斜め上を下のように左斜め下実装してみました。

require 'fileutils'

# 出力先フォルダを作成(再帰的にフォルダを作ってくれます)
dst_folder = "./dst/"
FileUtils.mkdir_p(dst_folder)

# srcフォルダ内のテキストファイルに処理を実行
Dir.glob("./src/*.txt") do | src_path |
# 変換後のファイル名を生成
dst_file = File::basename(src_path, ".txt") + ".dat"

# ツールを呼び出し(コマンドは必要に応じて変えてください。)
# ``(バッククォート)で囲んだ範囲がコマンドとして実行されます。
# ツールが標準出力に出力した内容は、返り値として返ってきます。
`SampleTool.exe #{src_path} #{dst_file}`

# ツール実行後のファイルをdstフォルダへ移動
FileUtils.move(dst_file, dst_folder)
end

各行の内容はコメントを見てくださいぴかぴか(新しい)
コメント文を除くと8行ですが、さらに短くも出来ます手(チョキ)

このスクリプトを作成したことによって短縮される時間は、
1回のオペレーションあたり数分といった所でしょうか。
それでも、回数が多くなれば無視できない時間になってきますねグッド(上向き矢印)


今回はRubyを使っていますが、PerlやPythonといったスクリプト言語や
UNIXのシェルスクリプト、Windowsのバッチファイルなどでも同様の事が出来まするんるん
(言語によっては、もっと簡単だったり手間がかかったりします)


日々の開発や提出作品のテストを行う中で
  がく〜(落胆した顔)複数回のコンバーターやツールの実行
ふらふらフォルダの操作、ファイルのコピーや移動
もうやだ〜(悲しい顔)文字列(ファイル名、パス)の操作
といった事が必要な方々。

グルー言語の出番かもしれませんよexclamation&question

posted by 管理人 at 12:54 | Comment(0) | プログラミング

2009年07月30日

王様、王様。このプログラムは・・・・

大阪では、なかなかに暑い日々が続く今日この頃あせあせ(飛び散る汗)
寝るときに裸で寝たら涼しいかなぁ・・と思いつつ、
風邪をひくと困るのでしっかりと服を着ていますブティック
こんにちは、ハラです。

突然ですが、プログラムを使って「HEXADRIVE」と出力してみたいと思いますひらめき
と言っても、
  printf("HEXADRIVE");
とか
  std::cout << "HEXADRIVE" << std::endl;
とか
  puts "HEXADRIVE"
とかではありません。

では、実際に書いてみたいと思いますメモ
右斜め下 ここから、
   	  	   



















右斜め上 ここまでです。

どうでしょうかexclamation&question
このプログラムが見えない人は・・・・・・、と言いつつ私も見えませんがく〜(落胆した顔)

これは、Whitespaceという「スペース」と「タブ」だけで書くプログラム言語ですexclamation×2
文字は見えませんが、ドラッグしてみるとスペースとタブが並んでいることが分かると思います。

一見すると、このプログラム言語はちゃんとした機能を持っているの?
と、思ってしまいますが、文字列出力だけではなく、四則演算なども行うことができますぴかぴか(新しい)

ただ、機能があったとしても、ここまで読み難いと実際に使うのはなかなか難しいですねもうやだ〜(悲しい顔)


Whitespaceに限らず、プログラムは、一緒に開発する方や作品提出先の方など
自分以外の人が見る機会があるものです目

王様にしか見えない・・・・、ではなく、自分以外の人が読み難いプログラムは
出来る限り避けて行きましょう〜わーい(嬉しい顔)

posted by 管理人 at 10:41 | Comment(0) | プログラミング

2009年07月22日

ブックマーク機能

おはようございます。平尾です。
本日は皆既日食の日ということで、TVでも大いに話題になっていますねTV
外も少し薄暗いような気がします。
普段と違ったこういう日は妙にワクワクしてしまいます。



本日はVisualStudioの機能、ブックマークを紹介します。
結構便利な機能なのですが、あまり知られていないような気がします。

複数ソースを分割して開いたり、単一ソースのウィンドウを分割することで、
ある程度広い範囲のソースを見渡すことができます。
しかし「定義へ移動」などをして、見てた位置がずれてしまい、また探すことに…ふらふら
なんてことよくありませんか?
そこでこのブックマーク機能です!ひらめき

ブックマーク機能とはその名の通り、ソース中にブックマークを付ける機能ですペン
ただ単にマークを置くだけの機能です。
こうしてマークを置いておけば、後から簡単にジャンプすることができます。
「定義へ移動」などでずれてもすぐ復帰できます。
(ブレークポイントなどとは違い、実行中に何かが起きるといったものではありません)


使い方は次のようになっています。
マークのセット/解除:Ctrl + K → Ctrl + K (2回連続で Ctrl + K を押します)
次のマークへの移動 :F2
前のマークへの移動 :Shift + F2
マークをすべて削除 :Ctrl + K → Ctrl + L

普通のショートカットと違い、2回連続で入力するというちょっと特殊な押し方になっています。
使う人が少ないからでしょうか…たらーっ(汗)

マークをセットすると、ソースの左端に水色の四角形がセットされます。
これがブックマークです。



これでソースを自在に行き交うと、そばで見てる人には何が起こっているのか分からないけど、
何だかすごくかっこよく見えるかもしれません(笑)
普通に便利なので、是非一度試してみてくださいわーい(嬉しい顔)ぴかぴか(新しい)


posted by 管理人 at 10:47 | Comment(0) | プログラミング

2009年07月08日

コンパイラ最適化

こんにちは、イワムーですわーい(嬉しい顔)

今日はコンパイラによる最適化(compiler optimization)について少し書きたいと思いますペン

コンパイラとは人間がC言語などのプログラミング言語で記述したソースをコンピュータが実行できる形式に変換するソフトウェアのことです。
コンパイラがソースを実行形式に変換する際に、より実行時間を短くメモリを効率的に使用できるようになど、効率的に調整する処理のことを最適化と呼びます。

非常に簡単な例を挙げてみましょうexclamation

int func(void)
{
   int a = 5;
   int b = a * 2;
   int c = b + 2;

   if( a > b ){
     c /= 2;
   }
   return c;
}
まず、b は 定数を畳み込むことにより、 b = 10 へと最適化されます。同様 c = 12 となります。
次に、条件分岐(if文)の個所は真になることがないことが確定していますので、この個所はデッドコードとして削除されます。

結果、上記のコードは

int func(void)
{
   return 12;
}


と最適化されることになりますひらめき
(※コンパイラにより、また最適化レベルによっても挙動が異なります)


他にも、頻繁に使われる変数をレジスタに割り当て高速にアクセスできるようにしたり、ループ回数が同じである複数のループをまとめることでオーバーヘッドを少なくするなど、さまざまな最適化があります。

コード量が膨大になるゲームプログラムではコンパイラ最適化によって実行速度が大きく向上するケースが多くなるため、我々プログラマーの強力な味方となります手(チョキ)

ですが、最適化にも注意が必要な場合がありますexclamation×2

例えば、Win32API の ZeroMemoryマクロの最適化による不具合は有名ですよね。

void func(void)
{
   char password[100];

   // パスワード入力
   inputPassword();

   // メモリからパスワードを消去
   ZeroMemory(password,0);
}


このコードは一見問題なさそうに見えますが、リリースビルドなどでコンパイラの最適化を施すと、ZeroMemoryはコールされないケースが起こり得ます。
最適化では、それ以下のコードでメモリを参照していない変数への代入などの処理を省くように作られているケースが多く、上記の場合ではZeroMemoryをコールしている箇所以下では変数passwordが参照されていないため、ZeroMemoryを呼ぶ必要がないと判断されるのです。
結果、スタックにパスワードが残ることになり、セキュリティホールを招くような事態になる恐れがありますたらーっ(汗)

これを防ぐためには、上記の例ではZeroMemoryの代わりにSecureZeroMemoryマクロを利用したり、ゼロクリアした後に、変数passwwordを参照するコードを書くなどの解決策が挙げられるでしょう。
また、volatile修飾子を利用して、最適化を防ぐ方法もあります。

このように、コンパイラ最適化により思わぬ不具合を招く恐れもあるので、コンパイラの最適化オプションを利用する際は意識してみてください手(パー)


posted by 管理人 at 22:19 | Comment(0) | プログラミング

2009年06月15日

乱数

こんにちは、イワムーですわーい(嬉しい顔)

今日はゲームで使用される乱数について書きたいと思いますパンチ

ゲーム制作において、敵の行動の選択、アイテムの出現確率など、乱数を使用する場面は多いと思います。

現実世界でのサイの目の出現確率などとは違い、コンピューターで扱う乱数は、疑似乱数です。

つまり、乱数のようにみえるけれど、実際は論理式があり次に発生する数が求められますペン

この疑似乱数であるということに、注意が必要な場合がありますexclamation×2

C言語標準ライブラリにはrand()という関数があります。
この関数を用いて乱数を発生させている方も多いのではないでしょうか?

この関数の内部は線形合同法というアルゴリズムが用いられていますひらめき
このアルゴリズムですが、下位ビットの精度が悪いことで有名ですバッド(下向き矢印)
最下位ビットの周期は2で、0と1が交互に現れてしまいますたらーっ(汗)
その後上位ビットになるつれ、周期が4,8,16、・・・と上がっていきます。

if (rand() & 1) {
  if (rand() & 1) {
    printf("A");
  } else {
    printf("B");
  }
} else {
  if (rand() & 1) {
    printf("C");
  } else {
    printf("D");
  }
}

↑このプログラムではBとCしか出力されず、AとDは一切出力されません。
(※処理系によりrand()の実装が異なるため、結果が異なる場合があります)

さらに、線形合同法の問題点として、ある乱数が得られたら次に得られる乱数が限られることが挙げられますexclamation

例えば、2次元のベクトルの各要素を rand() で求めるような場合、得られる組み合わせが限られてしまうことになります。

(※処理系によっては、上位16ビットを右シフトし、下位の15ビットを返すものもあり、上記の限りではありませんが、乱数の返す値が0〜32767となり、精度が足りなくなる問題があります。単に精度の問題なのであれば rand()*rand() とするなどで対処可能な場合もあります)


では精度が高く、周期の長い乱数を得るにはどうすれば良いのでしょうかexclamation&question

ここはメルセンヌツイスターの出番ですよね手(グー)

623次均等分布かつ、10進数で6000桁以上の長周期性と、自然乱数近似を得たいのであれば、真っ先に挙げられるソリューションではないかと思います位置情報
(内部のワーキングメモリが32bit版で2496バイト必要になるので、使用の際は注意してください)

さて、rand()の使用上の注意や、メルセンヌツイスターの簡単な紹介をしてきましたが、ゲームで乱数を使う場合は、その用途に応じて適切なアルゴリズムを選択することが最も重要だと思いますわーい(嬉しい顔)

処理速度やメモリが切迫していて、精度をそこまで求めない場合は、rand() も十分候補に挙がるでしょうし、単純なタイマーでも代替可能かもしれません。

また、乱数の偏りが一概に悪いとは言いきれないときもあるのではと思います。
それがそのゲームの良い味になっていたりするんですよねわーい(嬉しい顔)

ゲームで乱数を使用する時は、意識してみてください手(パー)

posted by 管理人 at 16:55 | Comment(0) | プログラミング

2009年05月27日

クラスのメンバ初期化時の注意事項

こんにちは、イワムーですわーい(嬉しい顔)

今日はプログラムの話でもしてみようと思いますパンチ

C++ でクラスのデータメンバ(メンバ変数)の値を初期化したい場合、皆さんはどのように実装されていますか?

様々な方法が考えられると思いますが、
memset() の使用に関しては時として注意が必要になりますexclamation×2

#include < string.h >
void *memset(void *s, int c, size_t n);


memset() は s で示されるメモリ領域の先頭から n バイトを c で埋める関数です。

文字を格納するバッファを特定の文字で埋めておきたい場合など、便利ですよねペン

char buff[6];
memset(buff,'H',sizeof(buff));


同様にクラスオブジェクトに対しても memset() は使用できますが、そのクラスが virtual メソッドを含んでいた場合は注意が必要ですexclamation

#include < string.h >
#include < memory.h >
#include < iostream >

class Hexa {
public:
   Hexa(){}
   ~Hexa(){}
   virtual void setStr(void){}

protected:
   char _str[0x10];
};

class Hexa2 : pubilc Hexa {
public:
   Hexa2(){}
   ~Hexa2(){}
   void setStr(void){ strcpy(_str,"hexadrive"); }
   char* drawStr(void){ return _str;}
};

int main(void)
{
   Hexa2* pHexa = new Hexa2();
   memset( (void*)pHexa, 0, sizeof(Hexa2) );
   pHexa->setStr(); // ここでクラッシュする
   cout<drawStr();
   retuen 0;
}


このプログラムを実行するとsetStr()を呼び出す時点でクラッシュしますあせあせ(飛び散る汗)
なぜなら setStr() が virtual メソッドになっているからです。

C++ではクラスのメソッドに virtual キーワードをつけて宣言する(仮想関数)と、自動的に「関数ポインタの配列」(のポインタ)がメンバ変数領域に作られます。
これを仮想関数テーブル(vtable)と呼びますひらめき
仮想関数テーブルの詳細はこちら

実行時に動的に呼び出すメソッドを決める必要がある、virtual メソッドはこのvtableで実現されています。
通常はプログラマーはvtableにはアクセスできませんが、memset()をvirtualメソッドを持ったクラスオブジェクトに適用してしまうと、上記例ではvtableの中身までゼロクリアされてしまうため、アドレスがゼロになっている関数を呼び出そうとした時点でクラッシュしてしまうのですバッド(下向き矢印)
(コンパイラの最適化オプション等の影響でクラッシュしない場合もあります)

ではどうやってメンバを初期化したら良いのかというと、やはりデフォルトコンストラクタや初期化メソッドで各メンバの初期値を入れるのが良いのではと思いますわーい(嬉しい顔)

クラスのメンバ初期化時には意識してみてくださいねるんるん



posted by 管理人 at 18:56 | Comment(2) | プログラミング

2009年04月23日

小技

こんばんは、平尾です。
今日はプログラムのとっておきの(あまり使えないあせあせ(飛び散る汗))小技を紹介します!

C言語で値を交換する際、直接交換はできないので普通は一時領域を使いますよね。

int a = 1234567;
int b = 5555555;
int tmp;

tmp = a;
a = b;
b = tmp;

ところが、一時領域を使わない方法があったりするのです!
こんな感じになります。
(自分で考えたい方のために白い文字で書いています↓)

int a = 1234567;
int b = 5555555;

a ^= b;
b ^= a;
a ^= b;

これはXOR交換アルゴリズムというものです。

パッと見て何をしているのか分からないので、
普段から使うことはまず無いと思います。
ゲーム業界に入って早10年になりますが、
実際に今まで使ったことはありませんがく〜(落胆した顔)

ところが、とある方がこのアルゴリズムを実際に使ったと言っていました。
非常に限られた限定的な用途ですが、そのときは大いに役に立ったそうですぴかぴか(新しい)


一見するとやりようがないものでも、
解決方法は意外とどこかに埋もれていたりします。
それを発見したときの喜びといったら!
これがプログラムの醍醐味の1つだったりするんですよねわーい(嬉しい顔)

これを見ている皆さんの中にも、将来この小技を使う日がくるかもしれませんよ!?

posted by 管理人 at 21:07 | Comment(0) | プログラミング

2009年04月16日

キャッシュ再び

桜が咲いたと思ったら急に暖かくなって気分も急上昇ですねグッド(上向き矢印)
社内の室温も急上昇してきて、タンクトップに手を出しつつある「オクダ」です。
あ!、王子のほうじゃないです。

今回は以前お話ししたキャッシュの話をもう少し掘り下げていきます。
まずは前回の話をもう一度読んで復習してください。

前回の話だと机の上に本を広げるために図書館に本を借りに行き、自宅の本棚に移動、本棚から机の上といった例えでした。本
この例えだと、実際に必要な書籍やノート、計算用のメモなどを必要に応じて手近な所に移動し、不要になったら元の場所に戻すようなイメージです。
しかし、実際我々プログラマが扱っているコンピュータの世界でおこなわれている事は、この例とは少し違います。exclamation&question
もう少し具体的に話を掘り下げていきましょう。

コンピュータの世界では、メディアorネットワーク、ハードディスク、メモリ、メモリキャッシュ、レジスタの順にキャッシュすると前回お話ししました。
実際ここでおこなわれているのは、「データを移動」するのではなく「データをコピー」しています。
元のデータはそのままで、コピーしたものを手元に持ってきます。

コピーしてきたものを変更した場合はどうでしょう?
元のデータにも変更を反映させないとダメですね。

逆に変更していなくて当面必要なくなったデータはどうでしょう?
そのまま捨ててしまっても元のデータがあるので大丈夫ですね。

コピーした後で誰かが元のデータを変更したらどうなるでしょう?
もう一度コピーしなおす必要がありますね。
自分がデータを変更していたらどうなるでしょう?
誰のデータを信じればいいのか、、、がく〜(落胆した顔)

ハードディスクからメモリの場合、リードオンリーでファイルを開いたり、ファイルをロックして他の人が変更できなくしたり、OSがうまい事やってくれます。
メモリキャッシュからレジスタの場合、コンパイラがいい感じで動作するようにしてくれますし、スレッド切り替えなどで不都合が起こらないようにOSがうまい事やってくれます。

しかし、メモリからメモリキャッシュの場合プログラマが上手にケアしてあげないといけなかったりします。
(ちょっと難しいので読み飛ばしても大丈夫です)
一般的にキャッシュされていない場合、キャッシュ動作そのものが終了するのを待つ必要があります。
必要な事が分かっている部分をあらかじめキャッシュするように準備をしつつ別の処理を行い、必要になった時点でキャッシュ動作が完了しているのが理想的です。
キャッシュ動作待ちを回避できるからです。

当然書き戻しにも時間がかかります。
CPUによる個体差はありますが、キャッシュしたものは必ず書き戻す設計になっている場合もあります。
変更していない場合、書き戻しを行わないCPUもあります。
一般的に書き戻しはいつの間にか行われていますが、キャッシュが無くなった場合書き戻してキャッシュの容量を確保しないと次のキャッシュが出来ません。
新たにキャッシュをする時に書き戻しが発生する可能性は非常に高いです。
書き換えていなくて不要になったものはキャッシュから捨ててしまえば、書き戻し動作をしないで済みます。

DMAやビデオメモリとのやり取りなどを行う場合、ほぼ間違いなくメモリとメモリキャッシュとの間に不整合が起こります。
これを整理してあげるのもプログラマの仕事です。
スレッド切り替えにミューテックスやセマフォ、クリティカルセクションを駆使して不具合を起こさないようにするのと似てますね。

これらの事は普通のプログラムをする上で気にする必要はまずありませんわーい(嬉しい顔)
よかったですね。

しかし、描画やコリジョンといった速度を要求される部分や、ハードウェアを直接操作する必要がある場合、不可欠な知識です。
また、キャッシュによる不具合があった場合、知識がないとデバッグできません。
今すぐ必要ないかもしれないし一生無縁かもしれませんが、少しでも興味が沸いたら一度本腰を入れて勉強してみてください。
きっとワンランク上のプログラマになれるでしょうexclamation×2

posted by 管理人 at 19:43 | Comment(0) | プログラミング

2009年04月15日

読んだら・・・書いてみよう

暖かくなってきてます。春を感じる日々になってきています。
そして、花粉も元気です・・・・・もうやだ〜(悲しい顔)
こんにちは、ハラです。

過去3回にわたり、Ruby言語でいろいろなデータを読んできましたが、
実際にC/C++言語のプログラムで読み込むバイナリデータとして
書き出していませんでした。
今回は、書き出しを行ってみたいと思いますメモ

まずはサクッとスクリプトを書いてみましたレストラン
内容は、固定長の数値データをバイナリとして書き出しています。

# テキストファイルから読み込んだという仮定のデータ
dataArray = Array.new
dataArray.push([1, 2, 3, 4, 5, 6])
dataArray.push([7, 8, 9, 10, 11, 12])

# バイナリモードでファイルを開く
dataFile = File.open("data.bin", "wb")

# 書き込み
dataArray.each do | data |
# 個々の数値を符号なし32bitで出力
dataFile.write data.pack("L*")
end

# ファイルを閉じる
dataFile.close

書き出した結果をバイナリエディタで見ると下の通りになります。
読み込んだ数値が32Bitでちゃんと出力されてますねfax to

01000000 02000000 03000000 04000000
05000000 06000000 07000000 08000000
09000000 0A000000 0B000000 0C000000

ちなみに、符号のありなしや出力する際のビット数、
エンディアンは指定できますひらめき

後は、データを読む側のC/C++のプログラムを
書けば使うことが出来ますねグッド(上向き矢印)


実際のデータはここまでシンプルではないかもしれませんが、
データ作成に必要な要素は上のスクリプトや今までの日記に含まれています。
必要とされるフォーマットにあわせて読み込むファイルや
書き出し方を変えればOKです。

コメント文でも書いてますが、バイナリモードで開くことをお忘れなく。
テキストモードだとキャリッジリターンが含まれるので、
データサイズがずれてしまいます。
もちろんexclamation×2 私は間違えて、小一時間悩んだことがありますがく〜(落胆した顔)

短いコードと言えども油断せず、その恩恵に与りたいですねわーい(嬉しい顔)

posted by 管理人 at 11:39 | Comment(0) | プログラミング

2009年04月06日

4x4 part2

こんにちは!イワサキです。
桜がついに見頃ですね。お花見に行く人も多いのではないでしょうかわーい(嬉しい顔)
今日は丸一年前のブログ記事を思い起こしてみます。

ちょうど一年前に4x4行列の「行優先」と「列優先」の違いの話をしました。
以前は「列優先」のほうが内積演算を生成できるメリットがあることを紹介しました。

では具体的にそれ以外でもっと便利なことはないのか?
というわけで今回は「メモリ・レジスタ節約」について紹介します。

ゲーム内でよく利用するスキニングボーンアニメーションがあります。
これは基本的には

1 回転(Rotate)
2 拡大縮小(Scale)
3 平行移動量(Translate)

の3つの要素で表すことが多いです。これを四元数(Quaternion)で持っておき、その回転要素と残りの要素を取り込んだものとして最終的に4x4行列に変換して使用することが一般的です。

4x4行列とは次のようなデータ構造です。

2009-04-06_00.PNG

GPU内部では行列もベクトルデータとして扱われ、「4要素ベクトルが4本」として扱われます。
レジスタの本数はハードウェアが持っている有限個数のリソースです。これを節約できれば、より沢山の行列を同時に読み込むことができるようになります。
この場合「行優先」で考えると成分の位置は次のようになります。

2009-04-06_01.PNG

モーションアニメーションのデータは上記の「回転」「拡大/縮小」「平行移動」3つの要素のみで構成されますので、基本的には「回転3x3行列」と「平行移動3要素ベクトル」のみで情報量は足りることになります。
そこでデータを減らして4x3行列にしてみます。

2009-04-06_02.PNG

このようにW成分は必要ないわけですが、レジスタは1本あたりXYZWで4要素あります。
W成分を使わなくなっただけではレジスタの本数が減りません。
これでは節約の効果はありませんよね。

さらにここで「転置(Transpose)」を使います。
すると、次のようになります。

2009-04-06_03.PNG

どうでしょうか。データがぴったり詰まってレジスタが一本節約できました!
この状態では転置されていますので「列優先」の状態です。
行優先の4x3行列列優先の3x4行列にすることがアニメーションデータの節約になることがわかります。
ちなみにOpenGLではこの「列優先」のデータ構造が採用されています。

たとえばGPUが256本のレジスタを持っていたとすると、その中に設定できる4x4行列の数は

256÷4本 =64個

になりますが、これが列優先の3x4行列ならば

256÷3本 =85個

の行列パレットを登録することができるようになります。
これを応用してさらに節約して回転をクォータニオン、平行移動量を3要素ベクトル、拡大率をW値に持つことでさらに2本のベクトルにまで節約することもできます。
そうすればなんと128個・・・つまり4x4行列の倍の姿勢データを持つことができるようになりますexclamation×2

上記は一例にすぎませんが、このように応用の幅が広がっていきますぴかぴか(新しい)
最近ではPCのメモリ容量も2GBを超えることが当たり前になっていますが、資源は有限です!
容量を節約することで高速化、時間を短縮することで高速化。
ゲーム制作の現場では快適なゲーム作品にするためにスタッフが常に努力をしています。
遊んでくれるユーザーに少しでも快適な操作感・レスポンスで楽しんでほしい、ゲームプログラマのコード高速化にはそんな思いがあります晴れ

ヘキサドライブは常にプレイしてくれる皆さんのことを考えています。手(パー)ぴかぴか(新しい)ぴかぴか(新しい)

posted by 管理人 at 21:00 | Comment(0) | プログラミング