Posted at

Unityでインスタ映え?するAR作ってみた。


はじめに

前回書いたiPhoneのARアプリの記事からグレードアップしたものを作ろうということで、

好きな文字や飾りを出すiPhoneのARアプリを作成しました。

流行に乗ってインスタ映え狙ってみました!(インスタやってないですがww)


作ったものは??

これです!!

こだわった点は、以下3点です。

①オリジナルのモデルを作成をし、Unityで使う。

②オープニングとタイトル、メインの3シーン構成。

③メニュー画面を作り、好きな飾りや入力した文字を空間に出せる。

①のモデル作成については過去の記事で詳細に解説していますのでご覧下さい。

この記事では②と③の仕組み・プロセスを解説していきます。


事前準備

前回書いたiPhoneのARアプリの記事の「1.事前準備」と同じです!

(1) Unityをインストール →バージョンは「2019.1.10f1」を利用

(2) Unity-ARKit-Plugin をダウンロード

(3) Xcodeをインストール

(4) iOSを12にアップデート


いざ、実装へ!

このARアプリは、以下の3シーンから成っています。

1. オープニング

2. タイトル

3. メイン

全てのシーンの大まかな作成手順は、「新規シーン作成 → UI作成 → スクリプト作成 」と単純です。

ではそれぞれのシーン作成に入っていきます。


◆オープニング作成


・手順

①まずはメニュータブの「File > New Scene」で新規シーンを作成。

 「Game」の画面サイズを"Free Aspect"から"iPhone 1334×750 Portrait(750×1334)1"に変更。

 そして「Hierarchy」のCreateから「UI > Text」をクリック。

②Canvas内にTextオブジェクトとEventSystemオブジェクト2が生成される。

 Textオブジェクトの「Inspector」のRect Transformからオープニング位置を設定。

 またText(Script)からオープニング名や文字サイズ、色などを設定。

③ProjectのCreateから「C#Script」をクリックし、スクリプトを生成。



 ソースは以下の通り。


OpeningController.cs

using System.Collections;

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

public class OpeningController : MonoBehaviour
{
public Text text;
private float textColor;

// Start is called before the first frame update
void Start()
{
textColor = 0.0f;
//DelayMethodを5秒後に呼び出す
Invoke("DelayMethod", 5.0f);
}

// Update is called once per frame
void Update()
{
//徐々にテキストの文字色を浮かび上がらせる
if(textColor <= 1){
textColor += 0.01f;
text.color = new Color(255, 255, 255, textColor);
}
}
//タイトルシーンをロードする。
void DelayMethod(){
SceneManager.LoadScene("Title");
}
}


④作成したスクリプトをGameObjectにドラッグ&ドロップし、オープニングシーン完成!!


・完成イメージ

「Up All Night Project Inc.」という会社は架空ですww


◆タイトルシーン作成


・オープニングシーンとの違い

流れはオープニングシーンと同様です!

主な違いは、スカイボックスの変更とスクリプトだけ。

スカイボックスとは簡単に言うと、シーンの背景を設定するもの(デフォルトは青空の背景)

参考リンク

メニュータブの「Window > Rendering > Lighting Settings」で変更可能。

今回はオシャレに宇宙の背景で!Asset Storeからインポートしました。

・Skybox Series Free

https://assetstore.unity.com/packages/2d/textures-materials/sky/skybox-series-free-103633



↓ソースは以下の通り。スカイボックス(背景)が動くようにしています。


TitleContoroller.cs

using System.Collections;

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

public class TitleContoroller : MonoBehaviour
{
// 回転スピード
[SerializeField]
private float rotateSpeed = 5.0f;
// スカイボックスのマテリアル
private Material skyboxMaterial;

// Use this for initialization
void Start () {
// Lighting Settingsで指定したスカイボックスのマテリアルを取得
skyboxMaterial = RenderSettings.skybox;
}

// Update is called once per frame
void Update () {
// スカイボックスマテリアルのRotationを操作して角度を変化させる
skyboxMaterial.SetFloat("_Rotation", Mathf.Repeat(skyboxMaterial.GetFloat("_Rotation") + rotateSpeed * Time.deltaTime, 360f));

//画面をタッチしたらメインシーンをロード
if (Input.touchCount > 0 ){
SceneManager.LoadScene("Main");
}
}
}



・完成イメージ


◆メインシーン作成

さて、いよいよメインシーンに入っていきます!


・仕様

メインシーンの仕様としては、初期画面では左下にボタンが1つだけあるだけで、ボタン以外の画面部分をタップしても何も反応しません。

ボタンを押すことでUIのCanvasが活性化し、メニュー画面を表示します。

そして、オブジェクトを選択し、再度ボタンを押下することでCanvasを非活性にし、メニュー画面を非表示にします。あとは画面でタップした位置に選択したオブジェクトが表示されるという仕組みです!

ちなみに作成したモデル以外に利用したオブジェクト(フルーツやプレゼント箱など)は、

前回のiPhoneのARアプリの記事で紹介したAsset Storeのものです。


・ARkitから利用するものをピックアップ

メインシーンは、ARkitの「UnityARKitScene」というサンプルを元に作成してきます。

その中で必要なオブジェクトは以下の4つ3なので、他は削除します。シーン名もMainに変えてます。


・初期画面のボタンの作り方

「Hierarchy」のCreateから「UI > Button」をクリックし、ボタンを作成。

またボタン名を表示するために「UI > Text」を生成し、Button配下にドラッグ&ドロップする。

そしてCanvasの「Inspector」内のCanvas Scalerのプロパティ4を修正する。

ボタン押下時のスクリプトを生成する。

ソースは以下。次に実装するメニュー画面のCanvasの活性・非活性の制御を実装。


ButtonScript.cs


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

public class ButtonScript : MonoBehaviour
{
public GameObject canvas;
public bool viewCanvas = false;

//ボタン押下時に、メニュー画面のCanvasの活性・非活性の制御を行う。
public void OnClick() {
Debug.Log("click!");
if(viewCanvas){
canvas.SetActive (false);
viewCanvas = false;
}else{
canvas.SetActive (true);
viewCanvas = true;
}
}
}


作成したスクリプトをButtonにドラッグ&ドロップし、

Button「Inspector」でボタン押下時にスクリプトを呼び出すように設定する。



↓この時点ではまだボタンがあるだけ。


・メニュー画面の作り方

「Hierarchy」のCreateから「UI > Panel」をクリックし、パネルを作成。

その上にInputFieldとButton(Imageの上に並べた)を配置。今回選択肢は7種類なのでボタンを7個用意。

ボタンには文字ではなく画像を貼り付けるので、Button配下のTextは削除して下さい。

また、ここもCanvasの「Inspector」内のCanvas Scalerのプロパティ4を修正しておきましょう。

ボタンへの画像貼り付けは、この記事をご参考ください。

今回、画像はモデルに合ったフリー素材をネットから拾ってきました。



そしてCanvasの「Inspector」内のチェックボックスを外しておく。


・メニュー画面のボタン実装

今回メニュー画面で選べるオブジェクトは"モデル"と"テキスト"の2種類があります。

メニュー画面ボタンのソースは以下。

簡単に処理を説明すると、メニュー画面で押下されたオブジェクトを画面でタップされた時に表示させるためにセットする処理です。


CreateObjMerge.cs


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.iOS;
using UnityEngine.EventSystems;

public class CreateObjMerge : MonoBehaviour
{
//メニュー画面のボタンは7個あるが、他のボタンも同じ処理のため他は省略。
//ボタン分の数、プロパティとメソッドを、frame2,frame3....で用意する。
public GameObject frame1;

GameObject TargetObj;
TouchCreateObjMerge obj;

public void Frame1()
{
//"Game_yasuko"というゲームオブジェクトの"TouchCreateObjMerge"コンポーネント(スクリプト)のdecoratePrefabプロパティにオブジェクトをセット。
TargetObj = GameObject.Find("Game_yasuko");
obj = TargetObj.GetComponent<TouchCreateObjMerge>();
obj.decoratePrefab = frame1;
obj.textFlug = false;
}
}


「Hierarchy」のCreateからCreate Emptyをクリックし、オブジェクトを作成。

上記で作成したスクリプトをドラッグ&ドロップし、7種類のモデルをframeプロパティにセット。



そしてメニュー画面の各ボタンに上記のソースをメソッド出すようにそれぞれ設定する。


・InputFieldの実装

好きな文字を空間に出すために、InputFieldに文字が入力される毎に文字をセットします。

ソースは以下です。


ChangeText.cs

using System.Collections;

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class ChangeText : MonoBehaviour
{
public string changeText = "";
public InputField inputField;

public void InputText() { // 必ず public にする。
//テキストにinputFieldの内容を反映
changeText = inputField.text;
}
}


InputFieldで上記のスクリプトを呼び出すように設定します。


・画面タップ時の実装

さて、いよいよメインシーン最後の工程です!

メニュー画面で選択されたオブジェクトを画面でタップされた位置に表示させる実装をします。

ソースは以下です。


TouchCreateObjMerge.cs

using System.Collections;

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.iOS;
using UnityEngine.UI;
using UnityEngine.XR.iOS;

public class TouchCreateObjMerge : MonoBehaviour
{
public GameObject decoratePrefab;
public GameObject inputField;
public GameObject button;
private bool viewCanvas = false;
private ChangeText textScript;
private ButtonScript buttonScript;
public bool textFlug = false;

// オブジェクト生成メソッド
void CreateObj(Vector3 atPosition)
{
// メニュー画面で選択されたオブジェクトをWorld座標の位置に生成する。
GameObject decorateObj = Instantiate(decoratePrefab, atPosition, Quaternion.identity);
if (textFlug){
//テキストの場合は正面を向くので向きを調整は不要。
decorateObj.transform.Rotate(0.0f, 0.0f, 0.0f, Space.World);
//InputFieldで入力された文字をセット。
string text = textScript.changeText;
TextMesh textMesh = decorateObj.GetComponent<TextMesh>();
textMesh.text = text;
}
else
{
//モデルが正面を向くように向きを調整。
decorateObj.transform.Rotate(0.0f, 180.0f, 0.0f, Space.World);
}
}

// Use this for initialization
void Start()
{
textScript = inputField.GetComponent<ChangeText>();
buttonScript = button.GetComponent<ButtonScript>();
}

void Update()
{
//画面にタッチ、かつメニュー画面が活性化していない時
if (Input.touchCount > 0 && !buttonScript.viewCanvas)
{
var touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Began)
{
// タッチされたスクリーンの座標をセット
var screenPosition = Camera.main.ScreenToViewportPoint(touch.position);
ARPoint point = new ARPoint
{
x = screenPosition.x,
y = screenPosition.y
};

// スクリーンの座標をWorld座標に変換
List<ARHitTestResult> hitResults = UnityARSessionNativeInterface.GetARSessionNativeInterface().HitTest(point, ARHitTestResultType.ARHitTestResultTypeFeaturePoint);
if (hitResults.Count > 0)
{
foreach (var hitResult in hitResults)
{
Vector3 position = UnityARMatrixOps.GetPosition(hitResult.worldTransform);
// オブジェクト生成メソッドにWorld座標を渡す。
CreateObj(new Vector3(position.x, position.y, position.z));
break;
}
}
}
}
}
}


「Hierarchy」のCreateからCreate Emptyをクリックし、オブジェクトを作成。

上記で作成したスクリプトをドラッグ&ドロップし、「メニュー画面のボタン実装」で作成したオブジェクトとInputField、初期画面のボタンをセット。

以上で完成です!!


◆ビルド

あとは、3シーンをまとめてビルドします!

各シーンを開いて、メニューバーの「File > Build Settings」から以下の画面を開く。

"Add Open Scenes"を押下するとそのシーンがScenes In Buildに表示されるので、あとは順番を揃えて"Build And Run"を押下。

自分のiPhoneを接続しておくと、Unityでビルド完了後にXcodeが開くのでiPhoneにビルドする。


苦労したこと


・平面にしかオブジェクトを出せない?

最初は「UnityARKitScene」サンプルで平面上にしかオブジェクトを出せませんでしたが、ARkitのHitTestメソッドでオプションを変えれば空間にオブジェクトを出現できました!!

使ったオプションは「ARHitTestResultTypeFeaturePoint」で、ARKitで検出された特徴点を対象に当たり判定を行うものです。

↓以下の記事、参考にさせていただきました。

http://nn-hokuson.hatenablog.com/entry/2018/09/20/202442


・初めてのC#

Unityは実質選択肢はC#のみということで最初は不安でした。。

が同じオブジェクト志向のJavaを業務で経験していたので、サンプルを読んだり、実装していくうちに慣れてきました。


・シーンをセーブせずに次のシーン作成をし、設定が消えた。

初歩的ミスですww 皆様お気をつけて。

Unityは日本語ドキュメントがあり、情報も出回っているので調べれば大体出てきます。

サンプルも豊富で、参考にできる機能も多いので比較的苦労せずにできました。


さいごに

インスタ映えするかどうかは皆様のご判断に任せます。。。汗

ただ作りたかったものは実現できました!

まだ細かい点などブラッシュアップできるところはありますが、あわよくばApple Storeに出そうかなとも:rolling_eyes:(しかし年間参加費約1.2万円がネック。)

UnityはARだけでなくVRや2D・3Dゲームなど幅広くあるので、少しでも興味があればぜひトライしてみて下さい!

そして当記事がご参考になれば幸いです。ありがとうございました!





  1. 今回はiPhone8用に画面サイズを指定しましたので、機種によって画面サイズを変更して下さい。 



  2. UIオブジェクトを作ると、必ずこのEventSystemが作られる。このEventSystemはプレイヤーのUIへの入力を制御していていて、UIを使う時は必ず1つ必要になる。基本デフォルトのままで大丈夫で、今回手はつけません。 



  3. PointCloudParticleExampleはカメラで得られた特徴点(物と物の境界など)を表示するコンポーネント。アプリで小さな黄色い点で表示されているやつ。 



  4. Scale With Screen Sizeは、画面サイズによってGUIを拡大、縮小してくれる。