Unityメモ – Transformに情報を一括保存と読み込みをする処理を追加してみる

Posted: 2016年3月11日 カテゴリー: プログラミングメモ, Unity, Unity メモ
タグ:, ,

前に書いてから随分と間が空きましたが、結構前に書いたTransformを拡張してみよう系の派生メモです。
今回はセーブデータ用にTransformの位置、回転、スケールを一つの文字列に纏め、その纏めた文字列を元に値を設定する処理を作ってみました。
ちなみに、最適化とか軽量化とかを気にしている場合は、非推奨です。

まず、文字列を作る処理を作ります。ToString()をオーバーライドしていないのは元々の処理を使いたくなった時の為です。

public static class TransformExtension
{
    // 区切りの文字列
    const string _SAVE_SEPARATOR = "_";

    public static string CreateSaveString(this Transform transform, bool isLocal, bool isSavePosition, bool isSaveRotation, bool isSaveScale)
    {
        if (isLocal)
        {
            return CreateLocalSaveString(transform, isSavePosition, isSaveRotation, isSaveScale); 
        }

        return CreateWorldSaveString(transform, isSavePosition, isSaveRotation, isSaveScale);
    }

パラメーターのIsLocalでTransformのローカル座標とワールド座標のどちらを扱うかを決めています。

次にローカル座標の文字データを作る処理を実装します:

    private static string CreateLocalSaveString(Transform transform, bool isSavePosition, bool isSaveRotation, bool isSaveScale)
    {
        string data = "";

        if (isSavePosition)
        {
            Vector3 pos = transform.localPosition;
            data += pos.x.ToString() + _SAVE_SEPARATOR;
            data += pos.y.ToString() + _SAVE_SEPARATOR;
            data += pos.z.ToString() + _SAVE_SEPARATOR;
        }

        if (isSaveRotation)
        {
            Vector3 rot = transform.localEulerAngles;
            data += rot.x.ToString() + _SAVE_SEPARATOR;
            data += rot.y.ToString() + _SAVE_SEPARATOR;
            data += rot.z.ToString() + _SAVE_SEPARATOR;
        }

        if (isSaveScale)
        {
            Vector3 scale = transform.localScale;
            data += scale.x.ToString() + _SAVE_SEPARATOR;
            data += scale.y.ToString() + _SAVE_SEPARATOR;
            data += scale.z.ToString() + _SAVE_SEPARATOR;
        }

        return data;
    }

やっている事は単純に書く値を文字列にし、”_”を追加する事で書く値に区切りを付けています。

ローカルが出来たので次はワールド座標用の処理です:

    private static string CreateWorldSaveString(Transform transform, bool isSavePosition, bool isSaveRotation, bool isSaveScale)
    {
        string data = "";

        if (isSavePosition)
        {
            Vector3 pos = transform.position;
            data += pos.x.ToString() + _SAVE_SEPARATOR;
            data += pos.y.ToString() + _SAVE_SEPARATOR;
            data += pos.z.ToString() + _SAVE_SEPARATOR;
        }

        if (isSaveRotation)
        {
            Vector3 rot = transform.eulerAngles;
            data += rot.x.ToString() + _SAVE_SEPARATOR;
            data += rot.y.ToString() + _SAVE_SEPARATOR;
            data += rot.z.ToString() + _SAVE_SEPARATOR;
        }

        if (isSaveScale)
        {
            Vector3 scale = transform.localScale;
            data += scale.x.ToString() + _SAVE_SEPARATOR;
            data += scale.y.ToString() + _SAVE_SEPARATOR;
            data += scale.z.ToString() + _SAVE_SEPARATOR;
        }

        return data;
    }

内容は参照する値が違うだけでローカルの時と同じです。え、同じなら纏めろって?それは好みでやってね!(丸投げ)

保存用の文字列を作る処理が出来ましたので、次はその文字列を読み込んでTransformの値に設定する処理を作ります。ここでもローカル座標とワールド座標を分けます:

    public static void SetupFromSaveString(this Transform transform, string data, bool isLocal, 
                                                             bool isLoadPosition, bool isLoadRotation, bool isLoadScale
                                                             )
    {
        if (isLocal)
        {
            SetupLocalFromString(transform, data, isLoadPosition, isLoadRotation, isLoadScale);
            return;
        }

        SetupWorldFromString(transform, data, isLoadPosition, isLoadRotation, isLoadScale);
    }

次に、ローカルの各値を設定して行きます。やる事としましてはまず一つになっている文字列を区切りで追加した”_”毎に切り分けます。それが終わったら各値に設定していきます:

    private static void SetupLocalFromString(Transform transform, string data,
                                             bool isLoadPosition, bool isLoadRotation, bool isLoadScale
                                            )
    {
        string[] dataList = data.Split(_SAVE_SEPARATOR[0]);
        int index = 0;

        if (isLoadPosition)
        {
            transform.localPosition = new Vector3(float.Parse(dataList[index++]),
                                                  float.Parse(dataList[index++]),
                                                  float.Parse(dataList[index++])
                                                  );
        }

        if (isLoadRotation)
        {
            transform.localEulerAngles = new Vector3(float.Parse(dataList[index++]),
                                                    float.Parse(dataList[index++]),
                                                    float.Parse(dataList[index++])
                                                    );
        }

        if (isLoadScale)
        {
            transform.localScale = new Vector3(float.Parse(dataList[index++]),
                                               float.Parse(dataList[index++]),
                                               float.Parse(dataList[index++])
                                               );
        }
    }

ローカルが出来たので、今度はワールドです。今回も内容はローカルとほぼ同じです。

    private static void SetupWorldFromString(Transform transform, string data,
                                             bool isLoadPosition, bool isLoadRotation, bool isLoadScale
                                             )
    {
        string[] dataList = data.Split(_SAVE_SEPARATOR[0]);
        int index = 0;

        if (isLoadPosition)
        {
            transform.position = new Vector3(float.Parse(dataList[index++]),
                                             float.Parse(dataList[index++]),
                                             float.Parse(dataList[index++])
                                             );
        }

        if (isLoadRotation)
        {
            transform.eulerAngles = new Vector3(float.Parse(dataList[index++]),
                                                float.Parse(dataList[index++]),
                                                float.Parse(dataList[index++])
                                                );
        }

        if (isLoadScale)
        {
            transform.localScale = new Vector3(float.Parse(dataList[index++]),
                                               float.Parse(dataList[index++]),
                                               float.Parse(dataList[index++])
                                               );
        }
    }

え、同じなら揃えろって?それはおこn(ry

これで実装は終わりましたので、簡単なテストを作ります。

やる事は単純でCreateSaveString()で文字列を作った後にTransformの情報を一旦全部0にリセットし、その後に作った文字列をSetupFromSaveString()に渡します。それで開始時と同じ状態になっていたらOKです。

public class TransformTest
    : MonoBehaviour
{

	void Start()
    {
        string data = transform.CreateSaveString(true, true, true, true);
        Debug.Log("Start Data = " + data);

        transform.ResetLocal();
        transform.SetupFromSaveString(data, true, true, true, true);
        Debug.Log("Start Data = " + transform.CreateSaveString(false, true, true, true) );
	}
}

テストはCubeを1個作ってソレを適当に動かして、回転させて、大きさも変えます。それに今作ったテスト用スクリプトを追加します。

以上になります。これでセーブしたい時はCreateSaveString()で作った文字列をPlayerPrefs.SetString()等で保存すればそこから取り出して利用出来ます。

ちなみに今回はIsSavePosition等のどの値を保存するかが途中で変わった時の対処が入っていないので、そこは気をつけてください。

今回のソースコードです:
https://github.com/ookumaneko/Blog_Unity/tree/master/TransformExtension

 

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中