Unity3D 프로그래머라면 알아야할최소한의 C# 최흥배 https://github.com/jacking5/choiheungbae
Unity で覚える C Learn C# in Unity http://www.slideshare.net/lucifuges/unityc
C # 에서의클래스 클래스정의 using UnityEngine; // 사용할라이브러리선언 // MonoBehaviour 를상속하는 NewBehaviourScript 클래스선언 public class NewBehaviourScript : MonoBehaviour // 멤버필드선언 public Vector3 velocity; // 멤버메소스정의 void Update() transform.translate(velocity * Time.deltaTime); 멤버선언에 p u b lic 을붙이면클래스밖에서도접근할수있다. p riva te 를붙이던가아무것도붙이지않으면클래스밖에서접근할수없다. 내부에무엇이있고, 무엇을할수있는지숨기는것이좋다. 3 /5
C # 에서의클래스 클래스가멤버로가지고있는것 필드변수. 보통바로멤버변수를공개하지않지만 Un ity 의경우공개필드로하지않으면인스펙트에표시되지않는다. 메소드 / 생성자 / 소멸자함수. 생성자와소멸자는조금특별한함수. 프로퍼티접근자간이표기법 인덱서오브젝트를배열과같이다룰수있도록해주는기능. 이벤트처리의일부를외부에위임하거나어떤타이밍에서통지하기위한기능. 4 /5
C # 에서의클래스 생성자오브젝트의초기화처리를하는메소드. 단 Un ity 는 Mo n o B e h a vio u r 을상속한클래스가생성자를구현하는것을추천하지않는다. A w a ke / S ta rt 를사용하도록하자. public class Foo : Bar int i; // 생성자정의. 반환값이없고, 클래스명과같은이름의메소드로한다. public Foo(int i) this.i = i; // 같은클래스의다른생성자를호출한다. public Foo() : this(0) // 부모의생성자를명시적으로호출하는방법. 생략하면부모의인자없는생성자가호출된다. public Foo(float f) : base(f) Foo f = new Foo(1.0f); // 3 개의생성자가호출된다. 5 /5
C # 에서의클래스 소멸자오브젝트의파괴처리를하는메소드. C# 은 GC 가동작하므로명시적으로파괴하지않아도좋으므로통상의식할필요가없다. public class Foo // 소멸자정의. 반환값, 인수, 접근자지정도없다. 클래스명에 ~ 을붙인이름의메소드로한다. ~Foo() C # 에서파괴타이밍을관리하고싶은클래스는 ID is p o s a b le 을상속하고, D is p o s e () 메소드를구현, u s in g 문과같이사용한다. 단 D is p o s e () 구현은익숙하지않아서조금까다롭다 6 /5
C # 에서의클래스 프로퍼티 : 접근자간이표기법 public class NewBehaviourScript : MonoBehaviour // 멤버프로퍼티정의 public Vector3 velocity get return rigidbody.velocity; set rigidbody.velocity = value; // 위는아래의접근자정의를간단하게한것이다. Vector3 GetVelocity() return rigidbody.velocity; void SetVelocity(Vector3 value) rigidbody.velocity = value; // 프로퍼티는보통멤버필드와같도록접근할수있다 void Start() velocity = Vector3.zero; /5
C # 에서의클래스 프로퍼티를좀더편리하게사용프로퍼티는구현을생략해도 O K public class NewBehaviourScript : MonoBehaviour public Vector3 velocity get; set; // 위는아래와같은암묵적인멤버필드를가지고있다고생각하면 OK Vector3 m_velocity; public Vector3 velocity get return m_velocity; set m_velocity = value; 프로퍼티는 g e t/s e t 마다접근자지정을붙일수있다 public class NewBehaviourScript : MonoBehaviour public Vector3 velocity get; private set; 8 /5
C # 에서의클래스 프로퍼티를좀더편리하게사용읽기만허용하는프로퍼티는 s e t 을생략할수있다. public class NewBehaviourScript : MonoBehaviour public Vector3 velocity get return rigidbody.velocity; 9 /5
1 0 /5 C # 에서의클래스 인덱서 public class NewBehaviourScript : MonoBehaviour int[] data; // 인덱서를정의 public int this[int index] get set return data[index]; data[index] = value; // 인덱서를멤버로가지는오브젝트는배열과비슷하게접근할수있다 void Start() NewBehaviourScript nbs = new NewBehaviourScript(); print(nbs[0]);
1 1 /5 C # 에서의클래스 이벤트중요한기능이지만이해가어려우므로뒤에다시설명 public class Foo // 델리게이트정의 public delegate void OnDamageDelegate(GameObject victim, float amount); // 정의한형의이벤트를선언 public event OnDamageDelegate ondamage; // 데미지처리 float hp get; private set; public void DoDamage(float amount) hp -= amount; ondamage(gameobject, amount); // ondamage 에등록되어있는메소드를호출 // 예를들면데미지를먹었던타이밍에서이메소드를호출하고싶은경우 void PrintDamage(GameObject victim, float amount) print(victim.name + " got damage " + amount); // 이벤트에데릴게이트를등록 Foo f = new Foo(); f.ondamage = PrintDamage; // 데미지처리를호출하면이벤트기능으로 PrintDamage 메소드가호출된다 f.dodamage(10);
1 2 /5 C # 에서의클래스 클래스멤버와인스턴스멤버 public class Foo : MonoBehaviour // 클래스필드정의 public static int classfield = 0; // 인스턴스필드정의 public int instancefield = 0; // 클래스메소드정의 public static void Hoge() print("hoge! " + classfield); // 인스턴스메소드정의 public void Mage() print("mage! " + classfield); // 클래스멤버에는인스턴스가없어도접근할수있다 Foo.classField = 10; Foo.Hoge(); // "Hoge! 10" Foo foo = new Foo(); print(foo.instancefield); // 0 foo.mage(); // "Mage! 10"
1 3 /5 C # 에서의클래스 클래스멤버만가진클래스모든멤버가 s ta tic 한클래스는클래스자체도 s ta ti c 으로하여인스턴스를만드는것을명시적으로막는다. public static class Foo // 클래스필드정의 public static int classfield = 0; // 클래스메소드정의 public static void Hoge() print("hoge! " + classfield); // static 한클래스는정적생성자 ( 클래스생성자 ) 로초기화한다 static Foo() classfield = 10;
1 4 /5 C # 에서의클래스 클래스수식자 s e a le d s e a le d 수식자를붙인클래스는상속할수없다. 멤버필드수식자 con s t 실행중에값이변하지않는디 ( 변할수없다 ) 필드에붙여서값을바꾸는코드를쓰면컴파일시에에러가발생한다. re a d o n ly 의미적으로는 c o n s とじㅅ와같다. c o n s t 는값타입에만사용할수있지만 re a d o n yはl 는참조타입에도붙일수있다
1 5 /5 C # 에서의클래스 멤버메소드수식자 virtual overdeir exernt 외부에서구현한메소드를선언할때 DlmportIl 속성과함께사용. Unity 는네이티브플러그인메소드를호출하고싶을때사용
1 6 /5 C # 에서의클래스 속성 ( 어트리뷰트 ) C # 은어트리뷰트라는기능을사용하여클래스자체나그멤버에메타데이터를붙일수있다. Un ity 에서는자신이만든클래스에 S e ria liz a b le 속성을붙여서인스펙트에표시할수있도록하거나, 에디터에확장에서자주사용한다 [ExecuteInEditMode()] // 이속성을붙이면에디터상에서비실행시에도 Start 나 Update 를호출가능 public class NewBehaviourScript : MonoBehaviour [System.Serializable()] // 이속성을붙이지않으면 Foo 형필드가인스펙터에표시되지않는다. public class Foo public int hoge; public Foo f;
1 /5 C # 에서의구조체 구조체정의사양 public struct Point // 멤버필드선언 public int x, y; // 멤버메소드정의 public Point(int x, int y) this.x = x; this.y = y; 기본적으로는클래스정의와같지만구조체는클래스와같은구현상속은할수없다. 인터페이스를상속하여자신이구현하는것은가능하다. 또클래스는참조형이지만구조체는값형이라는것이다르다.
C # 에서의인터페이스 인터페이스정의사양 interface ICollidable // 멤버메소드선언 ( 정의는할수없다 ) bool IsCollide(GameObject other); public class NewBehaviourScript : MonoBehaviour, ICollidable // 인터페이스구현 public bool IsCollide(GameObject other) if (gameobject.collider == null other.collider == null) return false; return IsCollide(gameObject.collider, other.collider); 인터페이스는그것을상속한오브젝트가어떤멤버메소드, 프로퍼티, 인덱서, 이벤트를가질지를명시하기위한것이다. 멤버필드는선언할수없고구현도할수없기때문에단독으로는사용할수없다. 1 8 /5
1 9 /5 클래스 o r 구조체 참조형과값형클래스는참조형, 구조체는값형이다. 이 2 개의차이는메소드인수나반환값으로오브젝트가반환될때의행동에영향이미친다. 예를들면오브젝트 Fo o 에서 B a r 에변수가넘겨진경우 참조형 값형 Tra n s fo rm Ve c to r3 복제 Ve c to r3 Fo o B a r Foo B a r
2 0 /5 클래스 o r 구조체 값형의참조전달 re f 키워드나 o u t 키워드를사용하여구조체와같은값형의참조를전달하는방법이있다. public class Foo Vector3 myvector; public AddVectorTo(ref Vector3 target) target += myvector; public class Bar Foo foo Vector3 = new Foo(); myvector; public Bar() foo(ref myvector);
2 1 /5 클래스 o r 구조체 N u l l 허용형 값형의변수라도아직초기화되지않았다는것을명시하고싶은경우가있다. 이럴때 Nu ll 허용형을사용한다. public class Foo Vector3? myvector; // 변수의형뒤에? 을붙이면 Null 허용형이된다. public Vector3 GetVector() if (myvector == null) myvec tor = Vector3.up; return myvector.value; 이것은데이터베이스와연동을간단하게하기위해준비된것으로 Un ity 에서사용할일은거의없다. 만약이것을사용하게된다면설계에문제가있다고봐야한다.
2 2 /5 클래스 o r 구조체 참조형장점오브젝트의크기에상관없이전달비용이일정. 전달받은곳에서값을변경하면전달한곳에서의값도변경단점 G C 에서처리비용이높음 값형장점 G C 에서처리할때비용이낮고, 전달받은곳에서값을변경하여도전달한곳에영향을주지않는다단점오브젝트의크기에비례하여비용이증가한다. box 화에의한성능악화를알기어렵다
2 3 /5 클래스 o r 구조체 박스화 박스화해제값형을 o b je c t 형으로변환하는것을박스화, o b je c t 형을원래값으로캐스트하는것을박스화해제라고하며, 어느쪽도꽤비용이높은조작으로최대한피해야한다 ( 박스화되면인스턴스사이즈도증가한다 ). object o = 1; // 박스화 int i = (int)o; // 박스화해제 // ArrayList는모든요소를 object 형으로변화해서저장하는컬렉션 ArrayList list = new ArrayList(); list.add("hoge"); // 그러므로문자열도수치도같은 ArrayList에저장할수있지만 list.add(100); // 여기에서박스화발생! 알아차리기힘들다! 뒤에설명할제네릭이라는기능을사용하면불필요한박스화를줄일수있으므로적극적으로사용한다.
2 4 /5 클래스 o r 구조체 어느쪽을선택할까? 기본은클래스를사용하고, 아래조건을만족한경우에는구조체를사용한다 ( 라고 Mic ro s o ft 문서에써여있다 ). 1. 정수나부동소수점과같은논리적으로단일한값을표시 2. 인스턴스사이즈가 16 바이트미만인경우 3. 상속에의해행동을바꿀필요가없을때 4. 빈번하게박스화할필요가없다 예를드렴 이차원좌표를나타내는 P o i n t 와같은것은구조체에적합하다.
2 5 /5 C # 의편리한기능 Enum: 일련의정수를하나로모은형 public class Foo const int DamageTypeSlash = 1; const int DamageTypeBlunt = 2; const int DamageTypeExplosion = 3; // enum 으로하는쪽이보기좋다 enum DamageType Slash = 1, Blunt, Explosion, DamageType damagetype; // Enum 형의각값에는 형타입. 이름 이라는형식으로접근한다. public Foo() damagetype = DamageType.Blunt; // int GetDamageTypeID() return (int)damagetype;
2 6 /5 C # 의편리한기능 namespace: 이름공간 namespace MyClasses // 관련된오브젝트를하나의공간에모아놓아검색성을올리고, 이름충돌을피한다 public class Foo public class Bar // 이름공간안의요소에는이와값이접근한다 MyClasses.Foo f; // 다른필드에서도같은이름공간을선언할수있다 namespace MyClasses public class Hoge // 파일선두에서 using 디렉티브를사용하면접근시이름공간을생략할수있다 using MyClasses; Foo f;
2 /5 C # 의편리한기능 캐스트, 형정보이용캐스트 ( 형의명시적인변환 ) 는 C 방식과비슷. 또 is 연산자, a s 연산자라는형조사의간단한방법이있다. // 보통캐스트는 ( 목적형 ) 으로한다 Collider c = (Collider)gameObject.AddComponent("BoxCollider"); // is 연산자로인스턴스형을지정한것인지조사할수있다 if (c is BoxCollider) boxcollider = c as BoxCollider; // 인스턴스를지정한형으로캐스트. 할수없다면 null // 형정보를추출해서자신이판정 if (c.gettype() == typeof(boxcollider)) print("ok"); // 제너릭을사용하면캐스트는필요없다 c = gameobject.addcomponent<boxcollider>();
2 8 /5 C # 의편리한기능 var 형이명확한로컬변수는형명을 va r 로가능 public class Foo void Hoge() // 이것을 Transform t = transform; // 이렇게써도좋다 var t = transform; // 이 1 문으로는 var 가실제어떤형이될지모르므로에러 var i; i = 0; public var i = 0; // 이것도에러. var 를사용할수있는것은로컬변수만
2 9 /5 C # 의편리한기능 파티셜클래스와파티셜메소드하나의클래스정의를복수의파일에나누어서쓸수있다 public partial class Foo void Hoge() public partial class Foo void Mage() // 물론클래스 Foo 는 Hoge, Mage 양쪽메소드를멤버로가지고있다. Foo f = new Foo(); f.hoge(); f.mage(); 파티셜클래스의메소드에 p a rtia l 수식자를붙이면구현을다른파일에서정의하여도정의하지않아도 (!) 좋다. public partial class Foo partial void Hoge();
3 0 /5 C # 의편리한기능 확장메소드 // 본래는아래처럼사용해야하지만 Instantiate(prefabObject); // 확장메소드를사용하면 static class GameObjectExtension // static 메소드첫번째인수에앞에 this 를붙이면확장메소드 public static Object Instantiate(this GameObject prefab) return Instantiate(prefab); // GameObject 클래스가 Instantiate() 라는인스턴스메소드를가진것처럼쓸수있다 prefabobject.instantiate();
3 1 /5 C # 제어구문 if, for, while, do~while 문타언어와비교해서특별한차이는없다. continue, break, goto 등도사용할수있다. switch 문 case 에문자열을사용할수있고, 기본적으로 break 을생략할수없는등타언어와비교해서조금특별하다. switch (hoge) case 0: case 1: DoSomething(); break; case 2: DoSomething2(); goto case 0; // 어떤처리를한후어떻게하든다른 case 로가고싶은경우는 goto 를사용 default: DoNothing(); break;
3 2 /5 C # 제어구문 foreach 문배열이나컬렉션등의모든요소에대해서반복해서처리를한다. foreach (AudioSource a in GetComponents<AudioSource>()) a.stop(); 뒤에설명할 Lin q 와조합하면 fo r 문이거의불필요해진다 // 어태치되어있는 AoudioSouce 재생중인것에대해 Stop() 을호출하는예 foreach (var a in from a in GetComponents<AudioSource>() where a.isplaying select a) a.stop(); 루프문안에서 if 문을사용하여분기하면루프처리자체가길어지므로가능하면 Lin q 로처음부터리스트를정형화해서 fo re a c h 문으로처리하는것이보기좋다.
3 3 /5 C # 제어구문 try ~ catch ~ final 문 C # 에서예외처리를제어하는구문이지만 Un ity 가예외를거의의식하지않아도좋은설계로되어있어서거의사용하지않는다. StreamReader sr = null; try sr = File.OpenText(path); string s = ""; while ((s = sr.readline())!= null) print(s); catch (FileNotFoundException e) print("file Not Found!"); finally sr.dispose();
C # 제어구문 using 문 이름공간생략이나형의별명정의에도 u s in g 이라는키워드를사용해서헷갈리기쉽지만메소드내에나오는 u s in g 문은 D is p o s e () 호출을보증하는것. Un itye n g i n e 에는 ID is p o s a b le 을구현한클래스가없으므로그다지사용하지않는다 u s n g 판 using (StreamReader sr = File.OpenText(path)) string s = ""; while ((s = sr.readline())!= null) print(s); 비 u s in g 판 StreamReader sr = null; try sr = File.OpenText(path); string s = ""; while ((s = sr.readline())!= null) print(s); finally sr.dispose(); 3 4 /5
3 5 /5 제너릭 제너릭이라는것은불필요한캐스트를줄이고, 컴파일시에형조사를할수있고, 코드가최적화되어성능을좋게해주는아주멋진기능이다. BoxCollider c; // 비제너릭판 ( 길어서보기않좋다 ) c = (BoxCollider)gameObject.AddComponent(typeof(BoxCollider)); c = gameobject.addcomponent(typeof(boxcollider)) as BoxCollider; // 제너릭판 c = gameobject.addcomponent<boxcollider>(); 그러나 Un ity 는제너릭판메소드를그다지준비하고있지않다. ;;;;
3 6 /5 제너릭 직접제너릭대응하기 // 예를들면이런오브젝트를제너릭에대응하는경우 Object FindObjectOf(Type type) return FindObjectOfType(type); // 형파라미터이름을 T 로하는것은관습 T FindObjectOf<T>() where T: Component // 형파라미터에제한을둘수있다 ( 임의 ) return (T)FindObjectOfType(typeof(T)); // 위의제너릭메소드호출방법 var renderer = FindObjectOf<Renderer>(); // 형이확실하므로 var 를사용할수있고캐스트불필요! // 형파라미터에제한을걸어서아래는컴파일시에러 int i = FindObjectOf<int>(); // int 는 Component 를상속하고있지않다
3 /5 제너릭 비제너릭컨테이너사용하지말자!!! 박스화에서설명했듯이 A rra ylis t 등의비제너릭은몰래성능을악화시키고본래필요없는캐스트가증가해서좋지않다 ( 캐스트가증가하면버그도증가 ). 명확한이유가없는한 S ys te m.c o lle c tio n s.g e n e ric 이름공간에있는제너릭판컨테이너를사용하자. using System.Collections.Generic; List<int> list; list.add(100); // 박스화없음! list.add("hoge"); // 컴파일시에러! int i = list[0]; // 물론박스화해제도없다!
델리게이트 메소드를참조할수있는형 메소드의참조를보존해두고뒤에호출하거나복수의메소드를하나의변수에넣어서호출할수있는기능. // 델리게이트형정의 delegate void SomeDelegate(float f); // 정의한형의변수를선언 SomeDelegate somedelegate; // 예를들면이런메소드를앞의변수에대입할수있다 void Hoge(float value) // 형만맞으면인수의이름은같지않아도괜찮다 somedelegate = Hoge; // 인스턴스메소드도대입할수있다 public class Foo public void Bar(float amount) Foo f = new Foo(); somedelegate += f.bar; // += 로추가대입할수있다 // 델리게이트변수에저장되어있는메소드를호출 (Hoge 와 f.bar 가순서대로불려진다 ) somedelegate(1.0f);
3 9 /5 델리게이트 이미준비되어있는델리게이트 인수도없고반환값도없는델리게이트나인수를하나취하고 b o o l 값을반환하는델리게이트등잘사용하므로 S ys te m 아래에처음부터준비해두고있다. System.Func<TResult> // TResult Func() System.Predicate<T> // bool Predicate(T value) System.Action<T> // void Action(T value) System.Action<T1, T2> // void Action(T1 p1, T2 p2)
이벤트 델리게이트를밖에서실행하지않도록한다 예를들면데미지를먹은타이밍을델리게이트를사용하여다른오브젝에통지하고싶은경우델리게이트를노출하면밖에서실행할수있게되어버려서곤란하다. public class Foo // 델리게이트형정의 public delegate void OnDamageDelegate(GameObject victim, float amount); // 정의한델리게이트형의변수선언 (private 로해버리면등록조차할수없으므로의미없다 ) public OnDamageDelegate ondamage; // 데미지처리 float hp get; private set; public void DoDamage(float amount) hp -= amount; ondamage(gameobject, amount); Foo f = new Foo(); f.ondamage(1.0f); // 실제로데미지를먹지않았는다 ondamage 를직접호출해버린다 이런한때에는이벤트라는기능을사용하자
이벤트 이벤트는밖에서는추가와삭제만할수있다 델리게이트를노출할필요없이이벤트로하면추가와삭제는클래스밖에서할수있지만실행은클래스내에서만할수있다. 이것으로이상한타이밍에서호출되는것을방지. public class Foo // 델리게이트형정의 public delegate void OnDamageDelegate(GameObject victim, float amount); // 델리게이트형변수에 event 를붙인다 public event OnDamageDelegate ondamage; // 데미지처리 float hp get; private set; publi c void DoDamage(float amount) hp -= amount; ondamage(gameobject, amount); Foo f = new Foo(); f.ondamage(1.0f); // 컴파일에러 f.ondamage += PrintDamage; // 추가는문제없이할수있다.
4 2 /5 람다식 이름없는메소드 public class Foo // 델리게이트형정의 public delegate void OnDamageDelegate(GameObject victim, float // 델리게이트형변수에 event 를붙인다. public event OnDamageDelegate ondamage; /* 생략 */ Foo f = new Foo(); // 이벤트에람다식을대입 f.ondamage = (victim, amount) => print(victim.name + " got damage " // 이것은아래와같다 void PrintDamage(GameObject victim, float amount) print(victim.name + " got damage " + amount); f.ondamage += PrintDamage; amount); + amount);
4 3 /5 람다식 Lis t 클래스등은조건판정용델리게이트 ( 리스트요소하나를인수로하여 bool 값을반환하는메소드 ) 를인수로잡는 Find 메소드등을멤버로가지고있다. 여기에람다식을넘기는것도가능. List<GameObject> gameobjects = new List<GameObject>(src); // 이름에 Enemy 를포함한오브젝트를찾는다 GameObject go = gameobjects.find(o => o.name.contains("enemy")); // gameobjects 리스트중 Y 좌표가 0 미만오브젝트를모두삭제 gameobjects.removeall(o => o.transform.x < 0);
L IN Q 데이터베이스용으로도입된기능이지만대량의데이터중에서필요한것을찾거나, 복수의리스트를합치거나, 데이터뭉치를정형화하기위한기능. 잘사용하면코드를깔끔하게할수있다. // 예를들면이런루푸는 var renderers = GetComponentsInChildren<Renderer>(); foreach (var r in renderers) if (r!= null && r.material!= null) r.material.maincolor = Color.red; // LINQ 로이렇게할수있다 var materials = from r in GetComponentsInChildren<Renderer>() where r!= null && r.material!= null select r.material; foreach (Material m in materials) m.maincolor = Color.red; 루프안에서분기하는것보다리스트를정형화한쪽이실수하기가어렵다.
4 5 /5 L IN Q 주목하고싶은멤버만을추출하여익명클래스를만들어서반환하면관심없는멤버에접근해서버그를만드는것을방지. var materialsets = from Renderer r in FindObjectsOfType(typeof(Renderer)) where r.material!= null && r.sharedmaterial!= null select new r.material, r.sharedmaterial ; foreach (var m in materialsets) m.material.maintexture = texture; m.sha redmaterial.maintexture = texture; 단가벼운처리는아니므로사용할때주의가필요
4 6 /5 코루틴 C # 의이터레이터구문을유용하고있다 C # 의 yie ld re tu rn 의본래목적은이터레이터를가볍게구현하기위한것이다. 도중까지처리하고일시메소드를빠져서다음에또그기에서 ( 메소드도중 ) 재개할수있다라는특성이멀티태스크처리에어울려서 Un ity 에서는코루틴에사용되고있다. 코루틴은 C # 의기능은아니다. // 본래사용방법 IEnumerator Count10() for (int i = 10; i >= 0; --i) yield return i; foreach (var i in Count1to10()) print(i); // 10, 9, 8,...
코루틴 컴파일시에암묵적으로클래스가만들어진다 yie ld 문이포함된메소드는실은컴파일시에암묵의열거용클래스로변환되고있다. IEnumerator CountUp() print(1); yield return null; print(2); yield return null; print(3); class CountUpEnumerator : IEnumerator int state = 0; bool MoveNext() switch (state++) case 0: print(1); re turn null; case 1: print(2); re turn null; case 2: print(3); IEnumerator CountUp() return new CountUpEnumerator();
4 8 /5 코루틴 코루틴은 MoveNex を t 을매프레임호출 아마 G a m e O b je c t 가지금실행중의코루틴의리스트를가지고있고매프레임이것들의 Mo ve Ne xt() 메소드를호출하고있을뿐. 비슷한처리를직접쓸수있다. 코루틴마다정지재개를관리하고싶은경우는이쪽이유연성이더높다. public class Foo : MonoBehaviour List<IEnumerator> coroutines = new List<IEnumerator>(); void Start() coroutines.add(mycoroutine1()); coroutines.add(mycoroutine2()); void Update() coroutines.removeall(c =>!c.movenext());
C# 학습소개 http://www.csharpstudy.com/default.aspx http://www.aladin.co.kr/shop/wproduct.aspx?isbn=8998139340
C# 학습소개