HoloLensでリアルの壁や床に反射するレーザーを撃ってみる #アセットアドカレ

この記事は [Unity Assets Advent Calendar 2016](http://qiita.com/advent-calendar/2016/unity-asset) 20日目の記事になります。

今日はHoloLensの空間認識を利用して、現実の壁とか床とか天井とか机などで反射するレーザーを撃ってみます。

### ArcReactor Rays Generator 1.8 (Dec 16, 2016)
[![image](https://qiita-image-store.s3.amazonaws.com/0/32347/c444c2d4-5e39-cc57-71e1-f97b078aa02d.png)](https://www.assetstore.unity3d.com/jp/#!/content/18445)

反射するビームのポイントとなるのは、Demo3_reflectデモシーンに含まれたLaserLauncher、PlasmaLauncherゲームオブジェクトにアタッチされたArcReactor_Launcher.csになります。これのReflection Settingsで反射させたいLayerを指定。
![image](https://qiita-image-store.s3.amazonaws.com/0/32347/3c54e43b-552c-6165-f678-1a37e33d807b.png)
あと、反射する壁にアタッチされたArcReactorDemo3Mirror.csが実際の反射を行っているようです。
![image](https://qiita-image-store.s3.amazonaws.com/0/32347/87a599b0-e4cd-347f-9d7f-14d43d1f8140.png)


なので、HoloToolkit-Unityに含まれる、空間認識後によしなにPlaneを生成して配置してくれるSpatialProcessingデモシーンをもとに、生成されたPlaneに対してArcReactorDemo3Mirror.csもアタッチするようにちょこっとカスタマイズしていきます。

```csharp:SurfaceMeshesToPlanes.cs

// どっかに追加してインスペクター上で\ArcReactor\Demos\Prefabs\Particles\MirrorHitBubbles.prefabをアタッチする
public GameObject hitParticles;

// 265行目付近に追加
ArcReactor.Demo.ArcReactorDemo3Mirror mirror = destPlane.AddComponent<ArcReactor.Demo.ArcReactorDemo3Mirror>();
mirror.hitParticles = hitParticles;
Gradient g;
GradientColorKey[] gck;
GradientAlphaKey[] gak;
g = new Gradient();
gck = new GradientColorKey[2];
gck[0].color = Color.red;
gck[0].time = 0.0F;
gck[1].color = Color.blue;
gck[1].time = 1.0F;
gak = new GradientAlphaKey[2];
gak[0].alpha = 1.0F;
gak[0].time = 0.0F;
gak[1].alpha = 0.0F;
gak[1].time = 1.0F;
mirror.colorGrad = g;
mirror.rotationSpeed = new Vector3(0f, 0f, 0f);
destPlane.transform.parent = planesParent.transform;
SurfacePlane surfacePlane = destPlane.GetComponent<SurfacePlane>();
```

これでSurfacePlaneがこうなる。
![image](https://qiita-image-store.s3.amazonaws.com/0/32347/5c0151d5-23dc-0608-17cc-56c76f4611b7.png)

## レーザーをAir-Tapで出す

次にDemo3_reflectデモシーンにあったLaserLauncherゲームオブジェクトを参考に、ArcReactor_Launcher.csを適当なゲームオブジェクトを作ってアタッチして値を設定。
Demo3_reflectデモシーンのLaserLauncherゲームオブジェクトをプレハブ化して持ってきてもOK
![image](https://qiita-image-store.s3.amazonaws.com/0/32347/1fd9061c-f050-0cd0-bda0-d79f9643b2f5.png)


### Air-Tapで好きなイベントを実行できる汎用スクリプト

下記のスクリプトを使って、Air-TapでLaserLauncher.csのLaunchRay()が実行されるように設定する。
![image](https://qiita-image-store.s3.amazonaws.com/0/32347/e62de7f0-a24e-de20-15b9-c728f7c7929f.png)

※Managerオブジェクトとかにアタッチして汎用的に使える。

```csharp:AirTapAction.cs
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.VR.WSA.Input;
using HoloToolkit.Unity;

[RequireComponent(typeof(GazeManager))]
public class AirTapAction : MonoBehaviour
{
	GestureRecognizer recognizer;
	
	public UnityEvent myEvent;
	
	void Start()
	{
		recognizer = new GestureRecognizer();
		recognizer.SetRecognizableGestures(GestureSettings.Tap);
		recognizer.TappedEvent += Recognizer_TappedEvent;
		recognizer.StartCapturingGestures();
	}
	
	void OnDestroy()
	{
		recognizer.StopCapturingGestures();
		recognizer.TappedEvent -= Recognizer_TappedEvent;
	}
	
	private void Recognizer_TappedEvent(InteractionSourceKind source, int tapCount, Ray headRay)
	{
		OnTap();
	}
	
	void LateUpdate()
	{
#if UNITY_EDITOR
		if (Input.GetMouseButtonDown(0))
		{
			OnTap();
		}
#endif
	}
	private void OnTap()
	{
		if (myEvent != null)
			myEvent.Invoke();
	}
}
```

## 完成

![image](https://qiita-image-store.s3.amazonaws.com/0/32347/f3c0a18a-6c21-0f96-8cb1-a465874382bf.png)