GoogleのAR技術Tango+UnityでAR空間を塗るアプリ、「RAKUGAKI」の紹介

#コンセプト
「ひみつ道具」のようなアプリを作りたい。そんな思いから４人でチームを組みこのアプリは開発されました。スプレー缶をイメージして、端末を振るとカラカラ音が鳴り、それに加えスプレーの噴射音も再現しました。
![logo.png](https://qiita-image-store.s3.amazonaws.com/0/183210/67e34122-2619-d802-09a1-11e0672ba4ec.png)
タイトルデザイン:[とみぎさん](https://twitter.com/_tomigie_)
#Tangoについて軽く紹介
Googleの持つAR(拡張現実）技術でモーショントラッキング (Motion Tracking)、エリア学習 (Area Learning)、深度検出 (Depth Perception)などの機能をもちいて空間の情報をより詳細に取得することができるものです。詳しい記事は他にもあるので調べて見るといいかもしれません。
#準備
[Tango Unity SDK](https://developers.google.com/tango/apis/unity/)をダウンロードします。事前にUnityが入ってない場合はインストールする必要があります。あとはpackage形式なのでUnityを起動してimportすれば普通に実装するときには使えます。ビルドの際にはまた設定が必要ですが今回は省略します。
#アプリのしくみの簡単な説明
・Tangoのモーショントラッキング機能を使って端末の振動を検出し、それでカラカラ音を鳴らせるようにする。
・UnityでAR空間に対して色（可変式）の付いたダイナミックメッシュを生成することで空間に色を塗ることができる。
> このダイナミックメッシュの生成ですが、Tango側の方で初期化がかかって、色を塗った部分がすぐ消えてしまうのをどうにかするのが大変だったと、チームの担当の人が言っていて解決する技術力に凄さを感じました。[僕](https://twitter.com/ItinoseVM)は主にGUIの仕組みを設計したコーダーだったので頭が下がります。

#実装
記事を読んでいる皆さんが気になる色を塗るところの実装を抜粋してみました
実装者：[うじまるさん](https://twitter.com/uzimaru0601)
・ダイナミックメッシュの生成

```c#
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;
 
public class Paint : MonoBehaviour {
 
    public enum SprayColor { //色の種類を列挙
        Yellow,
        Red,
        Pink,
        Green,
        Blue
    }
 
    public Text debugText;
    public Material material;
 
    SprayColor paintColor = SprayColor.Yellow;　//今回は最初の色を黄色に設定しました
    Color[] paintColors = {　//配列で色の種類を定義
        Color.yellow,
        Color.red,
        Color.magenta,
        Color.green,
        Color.blue
    };
 
    Dictionary<string, GameObject> meshObjects = new Dictionary<string, GameObject>();
 
    public void Painting(int num) {　//cameraからrayを飛ばしていく感じ
        var cam = Camera.main;
        RaycastHit hit;
        for (var i = 0; i < num; i++) {
            var rand = cam.transform.TransformDirection(Random.insideUnitCircle) * 0.05f;
            var dir = cam.transform.forward + rand;
            if (Physics.Raycast(cam.transform.position, dir, out hit)) {
                var meshCollider = hit.collider as MeshCollider;
                if (!meshCollider || !meshCollider.sharedMesh) return;
                PaintMesh(meshCollider, hit.triangleIndex);
            }
        }
    }
 
    void PaintMesh(MeshCollider collider, int triangleIndex) {　//ダイナミックメッシュ生成部分
        var name = collider.gameObject.name;
        if (!meshObjects.ContainsKey(name)) {
            var obj = collider.gameObject;
            var newObj = Instantiate(obj, obj.transform.position, obj.transform.rotation);
            meshObjects[name] = newObj;
            var instanceMesh = Instantiate(collider.sharedMesh);
            meshObjects[name].GetComponent<MeshRenderer>().material = material;　　　
            meshObjects[name].GetComponent<MeshFilter>().sharedMesh = instanceMesh;
            Destroy(meshObjects[name].GetComponent<MeshCollider>());
        }
        var mesh = meshObjects[name].GetComponent<MeshFilter>().sharedMesh;
        var vert = mesh.vertices;
        var triangle = mesh.triangles;
        var colors = mesh.colors;
        if (colors.Length <= 0) {
            colors = (new Color[vert.Length]).Select(x => Color.white).ToArray();
        }
        colors[triangle[triangleIndex * 3 + 0]] = paintColors[(int)paintColor];
        colors[triangle[triangleIndex * 3 + 1]] = paintColors[(int)paintColor];
        colors[triangle[triangleIndex * 3 + 2]] = paintColors[(int)paintColor];
        mesh.colors = colors;
    }
 
    public void ChangeColor(int col) {　//色の変更をする関数
        paintColor = (SprayColor)System.Enum.ToObject(typeof(SprayColor), col);
    }
}
```
こんな感じです。コードの説明はまた編集で加えていこうと思います。
#デモ

![image.png](https://qiita-image-store.s3.amazonaws.com/0/183210/5da6d0fc-6c3d-c471-21dc-12dc52100407.png)
画像は[Mizutoriさん](https://twitter.com/mizutory)提供
#今回の開発について軽く感想
チームでやったのは今回が初めてなんですが、複数人のひらめきによりアプリができていく感覚が楽しかったです。またこうした機会に巡り会えることを願っています。（なんかここだけブログっぽくなってしまった気が...)

最後まで読んでいただきありがとうございました。