Hatena::ブログ(Diary)

Ideals and Reality このページをアンテナに追加 RSSフィード

2012-08-10

Unityの核であるMonoBehaviourについて

Unityを使っている人なら誰でも見たことがあるであろう、MonoBehaviorクラス。

巷ではMonoBehaviourを使うとC#の機能が使えなくなるとか、コードビハインド出来なくてMVCモデル的な作りが出来ないとか、極力MonoBehaviourは使うな!とまで言われたりしています。

私は逆にUnityを使うのにMonoBehaviourを使わないなんてとんでもない!と思っています。

と、いうかMonoBehaviourを使うことがUnityを使う一番の理由になると思ってるくらいに。

その潜在能力の高さを1度知ってしまうと使わないなんて考えられなくなってしまいます。


1.MonoBehaviourの持つコルーチンが超強力

これもう本当に強力。

おもむろに

yield return 0;

と書けばそのフレームの処理をそこで終了し、次のフレームはそこから処理を再開するのである。

yield return new WaitForSeconds(1f);

と書けばなんと処理を終了し一秒後にはそこから処理を再開してくれるというのが簡単に書ける。

これを使えば非同期処理は簡単に書けるし、何かを待ってステート進行させるというのが一瞬で出来てしまう。

スタートしたコルーチンは簡単に止めることも出来るので、コルーチン中でも対応が出来る。

ここらへんはもう誰でも使っているようなレベルだろう。

が、Unityのコルーチンの強力さはそれだけではない。

本来UnityのコルーチンはMonoBehaviourのUpdate()関数内では使えないが、実はStart()関数無限ループしつつyieldを実行するとUpdate()内で実質的に同じことをやっているようなことが出来る。

http://answers.unity3d.com/questions/8908/is-function-update-short-for-function-start-whilet.html


void Start()
{
	while (true) {
		// 毎フレーム行う処理
		yield;
	}
}

こんな反則技がよく許されたもんだと思いつつもこれでAIのステート管理が超絶的なまでに楽になる。

実際このテクを使ってAIを記述しているケースも多いようだ。


2.OnXxxXxx系のコールバックで各種処理のフックが超簡単

例えばマウスでクリックされたという情報がとりたければOnMouseDown()を呼び出せばそれだけでマウスボタンのイベントが処理出来る。

このコールバックイベントはマウスだけでなくあらゆるものに対応しており、コリジョンのヒット時判定、レンダラーの描画ONOFF時、新しいシーンが読み込まれた時、描画が完了した時など、あらゆる状況で呼ばれる。

これがあるおかげであらゆる状況で処理をフックさせることが出来る。

実際にゲームを作っていると、「あの状況で処理を割り込ませたい…」という状況は多々発生する。

もちろん全てのケースに対応しているということはないだろうが、ほぼ大抵の状況に対応するほどの量は用意されている。

特にカメラ描画内に存在しているかいないかというのは非常に重要でAIの動きの制御や描画の最適化にも使用されるので知っておいて損はない。


3.MonoBehaviourが持つメッセージ機能が超汎用的

SendMessage("FuncName")というような具合で、MonoBehaviourが持つGameObject全体にFuncName()という名前の関数があれば呼び出すことが出来る。

これはそのGameObjectに対象となるスクリプトがアタッチされている必要があるが、やりようによってはかなり汎用的なことが出来る。

これだけならまぁありがちな感じがするが、Unityアニメーションエディターとアニメーションイベントを使ったメッセージが凄い。

UnityアニメーションエディターはGUIで編集する汎用的なものですが、ここで起きるアニメーションイベントにMonoBehaviourのスクリプトがアタッチ出来る。

ここであらかじめGameObjectのヒエラルキー上に位置情報を示すロケーターObjectを配置しておき、イベントが起きた際に

Transform[] transArray = gameObject.GetComponentsInChildren<Transform>();
foreach (Transform trans in transArray) {
	if (trans.name == eventNameString) {
		PlayEffectEvent(eventNameString, trans);
	}
}

上記のようなコードが呼ばれるようにしておく。

次は各GameObjectが持つTransformを取得し、GameObjectにはあらかじめ発生させたいイベント名などをつけておく。

そしてイベント発生時のMessage関数にそのイベント名の文字列を渡しておき、特定のアニメーションクリップに対して指定したタイミングでこの位置に対して汎用的なイベントを起こすということが出来る。

上記の方法では特定の位置のGameObject Transformの名前がイベント名文字列と一致した場合はその位置に対してエフェクトを発生させるというイベントを発生させている。

これはあらゆるものに応用が可能であり、エフェクトだけではなく、SE、カットシーン演出でのカメラ移動やAIアニメーションベースにステート管理させるといったことが簡単に出来るようになる。

ぶっちゃけこの仕組みだけでゲームの動きと演出と制御がかなり出来てしまう。

それくらいに恐しい仕組みであることは間違いない。


4.MonoBehaviourに揃っているコンポーネントたちが超豊富

MonoBehaviourはTransformなど当然のようなコンポーネントを持っているが、それ以外にも剛体物理を実現するrigidbody、カメラ制御をするcamera、ライト担当のlight、音担当のaudio、コリジョン担当のcolliderなどなど実に豊富なコンポーネントがアタッチ出来る。

これらはGetComponent()を使えばどこでも取得出来るし、自身がGameObjectであるのならAddComponet()でいつでも追加も出来る。

もちろん全部のコンポーネントを使うことは間違いなくないが、いつでもそれらを使うことが出来るのがMonoBehaviourの魅力であることは間違いない。

Unityコンポーネント指向という考え方はMonoBehaviourをベースに作られているということでMonoBehaviourの重要さがわかったのではないかと思う。

とはいえこれでもまだMonoBehaviourの魅力としては一部だと思っているし、私自身もまだまだ色々なことが応用出来るのではないかと思っています。

コルーチンと各種コールバック、メッセージなどは全て混ぜて使うことも出来るし、応用すればもっと凄いことが簡単に出来るはずです。

これで少しでもMonoBehaviourを「もっと使ってみたい!」と思ってもらえれば光栄だし、「もっとこんな使い方してる!」とかあればぜひ教えてください。


追記

改めてみても説明がわかりにくい。

そしてUnityの説明はコードや文章だけでは伝えにくいですね…

動画とか撮るべきなんだろうか。

2012-07-09

UGJ2ndに初参加してきました

UnityGameJameというゲームジャム(即興チーム超短期間ゲーム開発)イベントに参加してきました。

http://www.zusaar.com/event/306053


実はゲームジャム自体の参加が始めてだったんですが、限られた時間の中でどうすればいいのかよくわからず時間配分にも非常に悩みました。

開発時間自体は30時間もなく、当然食事や睡眠もその時間でとらないといけないので難しいところ。

Unityは最近本格的に触り始めてまだこれから覚えるという感じだったのですが、一緒にチームを組んだ人もほとんどUnityは初心者で逆に自分の方が教えている時間が長いという事態に。

しかしUnityを使って一通りゲームを作ってみて、意外と自分の知らない事が多くて勉強になる事ばかり。

普段プログラマーとしてコードばかり書いているとUnityの事ってあまり知らないんだなと実感。

チームを組んだ人もみんな初対面でスキルも当然バラバラ。 どこまで出来るのかと不安ばかりでしたが、なんとか形になったものが完成。以下のサイトにて実際にプレイする事が出来ます。

http://c8bkz3-ay9-app000.c4sa.jp/


今回はチームF:Uniben666としてゲームを作らせてもらいました。

色々とゲームとしては酷いところもありますが、少しでも遊んでもらえたら幸いです。

2011-12-25

ゲームプログラマのためのC++を読んだ

三宅陽一郎さん監修の本がまたでたという事で購入。

一部の人にとっては待望のその名も「ゲームプログラマのためのC++」です。

どういう本だったか紹介していきたいと思います。


入門書とEffective C++の間くらいの立ち位置の本

本書は所謂C++の入門書ではないことは間違いありません。

C++の構文を教えてくれるみたいなところは一切なく、様々なノウハウやテクニックを教えてくれる本です。

継承の使い方、const、参照、キャスト、テンプレート、例外、RTTI、デザインパターン、STLなど扱う分野はとても広いです。

そのどれもゲームを作る前提の話で書かれているため、例となっているコードもゲームの話がほとんど。

しかし、ゲームにおいて例外やRTTIの話はほとんど見かけないためそういう意味では貴重な資料だと思います。

Effective C++ほど濃くはないものの、そこそこに深い話も多いです。


メモリ周りは特に詳しい

ゲーム開発において一番気になるところはやはりメモリ周りの情報です。

本書はそこに関してかなりの情報量を割いており、ことあるごとにメモリの話が出てくるので安心?出来る内容です。

特にメモリ割り当てとカスタムメモリマネージャ、メモリプールの話はゲームにおいてほぼ必須な内容なので知らない人は知る必要があります。

もちろん知らなくてもゲームを作ることは出来ますが、これを知っているだけで限界をどこまで引き伸ばせるか差が出るでしょう。


パフォーマンス戦略

多くの章ではパフォーマンス戦略やコスト分析の話が載っており、何が重くて何が軽いかの情報があります。

当然便利なものは多くのコストがかかりますが、それを踏まえてもC++のこの機能は使用すべきだろうという指針を与えてくれます。

何よりもまずは計測重視で、重いか軽いかは実際に使用してみて計測してから検討すべきだろうと教えてくれます。

ここらへんは当たり前の話ですが、私が知る限りでは「とりあえず遅いみたいだから使わない」という人は沢山いました。

例外やRTTIは特にその最たるものですが、まずは使わないという選択肢ではなくて使ってみてから考えよう。


何気に詳しいSTL

ゲーム開発においてよく避けられるSTLことStandard Template Libraryですが、この本では積極的に活用しようという事でかなりページを割いて解説してくれます。

もちろんメリットデメリットをしっかりと理解した上で使用するということになっていますが、デメリットよりもメリットが勝るくらいの魅力があるという事を有名なゲーム開発者が教えてくれるというのは素晴しい事です。

この本ではSTLの初級解説から応用方法までしっかりと解説してくれてSTLを知らない開発者を助けてくれる内容になっています。

STLを知らない開発者はこのためにこの本を読む価値があると言ってもいいくらい分かりやすい解説だと思います。

本当に少しだけですが、Boost Libraryの紹介もあったりとこの手の本にしては珍しい内容だと思いました。


最後に

この本を読んでも残念ながらゲームを作れるようにはなりません。

なぜならゲームの作り方は一切解説してくれないからです。

C++を使用していて当然の事を出来るようにしてくれる本です。

そして出来あがるものの品質を何段階か引き上げてくれる事は間違いないでしょう。

大型プロジェクトでの対処法、クラッシュしないゲームの作り方、とても大切な事です。

ですが、それが出来ていない開発者はとても沢山います。

そういった人達に向けてぜひ本書は読んでもらいたい内容だと思います。

2011-02-12

OpenGLとDirectX

最近毎日のようにOpenGLに触れるようになって色々とDirectXと比較すると違うなぁと思ったりするのでちょっと個人的な主観で色々まとめてみようと思います.

■OpenGL

歴史的背景は置いておくとして,現在のOpenGLはバージョン4.1が最新版で一時期の更新が滞り,DirectXと様々な面で差をつけられていました.その名の通りオープンな仕様で非常に幅広い処理系に対応しており,専らゲーム専門のライブラリというわけではなく,画像処理やCADなどの分野でも使用されている事が多いです.GLSLという独自のシェーディング言語を仕様化し,更にマルチな展開をみせるようになり,組込み用途向けのOpenGL ESとの連携でどんな環境にも耐えられるようになってきつつあります.


■DirectX

ご存知Microsoftが作りだしたゲーム特化のマルチメディア処理用API群です.一時期の更新の頻度はOpenGLにとても大きく差をつけるほど進化しましたが,最近は少し大人しめ.またコンソールゲーム機であるXboxにもDirectXが搭載されており,完全な互換性とまではいかなくともかなり高い互換性を持っています.OpenGLと大きく違うのはマルチメディア処理用に作られているので描画部分以外でも使用出来るのが強み.ぶっちゃけDirectXさえあればゲーム開発が出来てしまうのが大きいですが,それ故に仕様がかなり肥大化しすぎているという側面もあります.


■OpenGLのメリット

  • マルチプラットフォームであらゆる分野において動作する ※完全ではない
  • 非常に単純な構造になっているので習得は簡単な方
  • OpenGL用に連携がしやすい外部ライブラリが沢山ある
  • 初期からインストールせずに動作する環境が入っているケースが多い
  • GLUTがWindowsAPIほどゴチャゴチャせずにとても扱いやすい
  • PS3などのコンソールゲーム機ではOpenGLの独自拡張が使われている

■DirectXのメリット

  • 他にインストールしなくても十分に開発が可能
  • WindowsにおいてはOpenGLよりも最適化されており,高速に動作
  • ネット上にドキュメントからサンプルまで非常に豊富
  • Windows環境においては安定した動作が期待出来る
  • OpenGL以上に技術の進歩が速く,最新の技術を導入出来る
  • とにかくゲーム開発においては今でもスタンダード

■OpenGLのデメリット

  • ゲームを作る場合,サウンドやジョイスティック用に別のライブラリを必要とする
  • 速度面でDirectXより多少劣る
  • サンプル,ドキュメントは少なめ
  • DirectXと比べて後追いな状態なため,比較的進化が遅い
  • 基本的な描画機能に絞っているため,線形代数も自分で作成する必要がある

■DirectXのデメリット

  • Windows環境でしか基本的に動かない
  • 豊富なライブラリ群なため,仕様がとても膨大
  • 簡単なスケルトンプログラムでさえも複雑になってしまう
  • とても扱いが難しい機能が多い
  • 良くも悪くも独自仕様により汎用性に乏しい

■結局どっちもどっち?

どちらも一長一短であり,メリットとデメリットもハッキリしています.一言で言うならば,「汎用性に特化しているOpenGL」と「ゲーム開発に特化しているDirectX」でしょうか.


OpenGLは最近凄い頻度で更新しているので,技術的にも十分DirectXと大差なくなってきているのでそこは心配ないかもしれません.OpenGL ESの存在も大きく,iPhoneアプリケーションなどで大活躍している現在においては十分すぎるシェアもあると言えます.またOpenGLは細かい機能を提供していないので,3Dなどの勉強にも適していると言われています.その割りには簡素で綺麗なコードも書きやすいというのも魅力のひとつ.


DirectXも最近でこそは少し大人しめの印象がありますが,今でもメジャーバージョンアップを繰り返し,どんどん新しい技術を提供しています.Windowsでゲームをする場合は相変らずDirectXという時代がまだまだ続くとは思いますので,凄いゲームを作るんだ!! という方にはやはりDirectXが向いていると思います.ドキュメントやサンプルが非常に多いというのもメリットだと思いますのでどんどん新しい事を取り込んでいきたいという人にオススメ.


■今後は?

ここからはあくまでも私の予測ですが,OpenGLの最近の伸びは凄まじく,iPhoneなどのスマートフォン環境ではもはやスタンダードとなりつつあります.PS3などのコンソールゲーム機でも独自拡張のOpenGLが使用されている事を考えるとOpenGLを学ぶメリットも十分あると言えるので,新しく3Dに挑戦してみようかなという人にはオススメします.もちろんDirectXもまだまだ安定して使えるので,用途に応じて使いわけるのがいいと思います.

2011-01-17

タスクシステム解説

始めてまともなゲームプログラムっぽいネタかもしれない.

え〜,ゲームでよく使われている技術のひとつにタスクシステムというものがあります.ただ呼び方は今ひとつ定着していないらしく,単にタスクと読んだり,ゲームタスク,タスクコントローラ,珍しいところではジョブコントローラという呼び方もあったようです.日本以外では,Actor,GameEntity,GameObjectなどの呼ばれ方でタスクシステムという呼び方はしていないそうです.私はアクターという呼び方が好きですが,あえてここではタスクという言い方で統一しています.


■タスクシステムとは

簡単に言えば汎用的作業をひとまとめにしたものがタスクシステムと言われますが,その中は単なるリストなどで構成される配列やコンテナである事が一般的です.STLを使うならstd::listで実装されたものを使うのが一般的です.もちろん自前のリストやコンテナを使うというのもありです.boostには便利なライブラリがあり,タスクを作るのにも色々適しています.


タスクでやりたい事というのは様々ですが,主に以下のようなサブシステムがあります.

  • 周期的なリアルタイムアップデート
  • メッセージとイベント処理
  • Luaなどのスクリプトとの連携
  • ゲームフローと直結した一元の管理
  • タスク固有IDからのクエリー参照

ここでは実装自体に関しては解説しませんが,それぞれが一体どういう役割でどういうメリットがあるのかという解説をしてみたいと思います.実際にタスクとして実装する際には以下の記事などが役に立つかもしれません.


近代的タスクシステムの構築


■周期的なリアルタイムアップデート

どこかのタイミングで一斉更新をかけて,タスク自身の自律行動を行います.ここでは場合によっては描画も必要になるかもしれません.しかし描画と行動は切り分けておいた方がいいでしょう.例えば描画が常に行なわれていても行動は常に行なわれているとは限らないからです.


よくあるケースはゲームのポーズ画面です.ここでは描画だけを行い,全てのタスクの行動を停止させるかもしくは一部のタスクを除いて完全に処理を行わせないようにします.結果的にポーズ画面では絵だけが描画されている状態で,全く動きがない状態を作れます.これは逆手にとるとプレイヤーだけが動いて敵の動きを停止させるような攻撃を実装する際にも使う事が出来ます.とにかく描画と行動を切り分けておいて損するような事はないでしょう.


■メッセージとイベント処理

タスク同士での相互メッセージのやりとりが必要になるケースがあります.例えばボスが雑魚に対して攻撃命令を発する時や,人がドアを開けたりする際などそのケースは様々です.メッセージ自体を独自のイベントシステムとして作ってしまうのもありなんですが,イベントというのはとても数が多いものなので,タスク上で一元管理されていた方が何かと都合が良いものです.


実装自体は様々な方法がありますが,単純にタスク側へとメッセージが発生した際にコールバックとして呼び出す方法が最もわかりやすく,一般的な方法だと思います.イベントメッセージ用の定義リストだけを作って,イベントが増えるたびに登録していくようにしましょう.


■Luaなどのスクリプトとの連携

昔のタスクと現在のタスクとの最大の違いはここかもしれません.タスクの動作に対してフック出来るように作っておき,C++のコード上にLuaなどに代表されるスクリプト言語を使用出来るようにします.ゲームによっては行動部分の大部分をスクリプトで書く場合もありますので,重要な部分でもあります.ただ何でもスクリプト化すればいいというものでもなく,当然ボトルネックになるような処理が出てきた場合はC++のコードで書く必要もあります.


上手いスクリプトとの連携はゲームの開発効率と直結します.これが上手く出来ているだけで開発時間が半分以下になるというのも珍しくありません.タスクシステムと連携出来るスクリプトは非常に有用ですので自身の技術に自信があれば実装してみることをオススメします.


■ゲームフローと直結した一元の管理

ゲームシーンというのは様々な状況がありますので,それと連携出来る管理があれば便利です.例えば状況によって雑魚だけを全滅させたい,もしくは特定のタスクだけを生き残らせたいというのであればタスクの属性に無敵などのようなものをもたせておくと便利です.進行フローによってタスクの生死状態も様々なものが起こりえますし,万が一に死ぬような状態にならなくなってしまったタスクがでてしまった場合でも一律に処理出来たりします.(この場合はむしろそういう状態を作るべきではないですが)


タイトル画面には敵はいりませんし,エンディングでプレイヤーが動きまわるような事はまずありません.そういった場合でも抑制がかけられるかそういう機能があるだけでもバグがとても出にくくなり結果的に開発はよりスムーズに進められる事になるでしょう.


■タスク固有IDからのクエリー参照

タスク自身が持つ固有IDからタスク本体の情報を取り出します.またID以外の複合条件も使用出来ればより様々な状況に対して参照をする事が可能となります.相手の情報がわからないとAIを作る時などにどう行動すればいいのかわかりません.最低限の情報だけでも知るためにもこの機能は必ず必要となってきます.しかし膨大なタスクの中から特定のタスクを探しだすという作業は決して軽い処理ではないので場合によっては控える必要もあります.


スマートポインタやハンドルなどを用いて実装します.情報が知りたい側からは相手が生きているか確認する術がないからです.もし,削除されて開放されたポインタにアクセスしようものなら一瞬にしてゲームは停止します.これはごく当たり前の事ですが,これが原因でハングアップしているゲームはとても多いです.とにかく実装する際は注意してスマートポインタやハンドルの理解を深めておきましょう.


■その他の問題

特に生成と削除が多いタスクはメモリに優しくありません.ゲームによっては固定長の数しかタスクを作れなくしていたりして最初から静的なメモリしか使わないというケースもあります.メモリプールという固定長のメモリを動的に確保し,その中でタスクを回し続けるという手段もあります.とにかくメモリが少ないゲーム機では死活問題となり,メモリのフラグメント化は死への一直線なので注意する必要があります.PCで開発する際はそこまで気にする必要はないかもしれません.


タスクの数が異常に増えたりする際は複数のタスクリストやコンテナに分けた方が管理面でも速度面でも良くなったりします.特に大量に当たり判定の計算などをする際は注意が必要です.タスクの数が増える場合は多少面倒でも分けておいた方が後々の苦労は少なくなるでしょう.


タスクの並列化はなかなか魅力的な話ですが,簡単な事ではありません.もちろん不可能ではありませんので,速度を追求するとか,異常な数のタスクを走らせてみたい!!という方にはオススメしたいところです.ただ,今回はそれを語るところがないのでまたの機会に.少しだけなら以下の記事でも触れられています.


西川善司の3Dゲームファンのための「ロスト プラネット」グラフィックス講座


■タスクとの付き合い方

タスクシステムは決して必須なものではありませんが,ある程度大規模な開発をしようものならほぼ必須なものとなってきています.便利なものであればあるほど,開発の効率も上がり,チーム開発では作業の分担がしやすくなっていい事尽くめになってきますので,ぜひ難しく考えずに習得しておきたいところです.