Unity AR FoundationとPUN2を使ってARシーンを同期する方法

# はじめに
今回がQiita初投稿です。プログラミングは初心者に毛が生えた程度なので、温かい目で見ていただければ幸いです。
また、今回の記事の内容はUnityについてある程度分かる人向けとなっています。
（一応手順をすべて記述したので、初心者の方も丸写しすれば同じことができると思います）
# 概要
今回はUnityのAR Foundationと、Photon Unity Networking2（PUN2）を使用してARシーンの中で3Dオブジェクトの動きを共有する方法をご紹介します。
Unityを使ったARを学習しながら作った関係で、ARのテンプレートシーンを使用しなかったため、初期設定が少し面倒になっています。ご了承ください。

# 今回のゴール
↓PCとスマートフォンで同じシーンを共有して、PCで操作しているオブジェクトをスマートフォンから観察しています。
![Screen_Recording_20240206_171306_TestProject.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/94f2065f-feec-7c21-a1f4-b76a53da17ad.gif)


# 目次
[1. 環境](#1-環境)
[2. パッケージのインポートと初期設定](#2-パッケージのインポートと初期設定)
[3. ARシーンにオブジェクトを表示させる](#3-arシーンにオブジェクトを表示させる)
[4. PUN2を使ってオブジェクトの動きを共有する](#4-pun2を使ってオブジェクトの動きを共有する)
[5. まとめ](#5-まとめ)

# 1. 環境

### OS
- Windows11

### Unity&Assetsバージョン
- Unity -	2022.3.17f1
- AR Foundation -	5.1.1
- Photon Unity Networking 2 - 2.44.0

### テストデバイス
- Galaxy S22 (Android 13)
- Google Pixel 6 (Android 12)



# 2. パッケージのインポートと初期設定

Unity Hubで、新しいプロジェクトから`3D`を選択し、プロジェクトを作成します。

![スクリーンショット (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/66829a23-d7cb-9557-7c06-e3140660ea18.png)

プロジェクトが開いたら、`Main Camera`を選択して削除します。（今回はARカメラを使用するため、通常のカメラは使用しません）

![スクリーンショット (4).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/d5f44a22-eea5-7692-222f-1e5ac2c970ad.png)



## 2-1. AR Foundation
まずはAR Foundationをインストールします。[Window] -> [Package Manager] を選択し、

![スクリーンショット (5).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/0d4e0060-c2a1-2ecf-97da-fed91d104b3d.png)

[Packages: Unity Registry] から`AR`をインストールします。

![スクリーンショット (7).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/3232db32-3785-7a13-f22c-e7c53b69151d.png)

このような警告が表示されるので、Yesを押します。すると、Unityが再起動され、ARパッケージのインストールが完了します。

![スクリーンショット (8).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/b7109e6a-cfb8-1ae0-f0a7-b8c8223f8c5c.png)

---

次に、ARを使うための設定を行います。

<details><summary>Androidでの開発が初めての方はこちらもご確認ください</summary>

>[Unity Hub] -> [インストール] から、右の歯車をクリックし、`モジュールを加える`を選択
>
>![スクリーンショット (14).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/3d9a891a-1077-14dc-2f21-e1adb7d05a8d.png)
>
>プラットフォームの中にある`Android Build Support`がインストールされているか確認してください。
>
>![スクリーンショット (15).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/386efd75-e4b9-7728-41ed-fe06046d12c4.png)
>
</details>

まずは [Edit] -> [Project Settings] を開きます。

![スクリーンショット (16).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/4c6bd9a2-1cbd-4e01-56d5-69e20e01e714.png)

[XR Plug-in Management] の [Android] タブにある`Google ARCore`のチェックボックスを有効にします。

![スクリーンショット (17).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/ce2240aa-3f74-aea9-983f-44ddefbe215b.png)

次に、[File] -> [Build Settings] を開き、

![スクリーンショット (18).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/c88efeeb-a65b-2720-3f58-07577b1dbb3b.png)

`Android`を選択後、[Switch Platform] を押します。

![スクリーンショット (20).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/640f80a2-b8ed-4d9d-646e-344f89237651.png)

:::note warn
この際、Scenes In Buildに何もなければ、シーンを追加してください。
:::

---

次に [Build Settings] ウインドウの左下にある [Player Settings] を開きます。

![スクリーンショット (22).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/ed15bed8-f7c8-e493-6270-b501ce9ab0b6.png)

[Player] -> [Other Settings] の中にある設定を変更していきます。

![スクリーンショット (23).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/461cf02e-497e-d5fb-6730-9ed14d65499d.png)

`Auto Graphics API` のチェックを外し、
[Graphics APIs] の`Vulkan`を選択後 `-` を押して削除し、OpenGLES3のみに設定。

![スクリーンショット (26)_copy.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/2b464636-2f89-39ca-d7a7-a5e8ba24dddd.png)

[Minimum API Level] を `API level 27`以上に設定。

![スクリーンショット (28).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/73701cef-6319-86dc-51aa-15a09dd65a66.png)

[Scripting Backend] を `Mono` から`IL2CPP` に設定。

![スクリーンショット (29).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/53a99d5e-1215-0de9-78e0-2740d7178a67.png)

`ARM64` を有効に設定。

![スクリーンショット (44).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/8fd429f9-f221-bb27-3f3a-0ab195b084a5.png)

お疲れ様でした。
ARの実装に向けた設定は以上です。



## 2-2. PUN2

PUN2のインストールについては、下記リンクを参考にして行ってください。
非常にわかりやすくPUN2の基礎について解説されています。

[PUN2（Photon Unity Networking 2）で始めるオンラインゲーム開発入門](https://zenn.dev/o8que/books/bdcb9af27bdd7d "https://zenn.dev/o8que/books/bdcb9af27bdd7d")




# 3. ARシーンにオブジェクトを表示させる

パッケージのインストールが完了したので、いよいよARのシーン作成をしていきましょう。
ヒエラルキーウインドウを右クリックし、[XR] -> `AR Session` と `XR Origin(Mobile AR)` をシーンに配置します。

![スクリーンショット (10).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/1d2ccca9-f40b-6a0a-cd22-774ed7b0967f.png)

次に、Projectウインドウを右クリックし、[Create] -> [XR] -> `Referance Image Library` を選択して配置します。

![スクリーンショット (33).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/d518e181-8287-2b78-8ff9-43cf147da16f.png)

Referance Image Libraryを選択し、Inspectorから [Add Image] を選択します。

![スクリーンショット (34).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/07797d4a-3f1c-e859-422a-9851883f2d3e.png)

ARのマーカーにしたいものの画像をUnityに取り込み、Inspectorの左上にドラッグアンドドロップします。

![スクリーンショット (45).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/4946abbf-bce6-f641-52b9-99c06d986f77.png)

Specify Sizeのチェックを有効にしてサイズを入力すると、マーカーを検出しやすくなります。



![スクリーンショット (35)_copy.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/69742e72-5a80-5af5-e438-e17c5cbdcdd0.png)

:::note warn
ここのX、Yの __１__ は、ARシーンでは __１m__ に相当します。
__cm__ ではない点に注意してください！
:::

次にXR Originを選択し、Inspector上の [Add Component] から `AR Tracked Image Manager` を追加します。

![スクリーンショット (36).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/e5d47e40-3e0a-80da-fd41-643fee463aa7.png)

[Serialized Library] に先ほどの `Referance Image Library` を、
[Tracked Image Prefab] に表示したいオブジェクトのプレハブを設定します。

![スクリーンショット (43).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/a0ced288-6a33-09dc-b1b0-a367d31091f7.png)

ARの表示準備が整いました。AndroidデバイスをPCにつなげてビルドしてみましょう。
<details><summary>ビルドができなかった場合</summary>

### ビルドができなかった場合

> Androidでのビルドができなかった場合、考えられる主な原因は以下の3つです。
> 
> - シーンがBuild Settingsに入っていない
> - ビルドの目標プラットフォームがAndroidに設定されていない
> - Android端末が開発者モードになっていない
>
>一つずつ解決策を提示していきます。
>
>#### シーンがBuild Settingsに入っていない
>
>[Build Settings] ウインドウの上部に Scenes In Build という項目があり、ここに制作したシーンが入っていないとビルドできません。Add Open Scenesを押すか、Projectウインドウの [Scenes] フォルダからシーンをドラッグアンドドロップしてビルドシーンを設定しましょう。
>
>#### ビルドの目標プラットフォームがAndroidに設定されていない
>
>これも同じく[Build Settings] ウインドウの左側にあるPlatform から `Android` を選択してSwitch Platformボタンを押して変更してください。
>
>#### Android端末が開発者モードになっていない
>
>Android端末が開発者モードに入っていないと、ビルドしたファイルを端末に送ることができません。
>開発者モードのオン/オフの方法は端末によって異なりますので、インターネットで調べて変更してください。
>
>#### そのほかの原因
>
>これら以外にもビルドできない原因は存在しますので、ビルドができなかった場合はコンソールウインドウのエラーログを確認してみてください。
</details>

![Screen_Recording_20240205_162655_TestProject.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/b759a3a5-b7fb-b42b-d236-0c58d0b49cd9.gif)


:::note
無事にマーカーの上にオブジェクトが出現しました。
:::




# 4. PUN2を使ってオブジェクトの動きを共有する

ARシーンの準備ができたので、次はPUN2を使った同期をしていきます。

## 4-1.同期されるオブジェクトの作成
まずは、シーン上で同期されるネットワークオブジェクトを作成します。

:::note
今回は例としてCubeで作成しましたが、オブジェクトは何でも大丈夫です。
大体の流れをつかんでいただければ幸いです。
:::

ヒエラルキーウインドウを右クリックし、[3D Object] から適当なオブジェクトを作成します。

![スクリーンショット (47).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/73b9ec30-4117-6fca-88ea-518832cc2a3f.png)

作成したCubeのInspectorから少し設定を行います。
ScaleをARシーン上で見える大きさに調整し、マーカーにめり込まないようにY座標も調整しましょう。

:::note warn
ScaleやPositionの __１__ は、ARシーンでは __１ｍ__ に相当します。
しっかり見える大きさに設定しましょう。
:::

![スクリーンショット 2024-02-06 144249.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/c2e3d8a1-63b1-f17c-48af-eef7e0b00ef3.png)

次に、同じInspectorウインドウの下部にある [Add Component] -> [Photon Networking]から、

![スクリーンショット 2024-02-06 145218.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/a23ba7e4-ff0c-7af4-bd2d-1b7017484913.png)

`Photon View`と`Photon Transform View`を追加します。

![スクリーンショット 2024-02-06 145238.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/84d29fd4-63dc-1ae7-a641-0480762e50c9.png)

さらに、プロジェクトウインドウを右クリックし、[Create] -> [C# Script] から、`PlayerController`というスクリプトを作成してください。

```cs:PlayerController.cs
using UnityEngine;
using Photon.Pun;

public class PlayerController : MonoBehaviourPunCallbacks
{
    void Update()
    {
        //自分のオブジェクトでなければ処理しない
        if (!photonView.IsMine) return;

        var input = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical"));
        transform.Translate(0.1f * Time.deltaTime * input.normalized);
    }
}


```

作成が終わったら、先ほどのCubeにアタッチしてください。

仕上げとして、[Assets] フォルダの中に`Resources`という名前のフォルダを作成し、Cubeをドラッグアンドドロップで格納してプレハブ化すれば、ネットワークオブジェクトの準備完了です。
ヒエラルキーウインドウ上にあるCubeは不要なので削除してください。

## 4-2. PUN2のサーバーに接続する
次にPUN2のサーバーに接続する準備をします。
`PUN2Manager`というスクリプトを作ったら、ヒエラルキーウインドウを右クリックし、 [Create Enpty] を選択して出てくる空のオブジェクトにアタッチしてください。（名前はなんでも大丈夫ですが、分かりやすく`PUN2ManagerObj`などにすると良いと思います）

```cs:PUN2Manager.cs
using UnityEngine;
using Photon.Pun;
using Photon.Realtime;

public class PUN2Manager : MonoBehaviourPunCallbacks
{
    [SerializeField] private GameObject _playerPrefab;
    
    void Start()
    {
        // PhotonServerSettingsの設定内容を使ってマスターサーバーへ接続する
        PhotonNetwork.ConnectUsingSettings();
    }

    // マスターサーバーへの接続が成功した際に呼ばれる
    public override void OnConnectedToMaster() {
        // "Room"という名前のルームに参加する（ルームが存在しなければ作成する）
        PhotonNetwork.JoinOrCreateRoom("Room", new RoomOptions(), TypedLobby.Default);
    }

    // ルームへの接続が成功した際に呼ばれる
    public override void OnJoinedRoom() {
        var position = new Vector3(Random.Range(-0.1f, 0.1f), 0.025f, Random.Range(-0.1f, 0.1f));
        PhotonNetwork.Instantiate(_playerPrefab.name, position, Quaternion.identity);
    }
}
```
これで、起動時に自動的にPUN2のサーバーに接続されるようになりました。

## 4-3. ARシーンの原点を固定する

次に、ARの出現位置を合わせるために原点をマーカーに固定する処理を実装します。
以下の二つのスクリプトを作成し、二つ目の`OriginDecideFromImageMaker`のみヒエラルキーウインドウを右クリックし、 [Create Enpty] を選択して出てくる空のオブジェクトにアタッチしてください。（名前はなんでも大丈夫ですが、分かりやすく`OriginDecideFromImageMakerObj`などにすると良いと思います）

:::note warn
この二つのスクリプトは引用、または大部分を参考にさせていただき開発環境に合わせて一部を変更したものです。
スクリプトの下部に引用元を示してありますので、そちらもご覧ください。
:::

>```cs:XROriginExtensions
>using System;
>using Unity.XR.CoreUtils;
>using UnityEngine;
>
>namespace UnityEngine.XR.ARFoundation.Samples
>{
>    /// <summary>
>    /// This class contains extension methods suitable for replacing the <c>ARSessionOrigin.MakeContentAppearAt</c>
>    /// API as existed in AR Foundation 4.2, allowing users to upgrade projects from
>    /// <see cref="ARSessionOrigin"/> to <see cref="XROrigin"/> with continued access to this API. 
>    /// </summary>
>    public static class XROriginExtensions
>    {
>        /// <summary>
>        /// Makes <paramref name="content"/> appear to be placed at <paramref name="position"/> with orientation <paramref name="rotation"/>.
>        /// </summary>
>        /// <param name="origin">The <c>XROrigin</c> in the Scene.</param>
>        /// <param name="content">The <c>Transform</c> of the content you wish to affect.</param>
>        /// <param name="position">The position you wish the content to appear at. This could be
>        /// a position on a detected plane, for example.</param>
>        /// <param name="rotation">The rotation the content should appear to be in, relative
>        /// to the <c>Camera</c>.</param>
>        /// <remarks>
>        /// This method does not actually change the <c>Transform</c> of content; instead,
>        /// it updates the <c>XROrigin</c>'s <c>Transform</c> so the content
>        /// appears to be at the given position and rotation. This is useful for placing AR
>        /// content onto surfaces when the content itself cannot be moved at runtime.
>        /// For example, if your content includes terrain or a NavMesh, it cannot
>        /// be moved or rotated dynamically.
>        /// </remarks>
>        public static void MakeContentAppearAt(this XROrigin origin, Transform content, Vector3 position, Quaternion rotation)
>        {
>            MakeContentAppearAt(origin, content, position);
>            MakeContentAppearAt(origin, content, rotation);
>        }
>
>        /// <summary>
>        /// Makes <paramref name="content"/> appear to be placed at <paramref name="position"/>.
>        /// </summary>
>        /// <param name="origin">The <c>XROrigin</c> in the Scene.</param>
>        /// <param name="content">The <c>Transform</c> of the content you wish to affect.</param>
>        /// <param name="position">The position you wish the content to appear at. This could be
>        /// a position on a detected plane, for example.</param>
>        /// <remarks>
>        /// This method does not actually change the <c>Transform</c> of content; instead,
>        /// it updates the <c>XROrigin</c>'s <c>Transform</c> so the content
>        /// appears to be at the given position.
>        /// </remarks>
>        public static void MakeContentAppearAt(this XROrigin origin, Transform content, Vector3 position)
>        {
>            if (content == null)
>                throw new ArgumentNullException(nameof(content));
>
>            var originTransform = origin.transform;
>            
>            // Adjust the Camera Offset transform to account
>            // for the actual position we want the content to appear at.
>            origin.CameraFloorOffsetObject.transform.position += originTransform.position - position;
>
>            // The XROrigin's position needs to match the content's pivot. This is so
>            // the entire XROrigin rotates around the content (so the impression is that
>            // the content is rotating, not the rig).
>            originTransform.position = content.position;
>        }
>
>        /// <summary>
>        /// Makes <paramref name="content"/> appear to have orientation <paramref name="rotation"/> relative to the <c>Camera</c>.
>        /// </summary>
>        /// <param name="origin">The <c>XROrigin</c> in the Scene.</param>
>        /// <param name="content">The <c>Transform</c> of the content you wish to affect.</param>
>        /// <param name="rotation">The rotation the content should appear to be in, relative
>        /// to the <c>Camera</c>.</param>
>        /// <remarks>
>        /// This method does not actually change the <c>Transform</c> of content; instead,
>        /// it updates the <c>XROrigin</c>'s <c>Transform</c> so that the content
>        /// appears to be in the requested orientation.
>        /// </remarks>
>        public static void MakeContentAppearAt(this XROrigin origin, Transform content, Quaternion rotation)
>        {
>            if (content == null)
>                throw new ArgumentNullException(nameof(content));
>
>            // Since we aren't rotating the content, we need to perform the inverse
>            // operation on the XROrigin. For example, if we want the
>            // content to appear to be rotated 90 degrees on the Y axis, we should
>            // rotate our rig -90 degrees on the Y axis.
>            origin.transform.rotation = Quaternion.Inverse(rotation) * content.rotation;
>        }
>    }
>}
>```
`XROriginExtensions`引用元：

https://github.com/Unity-Technologies/arfoundation-samples/issues/950

https://github.com/Unity-Technologies/arfoundation-samples/blob/main/Assets/Scripts/Runtime/XROriginExtensions.cs

---


>```cs:OriginDecideFromImageMaker
>using System.Collections;
>using Unity.XR.CoreUtils;
>using UnityEngine;
>using UnityEngine.XR.ARFoundation;
>using UnityEngine.XR.ARFoundation.Samples;
>
>/// <summary>
>/// 画像マーカーから原点を定める
>/// </summary>
>public class OriginDecideFromImageMaker : MonoBehaviour
>{
>    /// <summary>
>    /// ARTrackedImageManager
>    /// </summary>
>    [SerializeField] private ARTrackedImageManager _imageManager;
>
>    /// <summary>
>    /// ARSessionOrigin
>    /// </summary>
>    [SerializeField] private XROrigin _sessionOrigin;
>
>    /// <summary>
>    /// ワールドの原点として振る舞うオブジェクト
>    /// </summary>
>    private GameObject _worldOrigin;
>
>    /// <summary>
>    /// コルーチン
>    /// </summary>
>    private Coroutine _coroutine;
>
>    private void OnEnable()
>    {
>        _worldOrigin = new GameObject("Origin");
>        _imageManager.trackedImagesChanged += OnTrackedImagesChanged;
>    }
>
>    private void OnDisable()
>    {
>        _imageManager.trackedImagesChanged -= OnTrackedImagesChanged;
>    }
>
>    /// <summary>
>    /// 原点を定める
>    /// 今回は画像マーカーの位置が原点となる
>    /// </summary>
>    /// <param name="trackedImage">認識した画像マーカー</param>
>    /// <param name="trackInterval">認識のインターバル</param>
>    /// <returns></returns>
>    private IEnumerator OriginDecide(ARTrackedImage trackedImage,float trackInterval)
>    {
>        yield return new WaitForSeconds(trackInterval);
>        var trackedImageTransform = trackedImage.transform;
>        _worldOrigin.transform.SetPositionAndRotation(Vector3.zero,Quaternion.identity);
>        _sessionOrigin.MakeContentAppearAt(_worldOrigin.transform, trackedImageTransform.position,trackedImageTransform.localRotation);
>        _coroutine = null;
>    }
>
>    /// <summary>
>    /// ワールド座標を任意の点から見たローカル座標に変換
>    /// </summary>
>    /// <param name="world">ワールド座標</param>
>    /// <returns></returns>
>    public Vector3 WorldToOriginLocal(Vector3 world)
>    {
>        return _worldOrigin.transform.InverseTransformDirection(world);
>    }
>
>    /// <summary>
>    /// TrackedImagesChanged時の処理
>    /// </summary>
>    /// <param name="eventArgs">検出イベントに関する引数</param>
>    private void OnTrackedImagesChanged(ARTrackedImagesChangedEventArgs eventArgs)
>    {
>        foreach (var trackedImage in eventArgs.added)
>        {
>            StartCoroutine(OriginDecide(trackedImage,0));
>        }
>
>        foreach (var trackedImage in eventArgs.updated)
>        {
>            if(_coroutine == null)  _coroutine = StartCoroutine(OriginDecide(trackedImage, 5));
>        }
>    }
>}
>```
`OriginDecideFromImageMaker`引用・参考元：

https://qiita.com/OKsaiyowa/items/597c5db17be40ab3b265


## 4-4. 締めのアタッチ祭り
これが最後の作業になります。ミスが起こった場合は発見しにくいので慎重に作業しましょう。
今まで作成してきたオブジェクトやスクリプトがお互いを認識できるように、Inspector上でアタッチしていきます。

`PUN2Manager`をアタッチしたオブジェクトのInspectorにある [Player Prefab] に、Resourceフォルダにある4-1で作成したネットワークオブジェクトをドラッグアンドドロップして設定しましょう。

![スクリーンショット 2024-02-06 163900.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/da607fe0-858d-1f16-5895-8a1e23114cc5.png)

同様に、`OriginDecideFromImageMaker`をアタッチしたオブジェクトのInspectorにある [Image Manager] と [Session Origin] に、ヒエラルキーウインドウにあるXR Originをドラッグアンドドロップして設定しましょう。

![スクリーンショット 2024-02-06 165359.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/163dbaaa-b24b-7724-5936-650f5ed6cb10.png)

最後に、XR Originの[Tracked Image Prefab]をNoneに設定してください。(ARシーンの確認の際に何かしら設定していると思います。)

![スクリーンショット 2024-02-06 165417.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/a05ed9e1-af80-5726-93dd-12b583e5d611.png)


お疲れさまでした！これでビルドを行えば、ARシーンの中でオブジェクトの動きを同期することができます。

↓PCとスマートフォンで同じシーンを共有して、PCで操作しているオブジェクトをスマートフォンから観察しています。
![Screen_Recording_20240206_171306_TestProject.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692466/94f2065f-feec-7c21-a1f4-b76a53da17ad.gif)


# 5. まとめ

今回は、AR FoundationとPhoton Unity Networking2を使用して、ARシーン内のオブジェクトの動きを共有する方法について解説しました。
本当に動きを共有するところまでしか作っていないので、スマートフォンでの操作などは改めて実装する必要があります。また機会があれば、そこについても記事を作りたいです。

丁寧に解説しようとしたらとんでもない長文になってしまいましたが、ここまで読んでいただきありがとうございました。
問題点や改善点、ご質問などがありましたら教えてください。

# 参考文献

https://qiita.com/DI_198/items/0b2518c3c6593331d187

https://github.com/Unity-Technologies/arfoundation-samples/issues/950

https://qiita.com/OKsaiyowa/items/597c5db17be40ab3b265


[PUN2（Photon Unity Networking 2）で始めるオンラインゲーム開発入門](https://zenn.dev/o8que/books/bdcb9af27bdd7d "https://zenn.dev/o8que/books/bdcb9af27bdd7d")