タグ別アーカイブ: C-sharp

SVN or Git + Unity でやっておくこと

save prefabのショートカットキーを作成

prefabはApplyしてもファイルシステムへの保存が即時なされるわけではない。
変更後にすぐに git や Subversion にコミットしても変化が無かったり、空のプレファブをコミットしてしまったりで混乱を招くことがある。
これを回避するにはコミット前にUnityを終了する必要がある。
終了時に未保存のPrefabはちゃんとOS側のファイルシステムへ書き込まれる。

しかしそんなことを毎度操作してたら効率が悪いので特定のコマンドを実行するショートカットを用意する。

サンプル

      using UnityEngine;
      using UnityEditor;

      public class Util : Editor
      {
      #if UNITY_EDITOR
           [UnityEditor.MenuItem("Edit/SavePrefab %&s")]
           static void SavePrefab(){
                AssetDatabase.SaveAssets();
           }
      #endif
      }

gist

Menu Item のショートカットキー

ショートカットキーとして次の文字を特殊なキーの代わりに設定できる。
%#s で Command + Shift + s といった具合。

  • % : Command or Windows key
  • # : Shift key
  • &: Alt key

Unity Scripting Reference MenuItem

metaファイルの生成とPrefabデータのテキスト化

通常、Library下にUnityプロジェクト用ファイルが生成されるが、AssetServerを使うのでないのであれば、各Prefabに何がアタッチされているのかとか、インスペクタ上ではどんんあ値が入力されているか、とかはmetaファイルに格納される。このmetaファイルは各リソースファイルと同じところに置く必要がある。

これをしないとコミットしたプレファブのインスペクタが初期値(コードで指定した値)になる。

metaファイルの生成方法は次の通り。

Edit -> Project Settings -> EditorでInspector上にEditor Settingsが表示される。
この VersionControl の項目を MetaFiles にし、AssetSerializationForce Text にする。

これでprefabもTextベースになるのでDiffツール等で変更箇所が目視できるようになる。

Unityライブラリ辞典 ランタイム編

[Unity][XML][C#]テキストデータを外部ファイル化するサンプル

[wp_ad_camp_1]

Unityのプロジェクトで日本語をコード内に記述してる事案があって、これを解消した時の構造をメモ。

gist::ogkslownin

TxtLib.cs

staticで定義したテキストデータをKeyValue形式で保持したライブラリを複数所持するクラス。
プロパティは次に説明するXMLPlistContainerクラスで定義される。

使い方はライブラリの種類と取得したい文字列と対になっているkeyを渡すことで文字列データを取得する。

XMLPlistContainer.cs

クラス生成時にファイルを読み込む。
Editor上でのみ、ファイルのタイムスタンプを参照して使用時に読み直しをする。
文字列を取得する際にはインデクサを用意して、key文字列を渡すことで得られる仕組み。

fruit_default.xml

ここのファイルにkeyと対になる文字列を定義しておく。
ファイルは同名にして各国分用意する。

サンプルではアプリケーションサーバを各国で分ける前提での設計なのだけれど、各国のファイルを平行して保持したいのであれば、XMLのファイル名を国別に用意し、XMLPlistCOntainer内での読み込み時の”_default_”を、引数として受けた国名文字列と差し替える記述をすれば対応可能になる。
C#で学ぶ偏微分方程式の数値解法

[Unity3D][UnityEditor]ScriptableWizardが思ったよりも使えなかった話

[wp_ad_camp_1]

ScriptableWizardとは

ScriptableWizardは、一つのスクリプトをベースにオブジェクトを生成したい時等に利用するエディタ。EditorWindowを継承しているのでEditorWindowで出来る事は全部できるけども、独自に予めUIが用意されているのでOnGUIを仕込んではいけない。

主な用途は何か?

正直、何に使えるのか良くわからない。パラメータ違いのプレファブを大量生産するのに向いてるかと言えば、createボタンでウィンドウがきえてしまうしイマイチ不明。

もしあるとしたら、メインのEditorWindowのサブウィンドウとして出現させる等の用法。
EditorWindow内の特定のボタンをクリックしたら出現し、Wizard内の特定パラメータを変動させた上でCreateをしたら値を取得させる関数をEditorWindow側に命令するとか。
だとしてもOnGUIが使えないのでどうせならサブウィンドウもEditorWindowで生成し、各種ボタンを用意した方が用途が広い。

ただ、エラーを出したり、ヘルプを出したりできるので、特定の値を厳密に入力させたり、文字列による曖昧な入力が必要な場合等は効果的なのかもしれない。
Unityで作るスマートフォン3Dゲーム開発講座 Unity4対応

サンプル

using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;

public class TestWizard : ScriptableWizard {

 public string gstring;
 public int gint;
 private string pstring;
 private int pint;


 [UnityEditor.MenuItem("Custom/OHGAKI/TestWizard")]
 static void WillAppear()
 {
  ScriptableWizard.DisplayWizard("Test Wizard Title in DisplayWizard method.",typeof(TestWizard),"createButtonName","otherButtonName");
 }

 void OnWizardCreate()
 {
  GameObject newGameObj = new GameObject("new GameObj");
  newGameObj.AddComponent("SphereCollider");
  newGameObj.AddComponent("MeshFilter");
        newGameObj.AddComponent("MeshRenderer");
 }

 void OnWizardOtherButton()
 {
  Debug.Log("otherButtonName clicked");
 }

 void OnWizardUpdate()
 {
  helpString = "type "abc" in gstring.";
  if(gstring == "abc"){
   isValid = true;
   errorString = "";
  }else{
   isValid = false;
   errorString = "gstring is not "abc".";
  }
  
  position = new Rect(200f,200f,300f,300f);
 }
}

サンプルの解説

 [UnityEditor.MenuItem("Custom/OHGAKI/TestWizard")]

でメニュー項目を作成し、その際の起動ハンドラを次の行に記述してるのはメニュー起動系の通例通り。

起動時のスクリプトも通例通りStaticの関数として宣言。

関数内で実行しているのは

ScriptableWizard.DisplayWizard()

で、引数が3つある場合は一つ目がタイトル、二つ目がメインのボタン、三つ目がその他のボタンの文字列としてウィンドウが生成される。

この関数は引数の数や種類に応じて色々とやってることが変化するので、リファレンスを参照。

http://ws.cis.sojo-u.ac.jp/~izumi/Unity_Documentation_jp/Documentation/ScriptReference/ScriptableWizard.html

  • OnWizardCreate()はボタン押された時に駆動するイベント。
  • OnWizardOtherButton()はotherButtonを出現させていた場合の生成されるボタンが押された際に呼び出されるイベント。
  • helpStringは常時表示用の文字列を格納する。
  • errorStringはエラーっぽい表示させたい文字列を格納する。中身が空だとエラーマークは出て来ない。
  • OnWizardUpdate()は項目の内容が変更すると駆動する。

ScriptableWizard

また、サンプルでは特定の文字が入った場合にerrorString等をアップデートしたり、ボタンをisValidをtrueにすることで有効にしてる。
isValidはfalseにしておくとボタンが押せないグレーアウト状態にさせることができる。

今日はここまで。

[Unity3D][UnityEditor]EditorWindowの最小構成からボタン配置やらサブウィンドウの生成やら

[wp_ad_camp_1]

最小構成

ファイルをProjectのRootにEditorフォルダを作成し、その中に各種エディター的機能を使用するクラスを格納させておく必要がある。
(Asset/Editor/)
EditorはUnityEditorライブラリを使用し、ウィンドウを表示するのであればEditorWindowを継承させておく。
下のコードが最小構成。画面上部にメニュー項目が追加される。
それをクリックするとデバッグログに文字列を表示する。

using UnityEngine;
using UnityEditor;
using System.Collections;

public class _OGKEditor : EditorWindow {

 [UnityEditor.MenuItem("Custom/OHGAKI/EdtiorTest")]
 static void abcde(){
  Debug.Log("init"); 
 }

}

メニュー項目への表示は[]で囲われた範囲で、ディレクトリ階層を指定できる。
その次の行にあたるstatic関数が呼ばれる。この時の関数名は何でもいい。

Windowの出現

コードを次の様に書き換える。
変更した場所はDebag.Logから自身のクラスの型でEditorWindowによるGetWindowクラスで生成したwindowをキャストする。

public class _OGKEditor : EditorWindow {

 [UnityEditor.MenuItem("Custom/OHGAKI/EdtiorTest")]
 static void abcde(){
  _OGKEditor window = (_OGKEditor)EditorWindow.GetWindow (typeof (_OGKEditor));
 }

 void OnGUI(){
  if(GUILayout.Button("button")){
   //ボタン押した時の動作を記述
  }
 }
}

次の関数がウィンドウ生成している。サンプルは格納後に即座に開放して参照を切っているが、生成はちゃんとなされているのでOnGUI()で指定した動作は行われる。
EditorWindow.GetWindow( typeof() );
このウィンドウだけでウィンドウそのものを生成することはできるけど、各種初期設定をすることになるので、自身の型で参照を保ったまま、各種プロパティを変更しておく方が望ましい。

EditorWindowオブジェクトのプロパティやメソッドは次に一覧されている。
http://docs.unity3d.com/Documentation/ScriptReference/EditorWindow.html

プロパティ一覧

  • wantsMouseMove
    • BOOL
    • マウスイベントを取得するか否かを指定。
  • autoRepaintOnSceneChange
  • minSize
    • Vector2
    • ウィンドウの最小サイズ
  • maxSize
    • Vector2
    • ウィンドウの最大サイズ
  • title
    • string
    • ウィンドウのタブ上に表示される文字列
  • position
    • Rect
    • ウィンドウの初期位置と初期サイズ

試しに追加した初期値

 static void abcde(){
  _OGKEditor window = EditorWindow.GetWindow (typeof (_OGKEditor));
  window.minSize = new Vector2(300f,300f);
  GUIContent content = new GUIContent("__abcde__");
  window.ShowNotification(content);
  window.title = "org editor";
  window.position = new Rect(300f,300f,300f,300f);
 }

Window生成直後にNoticeを表示

初期化関数であるabcd()を次の様に追加してみる。

 static void abcde(){
  _OGKEditor window = EditorWindow.GetWindow (typeof (_OGKEditor));
  window.minSize = new Vector2(300,200);
  GUIContent content = new GUIContent("__abcde__");
  window.ShowNotification(content);
 }

すると、ウィンドウ生成直後に適当なサイズで文字列を表示できる。

UnityEditorWindowWithNotice

この文字列は数秒で消えるので注意喚起とか、複数のウィンドウを開いたり閉じたりさせる場合に現在のプレファブ名だとかを表示されることで、意識的に編集できるようになり、ヒューマンエラーを防止できる。
開いた時でなくても、windowに参照さえあれば次の様に記述してイベント事に確認したいことを表示することができる。

  GUIContent content = new GUIContent("Do not take my potato!");
  window.ShowNotification(content);

表示できるのはイベントが発生したときや独自の通知を受けた時。

EditorWindowクラス側が用意しているイベントは次の通り。

  • OnGUI Implement your own editor GUI here.
    • 描画されたときに走る。
    • ウィンドウ内のレイアウトを切ったりするのもここ。
  • Update Called 100 times per second on all visible windows.
    • 表示中のウィンドウは漏れなく秒間100回呼び出される。
  • OnInspectorUpdate OnInspectorUpdate is called at 10 frames per second to give the inspector a chance to update
    • 秒間10回の頻度で更新されるインスペクタ上のUpdate関数。
  • OnDestroy OnDestroy is called when the EditorWindow is closed.
    • windowを閉じた時に呼ばれる。
  • OnSelectionChange Called whenever the selection has changed.
    • SceneやHierarchy内でクリックする等して選択が変更される度に呼ばれる。
    • ウィンドウとは無関係で呼ばれる。
    • Selectionクラスから色々取得できる。
  • OnFocus Called when the window gets keyboard focus.
  • OnLostFocus Called when the window loses keyboard focus.
    • アクティブになるとOnFocusが、非アクティブになるとOnLostFocusが呼ばれます。
  • OnHierarchyChange Called whenever the scene hierarchy has changed.
    • ヒエラルキーが変わったら呼ばれる。ヒエラルキーの変動で読み直しすべきデータがあるのであれば読み直しを記述する。
  • OnProjectChange Called whenever the project has changed.
    • プロジェクトが変わったら呼ばれる。
    • データを読み込んで何かを表示しているのであれば、ここに読み直しの処理等を記述する。

OnSelectionChangeのサンプル

次のコードで選択しているオブジェクトのIDをずらっと表示できる。
オブジェクトを選択する都度呼ばれる。

 void OnSelectionChange(){
  foreach(int a in Selection.instanceIDs ){
   Debug.Log("id = "+ a); 
  }
 }

http://docs.unity3d.com/Documentation/ScriptReference/EditorWindow.OnSelectionChange.html

ウィンドウからさらにウィンドウを出現させる

サブウィンドウ用にクラス内クラスを用意。

 public class SubWindow: EditorWindow
 {
  public static SubWindow WillAppear(){
   
   SubWindow window = (SubWindow)EditorWindow.GetWindow(typeof(SubWindow));
   window.minSize = new Vector2(50f,100f);
   window.title = "SubWindow";
   return window;
  }
  void OnGUI(){
   GUILayout.Label("SubWindow Here"); 
  }
 }

で、親ウィンドウ側にはボタンから呼び出す様に次ぎのように記述を変更、追加する。

 void OnGUI(){
  if(GUILayout.Button("button"+num)){
   ButtonPushed();
  }
 }
 void ButtonPushed(){
  ++num;
  subWindow = SubWindow.WillAppear();
 }

SubWindowクラスに定義したWillAppearを呼び出す。
呼び出されたサブクラス側はサイズやらタイトルが差し替えられて以下のように出現する。

!!

うごごご。動かない。
エラー内容は次のとおり。

Instance of SubWindow couldn’t be created because there is no script with that name.

SubWindowのインスタンスは作られませんでしたよ。なぜならその名前でかかれたスクリプトなんざねーからだ!

サブウィンドウは別ファイルで定義

先に記述したクラス内クラスを独立したクラスとして定義しなおす。
次の様に適当なEditorWindow継承クラスを定義して、Editorディレクトリ下に入れておく。

using UnityEngine;
using UnityEditor;
using System.Collections;

public class SubWindow: EditorWindow
{
 public static SubWindow WillAppear(){
  
  SubWindow window = (SubWindow)EditorWindow.GetWindow(typeof(SubWindow));
  window.minSize = new Vector2(50f,100f);
  window.title = "SubWindow";
  return window;
 }
 void OnGUI(){
  GUILayout.Label("SubWindow Here"); 
 }
}

UnityEditorWindowWithSubWindow

と、今日はここまで。

ゲームの作り方  Unityで覚える遊びのアルゴリズム

おまけ

自身を無限に生成しつづけるブラクラみたいのつくれないかな?と思ったのだけれどEditorWindowクラスは代々シングルトンみたいです。
以下のサンプルはボタン付きウィンドウを表示してボタンが押されると自身を生成するもの。
生成されたとたん、もともとあったウィンドウは削除される。
解りやすくする為に生成される都度、位置を変更している。

using UnityEngine;
using UnityEditor;
using System.Collections;

public class _BrowserCrasher : EditorWindow {

 public _BrowserCrasher parent;

 [UnityEditor.MenuItem("Custom/OHGAKI/bracra")]
 public static void init()
 {
  _BrowserCrasher window = initWithPosition(new Vector2(100f,100f));
  window.Show();
 }

 public static _BrowserCrasher initWithPosition(Vector2 pos)
 {
  _BrowserCrasher window = EditorWindow.GetWindow(typeof(_BrowserCrasher));
  window.position = new Rect( pos.x+10f,pos.y+10f,100f,100f);
  return window;
 }

 void OnGUI()
 {
  if(GUILayout.Button("button"))
  {
   _BrowserCrasher window = initWithPosition(new Vector2( this.position.x ,this.position.y));
   window.parent = this;
   
  }
 }
}

サンプル全容

メインウィンドウ

using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;

public class _OGKEditor : EditorWindow {
	
	private int num = 0;
	private SubWindow subWindow;
	
	[UnityEditor.MenuItem("Custom/OHGAKI/EdtiorTest")]
	static void abcde(){
		_OGKEditor window = EditorWindow.GetWindow (typeof(_OGKEditor));
		window.minSize = new Vector2(300f,300f);
		window.title = "org editor";
		window.position = new Rect(300f,300f,300f,300f);
		GUIContent content = new GUIContent("__abcde__");
		window.ShowNotification(content);
	}
	
	void OnGUI(){
		if(GUILayout.Button("button"+num)){
			ButtonPushed();
		}
	}
	void ButtonPushed(){
		++num;
		subWindow = SubWindow.WillAppear();
	}
	
	
	//On!!
//	void OnSelectionChange(){
//		foreach(int a in Selection.instanceIDs ){
//			Debug.Log("id = "+ a);	
//		}
//	}
//	
//	void OnFocus(){
//		Debug.Log("OnFocus");
//	}
//	void OnLostFocus(){
//		Debug.Log("OnLostFocus");
//	}
	
	
}

サブウィンドウ

using UnityEngine;
using UnityEditor;
using System.Collections;

public class SubWindow: EditorWindow
{
	public static SubWindow WillAppear(){
		
		SubWindow window = (SubWindow)EditorWindow.GetWindow(typeof(SubWindow));
		window.minSize = new Vector2(50f,100f);
		window.title = "SubWindow";
		return window;
	}
	void OnGUI(){
		GUILayout.Label("SubWindow Here");	
	}
}

[C#]ListをRemoveAll( c => !c.MoveNext());する意味のまとめ

※いろいろと間違っていたのを修正。

数ヶ月前に参加したUnity仙人のセミナー、Unityで覚えるC#のサンプルで挙動がよくわからなかったもののまとめ。

サンプルは前述のスライドの56ページ。

ここで何が起こってるのかをまとめる。

まずListの言語仕様を整理

参考:http://www.atmarkit.co.jp/fdotnet/special/generics01/generics01_02.html

ListGenericClassは内容物の型を宣言する形式のArrayListクラス(のようなもの)。

Listという形で使用する。

ArrayListもListGenericも共に.Add()で内容を追加する。

この処理速度はListGenericの方が上。

理由はArrayListの場合はオブジェクトに変換するボックス化処理が挟まるから。

ジェネリッククラスの記述方法

参考:http://www.atmarkit.co.jp/fdotnet/special/generics01/generics01_03.html

山括弧内の型指定をTにすることで型を後付けにして汎用化できる。

ただ、複数の型を混在させる事が出来るわけではない。これをしたいのであればDictionaryClassを使用する。

Whereキーワード

型指定はTにしたいけれどもクラスであることは確実だったり、構造体であることが確実であったりと条件を幾分追加するキーワードで先の参考に示したページにその一覧が記述されている。

class MyGenericClass1 where T : struct {
  // 制約:Tは構造体
}

class MyGenericClass2 where T : class {
  // 制約:Tはクラス
}

class MyGenericClass3 where T : new() {
  // 制約:Tはインスタンス化可能
}

class MyGenericClass4 where T : MyOtherClass {
  // 制約:TはMyOtherClassクラスを継承
}

class MyGenericClass5 where T : IMyInterface {
  // 制約:TはIMyInterfaceインターフェイスを実装
}

class MyGenericClass6 where T : U {
  // 制約:Tは別の型パラメータUを継承
}

//////////////////////////////////////////////////
class MyOtherClass {
  // とあるクラス
}

interface IMyInterface {
  // とあるインターフェイス
}

また、複数の制約も可能ということなので試してみました。

class MyGenericClass where T:class,new()

こんな風にコロンで連結すれば良いです。

ゲームだと、出現したゴブリンとホフゴブリンとゴブリンアーチャー全員がゴブリンパンチ打てるよーって状況で全員がゴブリンパンチを採択するとかで使える。

例が限定的すぎるけれども。

ジェネリックに限らず、こういった書き方ができるのはC#の強み。

  public T this[int index] {
    get {
      return _list[index];
    }
    set {
      _list[index] = value;
    }
  }

で、ジェネリックについて整理したけどList型と必ずしも関連しないという事を注意したい。

List型はIEnumerableなのでforeachが使えるけれども、

前述した例だと独自実装なのでIEnumeratorを実装しておらず、foreachが使えない。

なんということでしょう・・・。

ListクラスのRemoveAllを理解する

RemoveAllメソッドは、特定の条件に一致する要素だけ削除する際に使用するメソッド。

RemoveAllの引数として、条件を判別するメソッドのハンドラ渡し、Trueが返る場合は削除を行う。

そこまでのサンプル。

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class genericRemoveAll : MonoBehaviour {

     // Use this for initialization
     void Start () {
          List<orgString> stringList = new List<orgString>();

          for( int i =0 ; i < 10; i++){
               orgString tmp = new orgString( "string:"+i );
               stringList.Add( tmp );
          }

          stringList.RemoveAll( Method );
          foreach(orgString o in stringList)
          {
                o.Method();
          }

     }
     bool Method(orgString o){
          return o.str == "string:3";
     }
     // Update is called once per frame
     void Update () {

     }
     public class orgString{
          public string str;
          public orgString(string s){
               this.str = s;
          }
          public void Method()
          {
               Debug.Log("at method :"+this.str);
          }
     }
}

相変わらず起動確認はUnityで。

RemoveAllの中身をラムダ式にするとこうなる。

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class genericRemoveAll : MonoBehaviour {

     // Use this for initialization
     void Start () {
          List<orgString> stringList = new List<orgString>();

          for( int i =0 ; i < 10; i++){
              orgString tmp = new orgString( "string:"+i );
              stringList.Add( tmp );
          }
          stringList.RemoveAll( s => s.str == "string:3" );
          foreach(orgString o in stringList)
          {
                o.Method();
          }

     }
     public class orgString{
          public string str;
          public orgString(string s){
               this.str = s;
          }
          public void Method()
          {
               Debug.Log("at method :"+this.str);
          }
     }
}

参考:http://www.atmarkit.co.jp/fdotnet/dotnettips/815listremove/listremove.html

ラムダ式をおさらい

ラムダ式はEventHandlerを省略したデリゲートをさらに省略した記述方式。

なのでラムダ式には左辺と右辺があり、左辺はdelegateとして呼び出されるメソッドのparameterが入り、右辺には式か文が入る。

左辺の記述は()にくくられていなければならないが、parameterが単一の場合は省略可能。

右辺の記述が式の場合、セミコロンで閉じない。

右辺の記述が文の場合、{ 文; 文; 文; }と記述し、{}にくくられていなければならないし、各文の最後に;が入ってなければ成らない。

(a, b) => {
a.transform.position = new Vector3( 0,0,0 );
b.transform.position = new Vector3( 1,1,1 );
}

また、以下の様な特徴もある。

http://d.hatena.ne.jp/seraphy/20120609

  • ラムダ式は括弧の外もスコープの対象。
  • スナップショットではなく、生きた参照。

まとめ

というわけで、RemoveAll()のparameterで記述されている件は…

列挙された内容をcというオブジェクトで代用し、MoveNext()が失敗する場合論理値を反転させTrueにし、RemoveAllの対象とする、という意味になる。

以下のサンプルが良い例になるだろうか。

         A a = new A ();
         a.Do ();
         a.Do ();
         a.Do ();
         a.Do ();
         a.Do ();

         public class A
         {
             List<IEnumerator> ien;
             public A ()
             {
                 this.ien = new List<IEnumerator>();
                 this.ien.Add (IenMethod1());
                 this.ien.Add (IenMethod2());
             }
             public void Do()
             {
                 this.ien.RemoveAll( c => !c.MoveNext() );
             }
             IEnumerator IenMethod1()
             {
                 Debug.Log ("befor1");
                 yield return null;
                 Debug.Log ("after1");
             }
             IEnumerator IenMethod2()
             {
                 Debug.Log ("befor2");
                 yield return null;
                 Debug.Log ("after2");
             }
         }

実行結果:

befor1
befor2
after1
after2

あーやっと終わった。スッキリ!

[C#][ObjC]列挙子

Enumerator=調査員。なんだか不穏な感じ。納税状態とかチェックされちゃいそう。
ちゃんと調べてみます。

C#だと

列挙子はGetEnumeratorメソッドを持っていればforeachで使うコレクションにできるIEnumeratorを取得できる。
このメソッドを持ってる=IEnumerableってこと。

IEnumeratorを実装しないとforeach文で列挙できない。
というか、IEnumeratorはforeachの為にある。かのようにMSDNには記述されている。

IEnumerable.GetEnumerator

http://msdn.microsoft.com/ja-jp/library/system.collections.ienumerable.getenumerator(v=vs.80).aspx

IEnumeratorの最初の位置、Reset直後はundefinedが入ってる。というかC#だと何も入ってない?
いずれにせよ最初に動作させるにはMoveNext()を一発かませとな。
コレクションの要素が変更追加削除されると無効になり、これは回復不能。
コレクションの全ての処理が終わるまでロックすることが推奨される。

以上、MSDNより。

ロックってどうやんの?

Unityだと

内容は以下のリンクのp55。
http://www.slideshare.net/lucifuges/unityc
つまり、MoveNextメソッド内の処理に、yield が挟まる間隔単位でcaseが設けられたswitch構文入りの処理をメンバに持つクラスが生成されるとのこと。
newされる度にクラス変数がカウントアップし、返す内容がシフトする仕組み。

Objective-Cだと

http://www.atmarkit.co.jp/fcoding/articles/objc/05/objc05d.html

NSEnumeratorクラス。

NSArrayやNSDictionaryクラス(これを継承したNSMutable**も?)列挙子を内包しているので、for-in使える。

Objective-Cでの使い方

.MoveNextにあたるのが nextObject。
取得する要素が無いとnilを返す。

for( id obj in array )
{
     NSLog( @"value : %@" , obj );
}
for (id key in dic)
{
     NSLog( @"key : %@ nvalue : %@n", key , [dic objectForKey:key] );
}

列挙子の取得から列挙。

NSEnumerator *en = [dic objectEnumerator];
for ( id obj in en)
{
     NSLog( @"value : %@n ", obj );
}

これで中身をズラズラっと取得。

NSDictionaryはkeyの列挙できるkeyEnumeratorがある。

NSEnumerator *en = [dic keyEnumerator];
id key;
while (key = [en nextObject]) {
     NSLog(@"key: %@, value: %@n", key, [dic objectForKey:key]);
}

サンプル

- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view, typically from a nib.
    NSLog(@"ApplicationStart");
    
    NSLog(@"NSArray");
    NSArray *arr = [NSArray arrayWithObjects:@"arr0",@"arr1",@"arr2",@"arr3",nil];
    for(id obj in arr)
    {
        NSLog(@"value:%@",obj);
    }
    
    NSLog(@"NSDictionary");
    NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:
                         @"value1",@"key1",
                         @"value2",@"key2", nil];
    for(id key in dic)
    {
        NSLog(@"key:%@, value:%@",key,[dic objectForKey:key]);
    }
    
    NSEnumerator *en = [dic keyEnumerator];
    id key;
    while(key = [en nextObject])
    {
        NSLog(@"en::key:%@, valu:%@",key,[dic objectForKey:key]);
    }
}