Closed5

ある場所(緯度経度高度不明)に配置されたARオブジェクトの位置を保存・読み込む

Ekito|駅人Ekito|駅人

はじめに

AR空間に配置されたオブジェクトの位置を保存し、アプリを再び起動したらその位置にオブジェクトを再配置する方法を考える。

たとえば、こんな風に空に浮かんでるオブジェクトの位置を保存したい

試したこと

  • オブジェクトの緯度経度高度を取得して保存できないか?Geospatial APIを使って特定の緯度経度高度にオブジェクトを配置することはできるが、すでに配置されているオブジェクトの緯度経度高度を取得する方法がなかなか見当たらない。
  • オブジェクトの(Unityにおける)ワールド座標を取得して保存するのは?ワールド座標系はアプリを起動した時の端末の場所や向きに左右されるので、アプリを再び起動したときに元の位置に再配置することはまず出来ない。

採用したやり方

最終的に上手くいった方法は、Geospatial APIを使って特定の緯度経度高度に何らかのゲームオブジェクト(以下Originとする)を配置してから、位置を保存したいオブジェクト(以下Targetとする)をOriginの子に設定しローカル座標を取得して保存すること。
Originが配置される位置は、アプリを起動した時の端末の場所や向きに左右されず同じ位置なので、Originを親とした時のローカル座標が示す位置も同じ位置になる。

Ekito|駅人Ekito|駅人

Geospatial APIを使って特定の緯度経度高度にOriginを配置

Geospatial API関連の設定を行う

参考: (以下のスクラップの「Unityシーンの作成」まで)
https://zenn.dev/ekito_station/scraps/7e656718c0ed4a

特定の緯度経度高度にアンカーを作成

// aRAnchorManagerはAR Session OriginにアタッチされたAR Anchor Managerスクリプト
var anchor = aRAnchorManager.AddAnchor(
    35.6896,    // アンカーの緯度
    139.6935,   // アンカーの経度
    36.9982,    // アンカーの高度
    Quaternion.identity
);

アンカーの位置にOriginを配置

// prefabはインスタンス化して配置したいゲームオブジェクト
GameObject origin = Instantiate(prefab, anchor.transform);
Ekito|駅人Ekito|駅人

TargetをOriginの子に設定しローカル座標を保存

TargetをOriginの子に設定

// targetは位置を保存したいオブジェクト
target.transform.parent = origin.transform;

また、Targetをプレハブ化しておく。

Targetのローカル座標を取得・保存

今回はPlayerPrefsを使って値を端末内に保存する。

// targetのローカル座標を取得
Vector3 localPos = target.transform.localPosition;
// targetのローカル座標を端末内に保存
PlayerPrefs.SetFloat("LocalX", localPos.x);
PlayerPrefs.SetFloat("LocalY", localPos.y);
PlayerPrefs.SetFloat("LocalZ", localPos.z);
PlayerPrefs.Save();
Ekito|駅人Ekito|駅人

保存されていたローカル座標を読み込み、Targetを再配置

再起動前に作成していたのと同じアンカーを作成し、アンカーの位置にOriginを配置

var anchor = aRAnchorManager.AddAnchor(
    35.6896,    // アンカーの緯度
    139.6935,   // アンカーの経度
    36.9982,    // アンカーの高度
    Quaternion.identity
);
GameObject origin = Instantiate(prefab, anchor.transform);

保存されていたローカル座標を読み込む

float localX = PlayerPrefs.GetFloat("LocalX", 0.0f);
float localY = PlayerPrefs.GetFloat("LocalY", 0.0f);
float localZ = PlayerPrefs.GetFloat("LocalZ", 0.0f);
Vector3 newLocalPos = new Vector3(localX, localY, localZ);

Originの子としてTargetをインスタンス化し、保存されていたローカル座標に配置する

// ローカル座標が保存されていない場合newLocalPosの各座標は0になり、newLocalPosはVector3.zeroと一致する
// 保存されていたローカル座標がたまたまVector3.zeroと一致することは基本ないとみなしてる
// ローカル座標が保存されている場合にのみオブジェクトを配置する
if (newLocalPos != Vector3.zero)
{
    // targetPrefabはtargetをプレハブ化したオブジェクト
    newTarget = Instantiate(targetPrefab, Vector3.zero, Quaternion.identity, origin.transform);
    // 保存されていたローカル座標に配置
    newTarget.transform.localPosition = newLocalPos;
}

これでTargetを再起動前と同じ位置に再配置できた。

このスクラップは2022/12/19にクローズされました