2013年1月30日水曜日

NGUI 2.3.1 , TNet 1.1.0, 1.2.0, 1.3.0, 1.3.1

NGUIバージョンアップ情報です。なお、同じ作者がリリースしているTNetアセットも併せてリリース情報を載せます:

NGUI

January 27, 2013, 01:30:13 AM

2.3.1
- 新機能: UICamera.touchCount を追加
- 新機能: UIInput にモバイルで自動補正を有効にするオプションを追加
- 修正: Unity 3でのコンパイルを修正
- 修正: Font インスペクタによりプレビュー ウィンドウでフォントが表示されるように修正

TNet

January 27, 2013, 01:04:32 AM


1.1.0
- 新機能: 任意のプロパティを自動的に同期出来る AutoSync のスクリプトを追加
- 新機能: シーン開始時にサーバに素早く入室出来るAutoJoin スクリプトを追加
- 新機能: 上記の自動スクリプトをテストするシーンを追加
- 新機能: パケットを受信した時に、どのプレイヤーがオブジェクトを要求したのか分かるように機能を新規追加
- 新機能:  スクリプトのAwake関数で TNManager.isThisMyObject より自身ご作成したオブジェクトか判定することご出来る
- 新機能: 速度のあるオブジェクトをインスタンス化出来る機能を新規追加
- 新機能: ushort および uint データ型(およびその配列) に対するネイティブ 対応を新規追加.
- 修正: 特定のプレイヤーにデータを直接送信する際のバグを修正
- 修正: プレイヤーのアドレス解決時に無効アドレスによる例外エラーが発生する不具合を修正
- 修正: いくつかの通知の順番を変更。例えば "player left" (プレイヤーの退室) の時に新規ホストが必ず選ばれる

1.2.0
- 新機能: TNManager.CloseChannel を新規追加
- 修正: 単独ホストの時にTNManager.isHosting で発生していた不具合を修正
- 修正: 実行時にインスタンス化したTNAutoSync で正しく起動しない不具合を修正

1.3.0
- 新機能: 既存チャネルにランダムで入室する手段を追加
- 新機能: チャネルのプレイヤー数上限を制限する手段を追加

1.3.1
- 修正: Object IDの使用方法を統一。今後は全てuintデータ型 とした
- 修正: その他動作のマイナー修正
-----

うーん、NGUI作者はこれだけ頻繁にバージョンアップするアセットを持って、Unity Technologies社にも入社して働いて、一体いつ寝ているんだろう??

2013年1月29日火曜日

Unityで初めてのAIキャラクター(3.巡回パターンの追加)


さて、前回記事に引き続いて、今度は敵キャラ(NPCキャラクター)の衛兵が止まったり、周りを見渡したりする。

関連記事


では例のごとく、Unity Gemsからの翻訳をどうぞ!


November 22, 2012

巡回パターンの追加


前述の文章までにNPCキャラクターに巡回させる指令を実現してきた。さらに間で別のアクションを行うようにしれすることが出来る。AからB、BからC、と盲目的に移動させる代わりに(また繰り返しの動きかいなとユーザに思われる)、AからBへと移動し、そこで見回す動きをさせて、それから次の動きをさせることが出来る。次にDに着いたときに180度回転してまた180度戻ってさらにAに向かわせる。

NPCキャラクターの動作はあなたがどういう設計にするかに依存する。もしベンチが途中にあるならばわずかな間にNPCキャラクターに座らせてからまた動作を続けることも出来る。やりたければムーンウォークさせても良いし、これはあなたの想像次第だ(ゲーム全体の見栄えによる)。まあ、ムーンウォークはシリアスなゾンビゲームやコールオブデューティみたいなゲームでは酷いセンスになるので当然やめてもらいたい。

次に準備してもらいたいのは適切なタイミングにコールしたい関数のことだ。まずはFSMをみてみる。


前述の通り、普通の動きであり、ちょっとづつ工夫を凝らしてる感じだ。キャラクターは動くし、巡回点に到達して、どの地点にいるかによりアクションを開始させる。巡回点の2および3では何も行わない。

アクションはシンプルな回転、停止、戻る回転のアニメーションであり、代わりに好きなアニメーションを使用してもかまわないし、好きなステート(状態)を使用してもかまわない。待機のステート(状態)はアイドル状態のアニメーションで2秒間待機するIEnumerator関数となる。

次にコードを見ていく:

巡回パターンの短いコード(クリックして展開)

これでいくつかのboolean変数があり、キャラクターの状態を確認出来る。歩行、待機、回転のいずれかを行っている。updateにより各々のbooleanをチェックし、適切な関数がコールされる。Walk関数によりどの巡回点に到達しているかチェックしたうえで各々のbooleanを巡回点に基づいて定義する。残りはシンプルなコードなので見るだけで分かるはずだ。

この種のゲームは良く見かけるはずで、例えば入り口の衛兵がドアの前を前後に動き回り、たまに停止して周りを見回すか、停止をトリガーにしてオリジナルのアクションを行う。

Unityで初めてのAIキャラクター(2.巡回点の追加)


さて、前回記事に引き続いて、さっそく敵キャラ(NPCキャラクター)の衛兵がグルグルと何かを護衛している様子をコーディングしていく。

関連記事


では例のごとく、Unity Gemsからの翻訳をどうぞ!


November 22, 2012

巡回点(waypoint)の追加

ここで紹介するアイデアはシンプルなものでNPCキャラクターの移動に制限をかける。NPCキャラクターが移動出来る範囲をいくつかの地点に限定します。ビルの周りを巡回するだけの監視員を想像すると
いい。ビルの周りに4つの点を配置し、彼の仕事はそこの巡回だと伝え、AからB、BからC、CからD、最後にAに戻った後はAからBと再び続けてもらう。AIとしてベストでないが、この時点では賢いAIは必要ない。

それに加えて巡回点をマニュアル配置するため、もし高度の上下がある場合、地形の高さにあわせて配置する必要ごある。あとの動作はCCに任せるのみだ。
上図では巡回点AおよびBは異なる高さyにある。緑の線はCCが実際に移動するパス(経路)を表す。一定の重力を加えているのでCCがターゲットに向かいつつカーブの通りに移動する。

コードを見ていく前に、頭に入れておくべきこととして、短いグラフではこれで十分であるということだ。この例では巡回点は4つの配列だ。

巡回点には単純にタグをつけて GameObject.FindObjectsWithTag を使用することが可能だ。問題はUnityでは最初にどれが見つかるか、二番目に何が見つかるか保証されるロジックがないということだ。始めてから正しい順番が得られるように微調整するか、またはインスペクタで手動にて割り当てすることが可能だ。

オブジェクトのインスペクタで配列表示されるのでそこでスロットに差し込むのみだ。

手動割り当ての欠点は巡回点が何百とあると作業が煩雑になることだ。ここで別のチュートリアルで見ていくグラフが役立つところだ。ここではシンプルのままにしておこう。

巡回点の短いコード(クリックして展開)

見ていくと分かる通り、特にすごいテクニックを要するわけではない。NPCは常に往復を続けて、行動は極めて普通だが道行く歩行者としては十分だし、相互作用さえなければ問題ない。次のセクションではもうすこし生命力を足していく。
-----
次回は巡回パターンを変更してもっと動きを足していくぜ!

関連記事


Unityで初めてのAIキャラクター(1.最初にNPCの設定)


さて、前回記事に引き続いて、さっそく敵キャラ(NPCキャラクター)を設定する。いずれはプレイヤーを追いかけ回すようになる衛兵をイメージすると良いとおもう。

関連記事


では例のごとく、Unity Gemsからの翻訳をどうぞ!



http://unitygems.com/basic-ai-character/
November 22, 2012

最初にNPCの設定

NPCキャラクターを扱う上で真っ先にやると良いのはキャラクター コントローラ (以下、CC) の使用だ。これによりNPCキャラクターが壁をすり抜ける問題がシンプルに解決出来る。

キャラクター コントローラ(CC)は単に、Rigidbodyなしでもコリジョンを取得してくれる複雑なコンポーネントだ。プレイヤーを包み、コリジョンを検知して通知するカプセルから主に構成されている。

NPC キャラクターで CC を使用すればワールドに存在するコライダ全ての上にぶつかって乗ることが出来ます。CCがベストの解決策とならないようなケースも発生し、例えばマリオギャラクシーの世界を真似して重力が継続的に変化するような場合、上手く行かない。もしPrey (2006) のようなゲームを遊ぶ場合、キャラクターは壁を歩くことご出来るけどCCでは当然にできない。CCで大いなる問題の一つは物理挙動と連動されないことで、自身でスクリプト対応が必要となる。ただし多くのの場合はそのままでも十分だ。

NPCにアタッチして、最初にやることはCCをフェッチして修正出来るようにすることだ。コンポーネントをなぜ、どのようにしてフェッチするかについてはここを参照のこと。さらにオブジェクトがCCを保有するかどうかをチェックするため、クラスの前の属性を記述する。

二つのコンポーネントが相互作用する場合はRequireComponent属性を使用する。例えばスクリプトA がスクリプトBの関数または変数に依存するとして次のようにスクリプトA に追加する:
[RequireComponent (typeof (ScriptB))]
同様に物理計算のスクリプトがあったとして、Rigidbody コンポーネントご必要となる可能性が高い。

次のように記述する:
using UnityEngine;
using System.Collections;
 
// 以下の属性により、CCがオブジェクトにアタッチされていることを保証し、削除出来ないようになる
// 実際にオブジェクトからCCコンポーネントを削除を試みてどうなるか見ると良い
[RequireComponent (typeof (CharacterController))] 
public class AiScript : MonoBehaviour {
   CharacterController _controller;
   Transform _transform;
 
   void Start(){
       _controller = GetComponent();
      _transform = GetComponent();
   }
}
_controller 変数を用いてキャラクターコントローラを制御する。二行目は単に最適化のためのTransform コンポーネントのキャッシュだ。これでNPCが動くようになる。
float speed = 5f;
float gravity = 20f;
Vector3 moveDirection;
void Update(){
        moveDirection = _transform.forward;
    moveDirection *= speed;
    moveDirection.y -= gravity * Time.deltaTime;
    _controller.Move(moveDirection * Time.deltaTime);
}
まだ大したことはしなのでスクリプトを慌てて書き取り始める必要性はない。上記はほとんどUnityのCCドキュメントからのコピーで、それに追加する形でvector3型のmoveDirectionでオブジェクトのforwardを取得し、各メンバにspeedを乗算して、y メンバが一定の重力を受けてNPCがupdateのなかで実際に重力を受けます。最後にCCクラスからMoveメソッドをコールして、moveDirectionベクトルに渡す。Time.deltaTime を使用するのは単にゲームがフレームレートに依存しないようにするためだ。

現時点ではNPCは真っ直ぐ前方しか進まず、つまらないゲームでしか使用出来ない。面白くするためにはNPCが向きを変えられるようにする。次のチュートリアルによりこれは実現出来るため、ここでは記載内容をそのまま反映する。
float speed = 5f;
Vector3 _target;
   float maxRotSpeed = 200.0f;
   float minTime = 0.1f;
   float _velocity;

   void Update () {  
               //移動のためのコード  
       var newRotation = Quaternion.LookRotation(_target - _transform.position).eulerAngles;
       var angles = _transform.rotation.eulerAngles;
       _transform.rotation = Quaternion.Euler(angles.x,
           Mathf.SmoothDampAngle(angles.y, newRotation.y, ref _velocity, minTime, maxRotSpeed),
               angles.z);
        }
まだ辛抱する必要があって、このキャラにはまだ抜けている細かい設定が残っている。それでも、CC を動かす基本的な部分は出来た。ターゲットがないだけなのだ。忘れちゃいけないのは、このキャラは賢くない前提ということでこれからどこに向かうか指示したら単純にその通りに従うということだ(本当に、その通りにね)。次のパートではNPCがターゲットとして出来るように_targetに値を設定する。

可読性を高めるために、二つの箇所(移動と回転)をひとつのMove() 関数に変更する。

シンプルなスクリプトでNPCが動きまわれるようにする:
bool change;
float range;
void Start () {
   range = 2f;
   _target = GetTarget();
   InvokeRepeating ("NewTarget",0.01f,2.0f);
}
void Update () {      
   if(change)_target = GetTarget ();

   if(Vector3.Distance(_transform.position,_target)>range){
      Move();
      animation.CrossFade("walk");
   }else animation.CrossFade ("idle");
}
Vector3 GetTarget(){
   return new Vector3(Random.Range (0,300),0,Random.Range (0,300));
}
void NewTarget(){
   int choice = Random.Range (0,3);
   switch(choice){
      case 0:
         change = true;
         break;
      case 1:
         change = false;
         break;
      case 2:
         _target = transform.position;
         break;
   }
}
このAIには高度な思考能力をもたせないため、どこに移動するか知らせる必要がある。GetTarget() はこの目的で使用する。単純にターゲット変数にセットするVector3を戻り値とする関数だ。

InvokeRepeating関数にコールされるNewTarget関数 (関数実行の詳細についてはここを参照してほしい) は単純に新しいターゲットを割り当てたかどうか判断するのみだ。0 から 2 の間のランダムな数で次の態度を決めるスイッチとしている。

0. 新しいターゲットを取得
1. 前回のターゲットを継続
2. 止まって待つ
NPCキャラクターの動きはターゲットとの距離に依存する。もし十分近い距離にいない場合 (rangeより大きい場合)、NPCはターゲットに近づき、そうでない場合は待機する。2 の場合にターゲットに transform.position をセットするのはこのためであり、NPCキャラクターが十分近い場合は待機する。移動する際は歩行アニメーションが使用され、待機する、という場合はアイドル状態のアニメーションを使用する。

初めてのAIでのFSMは次の図のようなものである:
この図からDecision Center (NewTarget関数、判断を行う中心地点) で次の行動が決定されることが分かる。判断の際に歩くことを選択せずターゲットを、提供することのみ選択していることに注目してほしい。Update によりターゲットを使用してこの転換を行う。

この種のAIは "人間タイプ" のNPCでは不十分だが、動物タイプであれば十分だ。例えば森で彷徨うイノシシとして使用出来る。まだ問題はひとつあって、地形には自然環境による障害物はまだ作成していない。しかし、もし崖や池がある場合、そこにNPCキャラクターがいると不自然になります。これを回避するためにNPCキャラクターに事前設定したターゲットを設定する。

さらに、すぐ出てくるもうひとつの問題は地形の問題の解決だ。ランダムな値を割り当て、この場合はy を 0に保持しているため平らな地形でしかこのアルゴリズムが適用出来ないということだ。例えばy = 10で小さな丘があったとしても、現在のランダムの値どはキャラクターが0の地点から移動するには遠いため、永遠にターゲットが変更されないことになる。そうでなければ、範囲を変更することになりますが、あまり好ましいことではない。
----

次回は巡回する方法についてとりあげるぜ!


関連記事

Unityで初めてのAIキャラクター(2.巡回点の追加)

Unityで初めてのAIキャラクター(目次)

GGJ 2013が盛大に開催されるのをみたが、幅広く色々なゲームに全力を尽くしている姿を見られて一人のゲーム開発者として凄く勉強になった。またUnityだけで48時間内に世界で1000以上のゲームが出来たのも凄いことだと感じた。

筆者はというと、自宅観戦だったので久しぶりに長編のゲーム開発記事を書いてみた。

なお今回は「初めてのキャラクターAI」とタイトルにつけてるが、コーディングに自信のない方は読み進めるのをオススメしない。

もちろん「プログラムは余裕さ、早くAIやろうぜ!」という奇特な方がいたらそれはそれで心配であるが、中級程度の自信をもっているのであれば問題ない(はずだ)。

そんな前提をおいたうえでも、今回は最後まで読むとかなり長編シリーズとなる。気合を入れて読んでいただくことをオススメする!

http://unitygems.com/basic-ai-character/
November 22, 2012

敵キャラにもっと狡猾な動きをさせよう!NPCキャラクターのAI(人口知能)を始めるにあたってこのイントロダクションを読んで、関連するテクニックや手順をみていく。

初めてのキャラクターAI 


目的

初めてのゲームをこれから開発し、まあゾンビ ゲームかSlenderのようなゲームを、目指してるのだとしよう。最悪なことにゾンビのはずのキャラクターが木や壁をすり抜けてしまって、これではすっかりゴーストだ。ゾンビのコードとコンポーネントを見て見るけど、何も間違ってるように見えず、壁のロジックまで見てみる。まあ、何かが間違ってるので正しく動かないのだ。

このチュートリアルではNPCキャラクターを動きまわして、環境によって反応させ、FSMを導入する方法を見ていく。Unity Gemsにある宇宙船シューティング (Space Shooter)チュートリアルよりは発展的でありながらFSM チュートリアルよりはとっつきやすい。

目次

  1. 最初にNPCの設定
  2. 巡回点の追加
  3. 巡回パターンの追加
  4. デリゲートの使い方
  5. さあ近づいてこい(距離のチェック)
  6. 見えるようにする(視界のチェック)
  7. しゃがんで隠れる(隠れる場所を探す)  ※2013/2/11 元記事より削除

プロジェクトサンプルのダウンロード

http://unitygems.com/Downloads/AICharacter/AICharacter5.zip からダウンロード。(Unity Gemsサイト)


関連記事

Qiitaリンク

http://qiita.com/items/037fdfd167d7bba33cc2

2013年1月24日木曜日

NGUI 2.3.0 - Proのみ

NGUIバージョンアップ情報です:

http://www.tasharen.com/forum/index.php?topic=11.15
January 23, 2013 at 10:59:50 AM

(現時点では NGUI Proのみ対応 -- その他ライセンス向けは 1/26 か 1/27 リリース予定)。

2.3.0:
- 新機能: NGUIで事前反映アルファに対応し、関連するシェーダとともに新規追加
- 新機能: キーバインディングを容易に出来る様にUIButtonKeyBindingを新規追加
- 新機能:Transform インスペクタで複数オブジェクトの編集をサポートする機能を新規追加(Bardelot
- 新機能: UIRoot の automatic フラグを削除、より直感的なドロップダウンリストに変更
- 新機能:UIRoot をモバイルデバイスで固定長、デスクトップ では ピクセルパーフェクトと出来る機能を新規追加
- 新機能:全てのTween でアニメーションカーブを指定出来るように新規追加
- 新機能:Unity 4.1で反映された最適化と対応する機能を新規追加
- 新機能:カスタムデバイスからの入力を受受付できる UICamera.onCustomInput コールバックを新規追加
- 新機能:ローカライゼーションでロード時にスタート時の言語の読み取りを試みる機能を新規追加
- 修正:ユーザビリティをあげるためのローカライゼーションへの微調整。これでどんな場面でもローカライゼーションを活用してほしい
- 修正: 入力に使用するラベルにアタッチされた UILocalize はデフォルト値もローカライズするように修正
- 修正: Kerning の保存が正しく行われるように修正。フォント再インポートが必要となるため注意
- 修正: マルチタッチを無効にした UICamera でスリープ復帰後に発生していた不具合を修正
- 修正: ActiveAnimation の onFinished コールバックは全てのアニメーション ステートが完了するのを待機しないように修正(再生中の分のみを待機)。
- 修正: UICamera のタッチ検知でスリープ復帰後に発生していた不具合を修正
- 修正:スプライトのピクセルがズレる不具合に出来る限り対応する目的でMakePixelPerfect の動作を修正。
- 修正: UIPanel でクリップ矩形が正しく表示しない不具合を修正.
- 修正: UIInputSaved を submit した際に正しく保存されない不具合を修正
- 削除:混乱が最も多く生じていたため UIAnchor.depthOffset を削除。代わりにオフセットした子オブジェクトを使用してほしい
- 削除: 非常に多くのデバイスで課題が生じていたためハードクリッピングは廃止した。
----

4/23 修正
以下、Unity Pro -> NGUI Proに修正

NGUI Pro専用リリースは今回がはじめてだなー。

あと今回の修正の中では UIRootのautomatic をまた変更するようですね〜。

このブログでも何回か取り上げたけど、やはりマルチデバイスの対応としては要望が多いところなのでしょうね

Unityで活かせるLINQの底力!! (後編)

前回の「Unityで活かせる LINQの底力!!」ではLINQの基本的な使い方をみていった。

関連記事

Unityで活かせる LINQの底力!!(前編)
http://gamesonytablet.blogspot.com/2013/01/unitylinq.html

今回はUnityでLINQを使用すると、どんな風に便利であるか、Unity Gemsより翻訳を紹介したい:
http://unitygems.com/linq-1-time-linq/
Nov, 23, 2012


ドリルダウン


LINQ を使用して、リスト項目のメンバに当たるコレクションのコンテンツにドリルダウンすることが出来る。
前述の例ではタグが一致した全てのアイテムの transform を戻していた - もし全てのレンダラを下の階層分も全てほしい場合はどうすれば良いだろう?ここで SelectMany が役立つ:
//C#

var transformArray = GameObject.FindGameObjectsWithTag("MyTag")
    .SelectMany(go => go.GetComponentsInChildren())
    .ToArray();

//Javascript

var transformArray = GameObject.FindGameObjectsWithTag("MyTag")
    .SelectMany(function (go) go.GetComponentsInChildren(Renderer))
    .ToArray();
SelectMany により、戻り値のコレクションの集合を連結してひとつのコレクションに入れるため、SelectMany を実行した後は全てのアイテムの全てのレンダラが含まれる。

Linq の中にLinq

ここで仮に前述の例で期待どおりでなくて、レンダラはあくまでオブジェクト自身と直接の子オブジェクトのみが本来は取得したかったとする - これも簡単なことだが、Linq の中にLinqを使用することになる。
//C#

var transformArray = GameObject.FindGameObjectsWithTag("MyTag")
    .SelectMany(go => go.transform.Cast()
       .Select(t=>t.renderer)
       .Concat(new [] { go.renderer })
       .Where(r=>r!=null)
     )
    .ToArray();

//Javascript

var transformArray = GameObject.FindGameObjectsWithTag("MyTag")
    .SelectMany(function (go) go.transform.Cast.()
         .Select(function (t) t.renderer)
         .Concat([go.renderer])
         .Where(function (r) r != null)
     )
    .ToArray();
Transform を取得して Transform のコレクションとなるように 型キャストする(Unity で変な仕様と感じるところだが、全ての子オブジェクトも取得出来るためにデフォルトでは型が異なる)。次に各々の子オブジェクトのレンダラを選択し、親オブジェクトのレンダラを格納する配列に列挙体を連結していき、次の行でnull でないものだけにこの処理を限定している。

この作業もこれで完了だ。

最も近いオブジェクトを見つける

最も近いオブジェクトを見つける方法はいくつかあるが、まずは効率悪い方法からお見せする:
//C#

var closestGameObject = GameObject.FindGameObjectsWithTag("MyTag")
    .OrderBy(go => Vector3.Distance(go.transform.position, transform.position)
    .FirstOrDefault();

//Javascript

var closestGameObject = GameObject.FindGameObjectsWithTag("MyTag")
    .OrderBy(function (go) Vector3.Distance(go.transform.position, transform.position))
    .FirstOrDefault();
次にコレクションを距離の順に並べて FirstOrDefault を取得する。次にオブジェクトが一覧にない場合、FirstOrDefault はnull を戻す - First を使用することも出来るが、アイテムがない場合は例外を投げることになる。

ここで問題は、リストのソートが必要だということだ - 最も近いオブジェクトを取得するだけのことに時間が多くかかってしまう。

ジェネリック関数の Aggregate を使用して、それを回避するように指示できる。
var closestGameObject = GameObject.FindGameObjectsWithTag("MyTag")
   .Aggregate((current, next)=> Vector3.Distance(current.transform.position, transform.position) < Vector3.Distance(next.transform.position, transform.position)
      ? current : next);

//Javascript

var closestGameObject = GameObject.FindGameObjectsWithTag("MyTag")
     .Aggregate(function(current, next) Vector3.Distance(current.transform.position, transform.position) < Vector3.Distance(next.transform.position, transform.position)
        ? current : next);
これはリストを一回だけ実行し、各ステップ毎に二つの候補のアイテムのうち近い方を戻す。リストは一回だけ渡すため何回も処理は行われなくて済む。C# では無名関数を使用して最適化出来る(JavaScript では上手く動作しないのでここでは割愛する)。
//最適化した C#
var currentPos = transform.position;    //高価なのでキャッシュして節約
var closestGameObject = GameObject.FindGameObjectsWithTag("MyTag")
   .Select( go => new { go = go, position  = go.transform.position })
   .Aggregate((current, next)=>
      (current.position - currentPosition).sqrMagnitude <
      (next.position - currentPosition).sqrMagnitude
      ? current : next).go;
最初のSelectでは二つのメンバつきの新しいクラスを作成した - その時点で列挙体に入っているのはそれだけだ。次にキャッシュされた位置を使って近い方を見つける(高価な平方根演算を回避して sqrMagnitude を使用)。最後に近いオブジェクトが得られるが、無名クラスになっているので、go 変数を使用してゲームオブジェクトに変換する。

リストおよびディクショナリ

ところで作成が出来るのは配列だけでなく、.ToList() を使用してリストを作成できるし、ディクショナリだって出来る!!

ディクショナリは明確にキーも持っているのでどうやって作成するか見ていくとする。では、シーンにある全てのタグつきのゲームオブジェクトを見つけて、そのタグに基づいてディクショナリに格納する :
//C#

var lookupByTag = GameObject.FindObjectsOfType(typeof(GameObject))
     .Cast()
     .Where(go=>!string.IsNullOrEmpty(go.tag))
     .ToLookup(go => go.tag);

//JavaScript

var lookupByTag = GameObject.FindObjectsOfType(GameObject)
    .Cast.()
    .Where(function (go) go.tag != "")
    .ToLookup( function (go) go.tag );
これにより特殊なディクショナリが作成され、そこで lookupByTag["タグ名"] によりゲームオブジェクトの一覧にアクセス出来るようになる。例えばゲームオブジェクトまでの距離を示すか通常のディクショナリを作成することが出来る(ゲームオブジェクトを関数の引数にするとターゲット地点までの距離を戻す)。いくつものゲームオブジェクトで頻繁に計算が実行される場合にも十分に速い処理だ。
//C#

var objectToDistance = GameObject.FindObjectsOfType(typeof(GameObject))
     .Cast()
     .ToDictionary(go=>go, go=>Vector3.Distance(go.transform.position, transform.position));

// ゲームオブジェクトの距離を後に取得する時:

var distance = objectToDistance[someGameObject];
ToDictionary の最初の引数がキーであり、二つ目が必要とするディクショナリの値であることに注目してほしい。これはDictionary<GameObject, float> を戻す。

結論

望むらくはこの紹介記事により Linq を使用する 際のテクニックが身に付き、リスト編集のコツが掴めてもらっていれば何よりだ。知っておくと便利なことは他にもあるので、それは次の機会に検証することとしたい!!

回答


  • 一行のコードで、ターゲットに最も近い準備で、5つのタグつきオブジェクトのレンダラのマテリアルを取得するのが信じられないひとつは次のコードをとくとご覧あれ!!

var materials = GameObject.FindGameObjectsWithTag("sometag").Select(r=>r.renderer).Where(r=>r!=null).OrderBy(r=>(r.transform.position - transform.position).sqrMagnitude).Take(5).SelectMany(r=>r.materials).ToList();

foreach(var m in materials) m.color = Color.red;


  • 最後に: 英語の文章で5 連続でand使っていみが通じるのは?

"Pig and Whistle" という飲み屋の看板をペンキ屋が描いてるところへ、地主が一瞥して一言:
 "You need to leave more space between the Pig and 'and' and 'and' and Whistle."
「Pig と and、andとWhistleの間の隙間が足りないな」

翻訳注: 5つもandが並んでるのが言いにくくてたまらない言葉遊び。まあ英語版「寿限無」の呪文とおもって笑えたらあなたもネイティブだ!! 「いや、意味分からん」とおもった方・・・まあ、外国のジョークはそんなもんだ(笑)
-------

筆者は正直、自分でLINQを使ったことはなかったのだが、Asset Storeのサンプルプロジェクトに入っていて、どんなものであるか調べていくうちに本記事をみて、なかなか便利なモノだと感じたが、皆さんはどうだろう。

いろんな見解を共有しあって、理解をどんどん深めていこうぜ!

2013年1月23日水曜日

3Dプリンタの未来が感じられるイヤリング

CGを彼女への素敵なイヤリングのプレゼントに出来る時代が来た!!

ここで紹介するCGを作成したRobert Cupiszさんと、それを3Dプリンタで素敵なイヤリングにしたAras Pranckevičiusさんはそれぞれの業界ではちょっとした有名人。

眺めてるだけで未来が感じられるなー

スマホからみてる人はCGがロードするまで時間かかるけどゴメンね!でも見る価値あるよー^^

フラクタルのカッコいいCG

Robert Cupisz フラクタル
http://www.iquilezles.org/www/articles/juliasets3d/juliasets3d.htm








3Dプリンタでイヤリングにした後 

Aras's website 3Dプリンタで冒険!




Unityで活かせるLINQの底力!! (前編)

最近、アセットストアでダウンロードしたコードに たまたま、LINQ を活用しているものがあって興味をもった。

そして「なるほどLINQ で書くと美しく見やすいコードを書ける場合があるのだ!」と素直に感じた。

調べてみたら、一般論としてUnity でどのような場合に活用すると良いか、Unity Gems に良い記事があったので2 回の投稿に分けて翻訳を紹介させてもらいたい。

長文ゴメン!!でもLINQをUnityでどう活用するか知らなかった人は覚えとくと、きっと役立つぜ!
http://unitygems.com/linq-1-time-linq/
Nov, 23, 2012

次の場合、この記事を読むとピッタリだ!

  • GameObject.FindGameObjectsWithTag() を使用して、ゲームオブジェクト自身でなく、それにアタッチされる全ての Transform を見つける方法がほしい場合
  • リストや配列の操作、ソート、及び変更を行いたいけれども、どうもコード量が余分に増えがちである場合
  • LINQ は聞いたことがあるけれど、仕組が良く分からない場合
  • 複雑な条件(あるターゲットに最も近い5つのタグつきオブジェクトのレンダラのマテリアルを全て)を戻り値とする条件を(あるいはもっと複雑な条件も)、一行のコードだけで出来ることを信じられない場合

上のトンチに対して、あなたが頭の中で「一行のコードでどうやって??」と悩んでる間に一緒に次の問題を考えることにしたい。「英語の文章で and が5連続で入って文法的に正しいものはどういうものだろう?」。想像つくだろうか。この答えは、文末にこの主題と完全に関連ない形で文末に載せてある(ズルして先に見ないでね)。 ※翻訳ブログでは次回記事


はじめに

LINQ とは Language Integrated Native Query の略称であり、.NET上でオブジェクトでも動作するデータベースのクエリの働きをするものである。強力であり、内容の理解が進むと非常に使いやすい。

LINQ は全ての for next ループを書き出すのに比べて遅い場合があります。半分ぐらいのスピードしか出ない場合があるため、毎フレーム行われる Update関数では慎重に使用されるべきだ。一方、キャッシング、プロセッシングでは優秀だ。

C# では実際に SQL 文に近い記述が出来る。
var transformsWithTag = from go in GameObject.FindGameObjectsWithTag("YourTag") 
     where Vector3.Distance(go.transform.position - transform.position) < 100
     select go.transform;
しかし関数ベースのアプローチがより強力かつ個人的に可読性が高いとおもわれるため、そちらを使用して行く。

列挙体の使用

Linq は列挙体を使用して既存の列挙体のコレクションを列挙しつつ、変更もかけていくものだ。このため Linq は配列、リスト、ディクショナリ、さらにあらゆるコレクションであるもので全て使用できる。Linq は列挙体を戻すために、これを繋ぎ合わせることができることは注目に値し、それゆえに強力だ。

Linq によりコレクションを編集する一連の関数を書くことが出来て、自身のコードを無名関数の形式で提供出来る。

早速始めてみよう!

LINQの有効化


Linq を使用するためには import または using ディレクティブをソースコードに追加する必要がある。
//C#
using System.Collections.Generic; //足しておくといつでも便利
Always a good idea
using System.Linq;

//JavaScript
import System.Linq;
有効化はこれだけで完了!

はじめてのLinqステートメント

まず簡単なことから始めよう - GameObject.FindGameObjectsWithTag で戻されるゲームオブジェクトと紐付けされた Transform の配列を作成してみる。

  • 全てのゲームオブジェクトを見つける
  • ゲームオブジェクトの Transform を選択
  • 結果を配列に変換

次のようなコードになる :
//C#

var transformArray = GameObject.FindGameObjectsWithTag("MyTag")
    .Select(go => go.transform)
    .ToArray();

//Javascript

var transformArray = GameObject.FindGameObjectsWithTag("MyTag")
    .Select(function (go) go.transform)
    .ToArray();
実現したいセレクション処理に無名関数を使用していることに注目してほしい。Select により列挙体(ここではゲームオブジェクト)の中身を取得し、適当な名前をつけて(ここでは go )、何らかの計算結果を戻して(ここでは transform )、ToArray が処理全体を実行したうえの結果を配列に格納します。

C# ではラムダ関数: go => go.transformを使用し、JavaScript では無名関数: function(go) go.transform を使用する。

さらに、、、この関数はひとつのステートメントに限定されない! { and }  をいれて好きなだけの量のコードを記述出来る。この点はまた後ほど説明する。

クエリ実行

次のトピックはクエリだ - 問い合わせを行うところから始めたい。現在の位置からワールド座標で 10 単位ほど離れたオブジェクトの Transform を全て取得して見よう。
//C#

var transformArray = GameObject.FindGameObjectsWithTag("MyTag")
    .Select(go => go.transform)
    .Where(t => Vector3.Distance(t.position - transform.position) < 10
    .ToArray();

//Javascript

var transformArray = GameObject.FindGameObjectsWithTag("MyTag")
    .Select(function (go) go.transform)
    .Where(function (t) Vector3.Distance(t.position - transform.position) < 10)
    .ToArray();
ここでは Where 関数を select の後に入れた - このため列挙体はすでに Transform の列挙体である - Where無名関数は真または偽を返す必要があるため、列挙体のTransform と現在のTransformの間の距離を計算する簡単なテストを行う。

ソート実行

Danger という名前のスクリプトが、オブジェクトにアタッチされているとして、このスクリプトには dangerLevel という float 変数を含むものとする。次に、Transform のリストをこの dangerLevel によってソートしていく(ここでは dangerLevel が範囲内にない場合は危険性がなく対象外という意味とする)。
//C#

var transformArray = GameObject.FindGameObjectsWithTag("MyTag")
    .Select(go => go.transform)
    .Where(t => Vector3.Distance(t.position - transform.position) < 10
    .OrderByDescending(t => {
       var danger = t.GetComponent();
       return danger ? danger.dangerLevel : 0;
      })
    .ToArray();

//Javascript

var transformArray = GameObject.FindGameObjectsWithTag("MyTag")
    .Select(function (go) go.transform)
    .Where(function (t) Vector3.Distance(t.position - transform.position) < 10)
    .OrderByDescending(function (t) {
          var danger = t.GetComponent(Danger);
          return danger ? danger.dangerLevel : 0;
         })
    .ToArray();
ここでは OrderByDescending を追加しました(降順に並べます)。最初にコンポーネントを取得して、次にその値を使用するために複数行に渡る関数を記述した。簡単なことであるのごお分かり頂けただろうか?通常の関数みたいに扱うことが出来る。

ここまでで十分イケてることは分かった - だけど、距離順 - 危険度順に並べて距離も重要なパラメータとして扱うにはどうすべきだろうか?
//C#

var transformArray = GameObject.FindGameObjectsWithTag("MyTag")
    .Select(go => go.transform)
    .Where(t => Vector3.Distance(t.position - transform.position) < 10
    .OrderByDescending(t => {
       var danger = t.GetComponent();
       return danger ? danger.dangerLevel : 0;
      })
    .ThenBy(t => Vector3.Distance(t.position, transform.position)
    .ToArray();

//Javascript

var transformArray = GameObject.FindGameObjectsWithTag("MyTag")
    .Select(function (go) go.transform)
    .Where(function (t) Vector3.Distance(t.position - transform.position) < 10)
    .OrderByDescending(function (t) {
          var danger = t.GetComponent(Danger);
          return danger ? danger.dangerLevel : 0;
         })
    .ThenBy(function (t) Vector3.Distance(t.position, transform.position)
    .ToArray();
ThenBy および ThenByDescending により何個でも連結が出来る(ただし sqrMagnitude でも同じ結果が得られて、パフォーマンス観点からは平方根計算も省けるそちらを使用すべきであるが、分かり易くするためだけが理由で、ここでは Vector3.Distance とした)。
---

次回に続くぜ!

2013年1月16日水曜日

Unityメモリ管理「ヒープのフラグメンテーション、オブジェクトプール、およびガーベージコレクション」

オブジェクトプールの利便性についてはこのブログでも過去に何度か取り上げてきた。

関連記事




今回の記事では

  1. Unity Gemsならではのオブジェクトプールに関するまとまった考え方
  2. 記事の一番最後にダウンロードできるサンプルのUnityプロジェクト

があるので是非そちらも試してみて欲しい。

なお、筆者環境ではサンプルシーンのうち1つだけがUnity4.0でうまく動作しなかったが、オブジェクトプールをプログラミングせずに雰囲気だけでも知るのに良いサンプルだとおもう!

-------
http://unitygems.com/memorymanagement/

October 21, 2012

ヒープ フラグメンテーション、ガーベージコレクション、およびオブジェクト プーリング


ヒープは大きなメモリのゾーンであり、データがランダムに格納する。実際にはそれほどランダムだなく、OSはメモリをみて必要なデータを当てはめるのに十分な最初のメモリゾーンを探す。ヒープのサイズおよび場所は選択したプレイヤーのプラットフォームにより幅がある。

例えば、整数をリクエストすると、OS は 4 バイト連続で空きメモリの箇所を探す。実際のところ、プログラム実行に伴い、メモリのロケーションは実際のロジックなしに使用および解放され、自身のプログラムはヒープのフラグメンテーションが生じる可能性がある。

図でヒープ フラグメンテーションの例を示す。明らかにこれは端的に誇張しているが、考え方は変わらない。data3 を割り当てているが、 state1 では state2 で破棄された data3 があった。
state1 と最後で 同じ量のデータあり、データは追加されていないことに留意してほしい。動作によってヒープ フラグメンテーションが作成されている。

Unity は .NET 管理メモリを使用する。 C プログラムではヒープ フラグメンテーション により、空きメモリ自体は十分にあっても十分な大きさの連続ブロックが見つからないために、メモリブロックを割り当てることが不可能な状況が作り上げられることがあった - 管理メモリはこの制約がない。もしメモリのブロックが .NET 管理メモリで見つからない場合は、ガーベージコレクション システムが使用されてアイテムを移動してヒープのフラグメンテーションを取り除く。これは当然ながら、時間消費の大きい演算であり、継続的に高いパフォーマンスを求められるゲームにおいて顕著なフレームレート低下を引き起こす可能性がある。

プログラマにとっての解決策はオブジェクト プーリングの概念を使用することだ。もし date3 を state1 で破棄する代わりに、後で使用できるように無効にしたらどうだろう。それによってヒープ フラグメンテーションが避けられたはずだ。

データを破棄せず、スリープさせて保持し、必要なときに揺り起こす。

この解決策には多くの利点があり、ひとつめとして前述のケースが回避でき、二つめとして Instantiate および Destory 関数のコールを回避できて gameObject.SetActiveRecursevely (true/false); を使用するのみだ。

最後に、破棄をしないため、高価な処理であるガーベージコレクションが行われない。
ObjectScript.cs
using UnityEngine;
using System.Collections;
 
public class Test : MonoBehaviour {
    public GameObject prefab;
    GameObject[] objectArray = new GameObject[5];  
        public static int counter; 
 
         void Start(){
        for(int i = 0;i < objectArray.Length;i++){
            objectArray[i] = (GameObject)Instantiate(prefab,new Vector3(0,0,0),Quaternion.identity);
            objectArray[i].SetActiveRecursively(false);
        }
        }
        void Createobject(){
        if(counter < objectArray.Length){    
                // 配列の中を反復
            for(int i = 0;i < objectArray.Length;i++){  
                // 無効なオブジェクトを探す
                if(!objectArray[i].active){    
                    // カウンタを増加させ、オブジェクトを有効化し、位置を決定
                    counter++;                                              
                    objectArray[i].SetActiveRecursively(true);             
                    objectArray[i].transform.position = new Vector3(0,0,0);  
                    return;
                }
            }return;
        }else return;
    }
}
void OnCollisionEnter(CollisionEnter){
    if(other.gameObject.tag=="Object"){
        other.gameobject.SetActiveRecursively(false);       //オブジェクトを無効化
        ObjectScript.counter--;                             //カウンタを減らす

シーンには同時に 5 オブジェクト以上はなく、ひとつが無効化されるとどこでも再度有効化できる。ガーベージコレクションは必要でない。

このテクニックを敵に使用できる。もし敵の波が向かってきたときに、やっつけた敵を破棄するかわりに、その敵を後ろの順番ににまわして、そのうち先ほどと同じ敵を気付かずにやっつけるようなことになる。

弾数についても同様で、あなたまたはNPCキャラクターが撃った弾丸を破棄するかわりに、無効化する。もう一回撃つときに銃口の前の同じ位置と正しい速度で有効化すればよい。

メモリ レベルでのプーリングはOSにより管理される別の処理であり、オブジェクトのサイズに関わらず同じ量のメモリを予約する。結果的にメモリ上に小さな空きがたくさん出来ることは防げる。

配列の再利用


オブジェクトの再利用に加えて、自身のコードにより異なるコールや同じコールの中で何回も再利用されるバッファを作成することは検討の価値がある。これはつまり、配列または List はヒープ上に割り当てられ、そして Update毎または何らか頻繁に発生する状況で、配列を作成することはガーベージコレクションのサイクルにつながってしまう。

void Update()
{
      if(readyToFire && Input.GetKeyDown(KeyCode.F))
      {
          var nearestFiveEnemies = new Enemy[5];
 
          // 最も近い敵をみつけて配列の
          // リストを埋める動作を行う
 
          TargetEnemies(nearestFiveEnemies);
      }
}

この例ではもっとも近い 5 人の敵をターゲットとしていて、自動小銃で撃っています。問題は発射ボタンを押すたびにメモリを割り当ててることだ。もしその代わりに生存期間の長い、コードのどこででも使用できる敵の配列を作成すれば、メモリを割り当てる必要がなく、結果的にスローダウンを防止できる。

次がもっともシンプルなケースとなる:
Enemy[] _nearestFiveEnemies = new Enemy[5];
 
void Update()
{
      if(readyToFire && Input.GetKeyDown(KeyCode.F))
      {
          // 最も近い敵をみつけて配列の
          // リストを埋める動作を行う
 
          TargetEnemies(_nearestFiveEnemies);
      }
}
しかしもしかすると、敵の配列が頻繁に必要であればもっと汎用的とすることができるかもしれない。
Enemy[] _enemies = new Enemy[100];
 
void Update()
{
      if(readyToFire && Input.GetKeyDown(KeyCode.F))
      {
          // 最も近い敵をみつけて配列の
          // リストを埋める動作を行う
 
          TargetEnemies(_enemies, 5);
      }
}
TargetEnemies のコードを書き直してカウントをとるようにすることで、効率的に一般的な目的でコードのどこからで使用できる、敵のバッファを使用して、さらなる割り当ての必要性を回避できる。

二つめの例はかなり極端なケースで、これは本当に大きくメモリ問題を引き起こすようなデータコレクショがある場合に限るべきだ - 一般的には数バイトのメモリ節約よりもつねに読みやすく、分かりやすいコードを優先すべきだ。
static 変数およびメモリ管理のデモのビデオをふたつ用意した。

このビデオで使用されたプロジェクトはここからダウンロードできます。


----

長らく翻訳してきたUnity Gemsメモリ管理についてはこれでひととおり完了だ。

機会があればまとめ記事として整理してみるぜ!

2013年1月12日土曜日

Unityメモリ管理「Static関数」

前回のUnity Gems記事に続いて、Unityメモリ管理「Static関数」について翻訳を紹介する:

-------
http://unitygems.com/memorymanagement/

October 21, 2012

---------

Static 関数


Static 関数は非Static クラスで実装することが出来て、この場合クラスの Static メンバのみが関数の中でアクセス出来る。static 関数はクラスをinstanceでなくclass経由でコールされ、存在してないかもしれないメンバをアクセスするのは不具合のもとであるため理屈に合う。

逆に、引数を渡すことは可能であり、この種の関数を使用することは良くあるはずだ。
Vector3.Distance(vec1.vec2);
vec1.Normalize();
 
Application.LoadLevel (1);
 
if (GUI.Button(Rect(10,10,50,50),btnTexture)){}
このリストはどこまでも続けることが出来る。二つめの例は static メソッドでないことに留意してほしい。Vector3 型 の vec1インスタンスを通してコールされる。その他の例は static 関数をコールするためにクラスが使用されることを示す。

Static 関数は非Static関数よりも速い傾向があり、それはインスタンスの関数のコンパイラのコールはインスタンスの存在チェックでわずかなオーバーヘッドが生じるためであり、static 関数の場合はクラスが存在することが保証されている。ただし、この時間はきわめて小さな差だ。

もうひとつの Static クラスの長所はその存在が永続的でコンパイラに既知であるため、C# では、拡張メソッドを定義するために使用できることだ - これらは、どのような型の変数にでも追加できる関数のようなメソッドだ。これも static の良いところで、この方法で拡張したクラスは実際には開かれておらず(private および protected 変数は拡張メソッドで利用可能でない)、しかし、既存オブジェクトに新しい関数を提供するのに良い方法であり、開発者にとってコードが読みやすく、より簡単なAPIにつながる。例えばTransform に新しい関数を追加できる。
transform.MyWeirdNewFunction(x);

拡張関数の例:

既存のものを拡張する配列のための関数を作成したいとして(順に並べる、ソートの関数、等)、static 関数を宣言してこのリストを拡張することが出来る。
using UnityEngine;
using System.Collections;
 
public static class ExtensionTest {
 
    public static int BiggestOfAll(this int[] integer){
        int length = integer.Length;
        int biggest = integer[0];
        for(int i = 1;i < length;i++){
            if(integer[i] > biggest)biggest = integer[i];
        }
        return biggest;
    }
}
static 関数を宣言していて、渡された引数が関数をコールしているインスタンスであることを確認して下さい。この引数の型を修正することは他のオブジェクトで作業することを可能にします。

次に配列を宣言して値で埋めます。
using UnityEngine;
using System.Collections;

public class Test : MonoBehaviour {

        int[] array = {1,25,12,120,12,6};

    void Start () {
        print(array.BiggestOfAll());
        }
}
配列名のドットを入力すると、自身の新しい関数が候補として自動表示されるはずだ。

新しくより汎用的な、型に関わらず配列を受け入れる関数を作成すると決めた場合はstaticなジェネリック拡張メソッドを実装する。
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
 
public static class ExtensionTest {
 
    public static T TypelessBiggestOfAll <T> (this T[] t){
        int length = t.Length;
        var biggest = t[0];
        for(int i = 1;i < length;i++){
            biggest =((Comparer <T>.Default.Compare(t[i], biggest) > 0) ? t[i] : biggest);
        }
        return biggest;
    }
}
using System.Collections.Generic; が追加されていることに留意してほしい。コンパイラはTの型が分からないため、値を < または > で単純に比較できない。しかし Comparer<T> 使用できる。例では、3つ組の演算子が使用されている。始めのうちはコーディングが少し難しいかもしれないが、行数を何行か節約できる。
using UnityEngine;
using System.Collections;

public static class GameManager{

    public static void ClampTransform(this Transform tr,Vector3 move, Vector3 min, Vector3 max){
        Vector3 pos = tr.position + move;
        if(pos.x < min.x ) pos.x = min.x;
        else if(pos.x > max.x) pos.x = max.x;

        if(pos.y  < min.y ) pos.y = min.y;
        else if(pos.y> max.y) pos.y = max.y;

        if(pos.z < min.z) pos.z = min.z;
        else if(pos.z > max.z) pos.z = max.z;

        tr.position = pos;
    }
}

3つ組の演算子は次のように表現される:       a ? b : c;
a が実行され、もし true ならば b がコマンド実行され、もし false ならば c がコマンド実行される。通常の if 文と比較した長所は 比較から戻された値にある。
       result = a ? b : c;           結果は a にもとづいて値 b または c を受け取る
これにより配列の異なる型を宣言して、同じ関数を使用することが可能だ。
using UnityEngine;
using System.Collections;

public class Test : MonoBehaviour {

    int[] arrayInt = {1, 25, 12, 120, 12, 6};
    float[] arrayFl = {0.5f, 52.456f, 654.25f, 41.2f};
    double[]arrayDb = {0.1254, -15487.258, 654, 8795.25, -2};

    void Start () {
        print(arrayInt.TypelessBiggestOfAll());
        print (arrayFl.TypelessBiggestOfAll());
        print (arrayDb.TypelessBiggestOfAll());
       }
}

これにより 120, 654.25, 8795.25 の値が出力される。

次にこれが便利となる事例を示したい。多くの Unity ユーザはオブジェクトを領域内に制限する方法を聞く。例えば、オブジェクトを画面の範囲内に保持したい場合、次の関数を使用してオブジェクトの Transform を はさむ(Clamp)することができる。
そしてこのようにして必要なところでどこでも使用することができる:
Vector3 minVector = new Vector3(-10,0,-10);
Vector3 maxVector = new Vector3(10,0,10);
transform.ClampTransform(move,minVector,maxVector);
この例では、オブジェクトは平面(y が 0 で固定して制限されていて、 (0,0,0) を中心に 20 x 20 の箱の中に収められる。
------

2013年に入ってもUnityの学習を積極的に進めようぜ!

2013年1月8日火曜日

Unityメモリ管理「Static変数」



前回のUnity Gems記事に続いて、Unityメモリ管理「Static変数」について翻訳を紹介する:

-------
http://unitygems.com/memorymanagement/

October 21, 2012

Static変数


多くの初心者は次の簡単な間違いをしている:
public static int health;
void Start(){
    health=100;
}
別のスクリプトでは次のことを見つけるかもしれない。
void OnCollisionEnter(Collision other){
    if (other.gameObject.tag == “Enemy”){
        EnemyScript.health-=10;
        if(EnemyScript.health <=0)Destroy(other);
    }
}
ゲームを一つの敵で初めて殺すことに成功する。しかし次に敵を追加してみて、なぜ同時に殺されるのだろうと悩むことになる。

理由は簡単で、enemyクラス(staticでない)のインスタンスがたくさんあるがhealthクラスが全てのインスタンスが1つしかないためだ。ひとつ殺すことで全部殺されてしまう!

実際、クラスで宣言されたStatic変数はオブジェクトに属しておらず、クラス自身に属する。

クラス名でなく、インスタンス名を使ってアクセスするのもこの理由のためだ。enemyのhealthについて機能しなかったのは、各enemyが各々のhealth変数を持つ必要があるためだ。

Static変数はStaticクラスと同じように、それを定義しているクラスが始めてアクセスされたときにインスタンス化される。つまり、ゲームでそのクラスのインスタンスがないにも関わらず、もしアクセスしようとすれば、そのクラスは存在することがある。

例えばenemyのカウンタ(counter)を作るとしてゲームでのenemyの数をトラッキングしていく必要があるとする。
static var counter;
 
public void CreateEnemy(){
    GameObject obj=Instantiate(enemyPrefab, new Vector3(0,0,0).Quaternion.identity);
 
    if(obj)counter++;
}
 
public void DestroyEnemy(){
    Destroy(gameObject);
    counter--;
}
このスクリプトは全ての敵にアタッチする各々別のスクリプトであっても全てが同じカウンタ変数を共有する。 この関数をゲームで使用することで、enemyのカウントをトラッキングすることが出来る。インスタンスがオブジェクト作成に失敗した場合を考慮して(例えばヒープのフラグメンテーションによって)参照の偏すが使用されていることをに留意する必要がある。失敗した場合Instantiateはhealthへの参照を返す。もしこのチェックをせずにカウントを増やしてしまうと、何らかの理由でインスタンス化に失敗した場合、カウンタが誤った値になる。もしカウンタをレベルの終わりに使用して新しいレベルロードすると:
if(counter<=0)Application.LoadLevel(nextLevel);
このチェックがなければ、失敗した場合、全ての敵を殺した後カウントが1となってゲームにバグが生じることになる。
ところでプレイヤーについてStatic変数のhealthを使用することは出来るのだろうか。答えとしては、プレイヤーにStatic変数を使用することが出来る。実際にStatic変数にアクセスするのはインスタンスメンバをアクセスするよりも効率的だ。
インスタンスメンバをコールするとき、コンパイラは小さな関数をコールしてオブジェクトの存在チェックを行う必要がある。存在しないデータを修正できるべきではないので、これは当然といえる。
-------

Unityメモリ管理の基礎を積み重ねていこうぜ!

2013年1月7日月曜日

Unityメモリ管理「Staticクラス」


前回のUnity Gems記事に続いて、Unityメモリ管理「Staticクラス」について翻訳を紹介する:

-------
http://unitygems.com/memorymanagement/

October 21, 2012


Staticクラス


Staticクラスはオブジェクトのインスタンス化ができないクラスでありStaticクラスのオブジェクトを作ることは出来ない。

実行すると1つのインスタンスしかない。先ほどインスタンス化ができないと書いたばかりなので混乱したかもしれないが、要はプログラムがこのオブジェクトをヒープに割り当てて、ユーザによるインスタンス化は許可しないということだ。

Staticオブジェクトは最初のコール時に作成されて、プログラムの終了時に破棄される。(あるいはクラッシュ時・・・)
public static class GameManager{
    public static int score;  
    public static float amountOfTime;
}
Game Managerというオブジェクトの型を宣言することは出来ない。
GameManager GM = new GameManager();
これはエラーを返す。
Staticクラスのメンバにアクセスするには単に:
GameManager.score = 10;
これはゲームのどの箇所からもどんなタイミングでもアクセスできます。Staticなクラスまはメンバは全てのファイルが有効範囲であり、プログラム全体が生存期間となる。

Staticクラスの使用例


新しいシーンをロードしたとき、前のシーンの全ての変数は破棄される。もしある変数を保持したい場合、例えばスコアを残す場いい、次のように記す:
DontDestroyOnLoad(score);
Staticクラスの便利なところは生存期間にある。なくなることがないため、シーンのロードのときも保持させたい場合はStaticクラスを使用すれば良い。レベル終了時に値をStaticメンバに渡し、新しいシーン開始時にその値を取得する。
DontDestroyOnLoad(score);
if(newLevel){
    GameManager.score = tempScore;
    Application.LoadLevel(nextLevel);
}
DontDestroyOnLoad(score);
また新しいシーンのスクリプトには:

void Start(){
    scoreTemp = gameManager.score;
}
ちょっとしたテクニックとして、GameManager.scoreはギリギリ最後まで使用を控えることだ。

例えばレベルが10個あって、レベルごとに最大200点獲得できるとする。

さらに別のプレイヤーがゲームを終えるまでに何度もリプレイした後に5000点獲得したとする。

二人目のプレイヤーは一人目ほどのスコアでなかっが、それでもより多くの点を獲得できたのは、書くレベルを何度もリプレイをして得点を積み重ねたためだ。次のレベルロードを待ってStatic変数の地点をスキップする場合、リトライによってそのレベルでの得点の積み重ねはキャンセルされます。

ゲームを中断するとき、Staticクラスのデータはセーブすることが出来る。

StaticクラスおよびStatic変数はプログラム実行で初めて登場したときに割り当てられます。Staticクラスは実際にそのクラスの変数またはメソッドにアクセスするまで作成されることは絶対にない。

-------

Unityメモリ管理の基礎を積み重ねていこうぜ!

2013年1月6日日曜日

NGUI 2.2.7



NGUIバージョンアップ情報です。

http://www.tasharen.com/forum/index.php?topic=11.15
2013/1/5 at 11:05:43 AM

2.2.7:
- 新機能: UICamera.stickyPress オプションを追加し、ひとつのタッチから複数のオブジェクトが OnPress通知を受け取れるようになった。
- 新機能: UICamera.hoveredObject がタッチイベントでも動作するようになり、最後のRaycastの結果を保持するようになった。
- 新機能: "Edit" ボタンを全ての Atlas と Font フィールドに追加し、Atlas/Fontの修正をより容易にした。
- 新機能: Localization.Localize を追加した。なお Localization.Get を static にしてしまいたかったが、下位互換性がなくなるため行っていない。
- 修正: 付属サンプルの Inventory が Unity 4.0 でも正しく動作するように修正。
- 修正: UILabel.text を null にセットできるように修正。
- 修正: UIPanel が場合により rect を正しく描画しなかった不具合を修正。
- 修正: Andrew Osborne 氏により、いくつかの微調整および修正を反映 (コミュニティメンバからの貢献)
- 修正: クリッピング パネルに属する UITexture のmainTexture での不具合を修正。
-------

筆者の環境ではアップグレード後にちょっと不具合が出ているため参考までに追記します。
(直れば後日に追記します)
<2013/1/6 9:30 追記>
NGUI作者が投稿から2時間ほどで以下の不具合の修正版をアップしてくれ、解決しました ^^

http://www.tasharen.com/forum/index.php?topic=2692.0


<追記ここまで>


不具合内容:


  • Example 9 - Quest Log、Example 10 - Localizationで境界線が紫色になってしまう
  • NGUI 2.2.7、Unity 4.0.0.f7


【変更前】

【変更後】



ブックマークに追加

このエントリーをはてなブックマークに追加

自己紹介

自分の写真
Unity3D公式マニュアル翻訳やってる人がスマホ(iPhone, Android)のゲーム開発しています。気軽に面白く初心者が遊べる内容がモットー。Blogでは開発情報をひたすら、Twitterではゲーム作成の過程で参考にしている情報を中心につぶやきます

ページビューの合計

過去7日間の人気投稿