UnityのAddComponentについて調べてみた

AddComponentできるのはMonoBehaviourクラスを継承しているコンポーネントだけ。
MonoBehaviourは
Behaviourクラス->Componentクラス->UnityEngine.Objectクラスを継承しています。
一見Componentクラスを継承すればAddComponentできるように思えますが間違いです。MonoBehaviourクラスを継承しないとAddComponentできません。

UnityでGameObjectにAddComponentするには2つの方法があります。

##1:InspectorのAddCompoentからコンポーネントをAddする。
<img width="259" alt="スクリーンショット 2016-08-06 11.43.23.png" src="https://qiita-image-store.s3.amazonaws.com/0/67575/cdad1f57-ec32-b693-eb97-b5bb15d94403.png">

InspectorのAdd Componentボタンを押すと、以下の処理が行われます。

1. UnityEditor/InspectorWindow.csクラスのAddComponentButton(Editor[] editors)関数が実行される
2. AddComponentButton関数でAddComponentWindow.Show関数が実行される
3. AddComponentWindow.Show関数の中でInit関数が呼ばれ、Init関数でCreateComponentTree関数が実行される。
4. CreateComponentTree関数で各種コンポーネントの名前が取得される
5. 取得したコンポーネント名を使ってTree表示する

```lang:AddComponentButton関数一部抜粋
					if (AddComponentWindow.Show(rect, (
						from o in firstNonImportInspectorEditor.targets
						select (GameObject)o).ToArray<GameObject>()))
					{
						GUIUtility.ExitGUI();
					}
```


```lang:Init関数でCreateComponentTree関数を実行
private void Init(Rect buttonRect)
		{
			buttonRect = GUIUtility.GUIToScreenRect(buttonRect);
			this.CreateComponentTree();
```

```lang:CreateComponentTree関数でコンポーネント名を取得
		private void CreateComponentTree()
		{
			string[] submenus = Unsupported.GetSubmenus("Component");
			string[] submenusCommands = Unsupported.GetSubmenusCommands("Component");
```

重要なのはCreateComponentTree関数内で行なわれているコンポーネント名とコンポーネントIDの取得です。

###Unsupported.GetSubmenus関数の取得結果

```lang:Unsupported.GetSubmenus("Component")関数の戻り値(string[])
Component/Add...
Component/Mesh/Mesh Filter
Component/Mesh/Text Mesh
Component/Mesh/Mesh Renderer
Component/Mesh/Skinned Mesh Renderer
Component/Effects/Particle System
Component/Effects/Trail Renderer
Component/Effects/Line Renderer
Component/Effects/Lens Flare
Component/Effects/Halo
Component/Effects/Projector
Component/Effects/Legacy Particles/Ellipsoid Particle Emitter
Component/Effects/Legacy Particles/Mesh Particle Emitter
Component/Effects/Legacy Particles/Particle Animator
Component/Effects/Legacy Particles/World Particle Collider
Component/Effects/Legacy Particles/Particle Renderer
Component/Physics/Rigidbody
Component/Physics/Character Controller
Component/Physics/Box Collider
Component/Physics/Sphere Collider
Component/Physics/Capsule Collider
Component/Physics/Mesh Collider
Component/Physics/Wheel Collider
Component/Physics/Terrain Collider
Component/Physics/Cloth
Component/Physics/Hinge Joint
Component/Physics/Fixed Joint
Component/Physics/Spring Joint
Component/Physics/Character Joint
Component/Physics/Configurable Joint
Component/Physics/Constant Force
Component/Physics 2D/Rigidbody 2D
Component/Physics 2D/Box Collider 2D
Component/Physics 2D/Circle Collider 2D
Component/Physics 2D/Edge Collider 2D
Component/Physics 2D/Polygon Collider 2D
Component/Physics 2D/Distance Joint 2D
Component/Physics 2D/Fixed Joint 2D
Component/Physics 2D/Friction Joint 2D
Component/Physics 2D/Hinge Joint 2D
Component/Physics 2D/Relative Joint 2D
Component/Physics 2D/Slider Joint 2D
Component/Physics 2D/Spring Joint 2D
Component/Physics 2D/Target Joint 2D
Component/Physics 2D/Wheel Joint 2D
Component/Physics 2D/Area Effector 2D
Component/Physics 2D/Buoyancy Effector 2D
Component/Physics 2D/Point Effector 2D
Component/Physics 2D/Platform Effector 2D
Component/Physics 2D/Surface Effector 2D
Component/Physics 2D/Constant Force 2D
Component/Navigation/Nav Mesh Agent
Component/Navigation/Off Mesh Link
Component/Navigation/Nav Mesh Obstacle
Component/Audio/Audio Listener
Component/Audio/Audio Source
Component/Audio/Audio Reverb Zone
Component/Audio/Audio Low Pass Filter
Component/Audio/Audio High Pass Filter
Component/Audio/Audio Echo Filter
Component/Audio/Audio Distortion Filter
Component/Audio/Audio Reverb Filter
Component/Audio/Audio Chorus Filter
Component/Rendering/Camera
Component/Rendering/Skybox
Component/Rendering/Flare Layer
Component/Rendering/GUI Layer
Component/Rendering/Light
Component/Rendering/Light Probe Group
Component/Rendering/Reflection Probe
Component/Rendering/Occlusion Area
Component/Rendering/Occlusion Portal
Component/Rendering/LOD Group
Component/Rendering/Sprite Renderer
Component/Rendering/Canvas Renderer
Component/Rendering/GUI Texture
Component/Rendering/GUI Text
Component/Layout/Rect Transform
Component/Layout/Canvas
Component/Layout/Canvas Group
Component/Layout/Canvas Scaler
Component/Layout/Layout Element
Component/Layout/Content Size Fitter
Component/Layout/Aspect Ratio Fitter
Component/Layout/Horizontal Layout Group
Component/Layout/Vertical Layout Group
Component/Layout/Grid Layout Group
Component/Miscellaneous/Animator
Component/Miscellaneous/Animation
Component/Miscellaneous/Network View
Component/Miscellaneous/Wind Zone
Component/Miscellaneous/Terrain
Component/Miscellaneous/Billboard Renderer
Component/Event/Event System
Component/Event/Event Trigger
Component/Event/Physics 2D Raycaster
Component/Event/Physics Raycaster
Component/Event/Standalone Input Module
Component/Event/Touch Input Module
Component/Event/Graphic Raycaster
Component/Network/NetworkAnimator
Component/Network/NetworkDiscovery
Component/Network/NetworkIdentity
Component/Network/NetworkLobbyManager
Component/Network/NetworkLobbyPlayer
Component/Network/NetworkManager
Component/Network/NetworkManagerHUD
Component/Network/NetworkMigrationManager
Component/Network/NetworkProximityChecker
Component/Network/NetworkStartPosition
Component/Network/NetworkTransform
Component/Network/NetworkTransformChild
Component/Network/NetworkTransformVisualizer
Component/UI/Effects/Shadow
Component/UI/Effects/Outline
Component/UI/Effects/Position As UV1
Component/UI/Text
Component/UI/Image
Component/UI/Raw Image
Component/UI/Mask
Component/UI/2D Rect Mask
Component/UI/Button
Component/UI/Input Field
Component/UI/Toggle
Component/UI/Toggle Group
Component/UI/Slider
Component/UI/Scrollbar
Component/UI/Dropdown
Component/UI/Scroll Rect
Component/UI/Selectable
```

| 引数 | 戻り値 |
|:-:|:-:|
| "Component"  | すべてのコンポーネント  |
| "Component/UI"  | "Component/UI"以下のコンポーネント  |
| "Hogehoge"  | Null(SubMenuが存在しない)  |

自分が作ったスクリプトは`Component/Scripts/`以下に存在します。そのため
`Unsupported.GetSubmenus("Component/Scripts")`と指定すれば自分のスクリプトだけを取得できます。

ちなみにSubMenuはアトリビュート`[AddComponentMenu ("階層/表示名")]`で指定できます。

```lang:AddComponentMenuをつける
[AddComponentMenu ("Piyo/A")]
public class Test :MonoBehaviour
```

<img width="267" alt="スクリーンショット 2016-08-06 13.33.53.png" src="https://qiita-image-store.s3.amazonaws.com/0/67575/5f0a9020-c619-f5cb-3a7a-1171a1fbf188.png">
<img width="290" alt="スクリーンショット 2016-08-06 13.33.58.png" src="https://qiita-image-store.s3.amazonaws.com/0/67575/e8b5bd7d-e4fa-86db-bb83-8430e0084748.png">

###Unsupported.GetSubmenusCommands関数の取得結果

```lang:Unsupported.GetSubmenusCommands("Component")関数の戻り値(string[])
ADD
33
102
23
137
198
96
120
123
122
119
~~~省略~~~...
SCRIPT1132
SCRIPT1094
SCRIPT1116
SCRIPT1102
SCRIPT1118
SCRIPT1072
SCRIPT1096
SCRIPT1134
SCRIPT1136
SCRIPT1128
SCRIPT1120
SCRIPT1078
SCRIPT1122
SCRIPT1124
SCRIPT1144
SCRIPT1170
SCRIPT9934
SCRIPT1252
```

ここで取得されるのはコンポーネントのIDです。SCRIPT数字の数字部分を使ってコンポーネントのサムネイル画像を取得しています。

```lang:第4引数のcommandStringがコンポーネントID
public ComponentElement(int level, string name, string menuPath, string commandString)
			{
				this.level = level;
				this.typeName = name.Replace(" ", string.Empty);
				this.menuPath = menuPath;
				if (commandString.StartsWith("SCRIPT"))
				{
					int instanceID = int.Parse(commandString.Substring(6));
					UnityEngine.Object obj = EditorUtility.InstanceIDToObject(instanceID);
					Texture miniThumbnail = AssetPreview.GetMiniThumbnail(obj);
					this.content = new GUIContent(name, miniThumbnail);
				}
				else
				{
					int classID = int.Parse(commandString);
					this.content = new GUIContent(name, AssetPreview.GetMiniTypeThumbnailFromClassID(classID));
				}
			}
```


###Unsupported.GetSubmenusとUnsupported.GetSubmenusCommands関数の実際の処理は？
実際はC++内部に隠蔽されています。

```lang:Unsupported.cs
		[WrapperlessIcall]
		[MethodImpl(MethodImplOptions.InternalCall)]
		public static extern string[] GetSubmenusCommands(string menuPath);
		[WrapperlessIcall]
		[MethodImpl(MethodImplOptions.InternalCall)]
		public static extern string[] GetSubmenus(string menuPath);
```

[MethodImpl(MethodImplOptions.InternalCall)]アトリビュートで処理を共通言語ランタイムの内部に任せていて(つまりC++へ処理を渡していて)、
[WrapperlessIcall]アトリビュートは、うーんなんだろう。MethodImpl(MethodImplOptions.InternalCall)と一緒に使われるものなのかな。

> I know WrapperlessIcall usually shows up with with MethodImpl in Unity codes, and they're about calling internal native code, but what is the purpose of WrapperlessIcall, exactly?
Like, Unity maybe use this attribute somewhere in their codes to distinguish the tagged methods, and maybe run automatic register process (mono_add_internal_call) for those tagged methods?
by marsonmao
If I remember correctly, they improved the managed to native call speed in Unity 3. It could be that they introduced a new attribute for that. But at the end it doesn't matter, because we can't use it anyways...
by Dantus
http://forum.unity3d.com/threads/unity-3-beta-wrapperlessicall.56378/

これを読むとMethodImplアトリビュートだけでもC++と連携できるらしいが、WrapperlessIcallアトリビュートがあるとC++へのアクセスが速くなるらしい??

##2:スクリプトからAddする.

```
void Start ()
	{
		var hoge = gameObject.AddComponent <Hoge> ();
	}
```

ちなみにこのようにPrefabにAddComponentすると、

```
public class Test :MonoBehaviour
{
	public GameObject prefab;

	void Start ()
	{
		prefab.AddComponent <Test> ();
	}
}

```

**ゲーム再生中に処理してゲームを停止してもPrefabにTestコンポーネントは残ります**ので注意

GameObjectのAddComponent関数を調べてみると、

```lang:AddComponent関数
		[TypeInferenceRule(TypeInferenceRules.TypeReferencedByFirstArgument)]
		public Component AddComponent(Type componentType)
		{
			return this.Internal_AddComponentWithType(componentType);
		}
		public T AddComponent<T>() where T : Component
		{
			return this.AddComponent(typeof(T)) as T;
		}
```

となっています。
ジェネリックを使ったAddComponent関数で引数にTypeを渡す方のAddComponentを実行しているので、
そっちを使ったほうがわずかに処理は速い。(誤差レベルだろうけど)

###AddComponent関数の速度計測してみた
物は試しということで

```lang:検証コード
public class Test :MonoBehaviour
{
	public GameObject prefab;
	
	public GameObject[] instanceA;
	public GameObject[] instanceB;

	public int count = 1000;

	void Start ()
	{

		instanceA = new GameObject[count];
		instanceB = new GameObject[count];

		for (int i = 0; i < count; i++) {
			instanceA [i] = GameObject.Instantiate <GameObject> (prefab);
			instanceB [i] = GameObject.Instantiate <GameObject> (prefab);
		}

		B ();
		A ();



	}


	void A ()
	{
		Profiler.BeginSample ("Testdayo AAA");
		for (int i = 0; i < instanceA.Length; i++) {
			Hoge a = instanceA [i].AddComponent <Hoge> ();
		}
		Profiler.EndSample ();

	}


	void B ()
	{
		Profiler.BeginSample ("Testdayo BBB");
		for (int i = 0; i < instanceB.Length; i++) {
			Hoge a = instanceB [i].AddComponent (typeof(Hoge)) as Hoge;
		}
		Profiler.EndSample ();
	}
}

```

結果
<img width="570" alt="スクリーンショット 2016-08-06 18.44.32.png" src="https://qiita-image-store.s3.amazonaws.com/0/67575/844de3a5-34b1-d435-ef29-4fc39ba4f56e.png">

20回くらいやってみたけど、だいたいAAAの方(AddComponent<T>)が遅い。