エイリアンプログラム

主にゲームプログラミングに関するブログです。

愛用のノートPCの設定を少し変えてみた

どうも今回もプログラミングに関する記事ではないです。
今回は愛用のノートパソコン(Alien ware 13インチ)の設定変更について記載します。
変更内容は、自動でスリープ状態に入る時間の変更についてです。
作業中に少し休憩して戻ってきて、毎回電源スイッチを入れるのはとてもめんどくさいと思い、変更することにしました(まあ、セキュリティ的には安全なんだろうけど・・・流石に2分間でスリープは早すぎ)。
普通なら コントロール パネル ≻ ハードウェアとサウンド ≻ 電源オプション ≻ プラン設定を変更すれば終わりなんですが、どうやらそれだけではうまくいかないことがあるみたいです(私の環境はそうでした)。

以下のブログのやり方にその回避策が記載されていました。

mseeeen.msen.jp

実際に私のところでも設定内容を変更してみましたが結果・・・失敗
色々調べた結果、以下の設定を変更する必要があるみたいです。

corgi758.blog.so-net.ne.jp

試しに381b4222-f694-41f0-9685-ff5bb260df2eのAcSettingIndexとDcSettingIndexを1時間(3600秒)に変更したら、今度はうまくいきました。

まとめ

結論から言うとWin10には、自動的にスリープ状態にしてしまうシステム無人スリープ タイムアウト (System unattended sleep timeout)という機能があるみたいです。
なのでコントロールパネル以外にも、レジストリでの設定を変更する必要があるみたいです。

PS4のHDDをSSDに交換しました

どうもお久しぶりです。
半年近くブログを放置していました。
というのも仕事が忙しく、どうしても自分の時間を作ることができませんでした。

さて、今日は久しぶりということもあり、技術ではなく完全な私の趣味について書きたいと思います(オイッ!)。
ここ最近、私が愛用しているPS4のHDD(500GB)の容量が一杯になったので、思い切って2TBのSSDに交換することにしました。
なのでセーブデータなどをSSDに移す作業を開始しました。

実は以前PS3のHDDを交換した時に購入したデータスタンドがあったので、 それを使ってHDDからSSDにデータをクローンすることにしました。
この時までは簡単にできるだろうな~と軽い気持ちでいましたが、すぐにその気持ちが焦りに変わりました。
クローンを開始するスイッチを押してもクローンが開始されない!?

試しにデータスタンドをデスクトップに接続してみると、データスタンドが認識したりしなかったりする状態でした。
つまり、データスタンドの故障してしまいました。 あきらめてデータスタンドを使わない方法でクローンすることにしました。
まず、デスクトップのケースを外し、PS4のHDDとSSDをデスクトップに繋いで、EaseUS Todo Backupをインストール。そしてクローン開始!
数時間(2時間ぐらい?)ぐらいでクローンが終了してたので、SSDPS4を挿してみました。
結果、大失敗!
PS4を立ち上げると「PS4を始められません。本体ストレージにアクセスできません。電源ボタンを1秒以上押して、PS4の電源を切ってください。」という謎のメッセージが表示されました。
もうクローンをすることは諦めて、素直にAmazonから1TBの外付けハードディスクを購入しました。
PS4の全データをバックアップして、そいつをさらにSSDにデータを展開しました。
うん、今度はうまくいきました(最初からこっちにすればよかった…)。

後から分かったことですが、どうやらEaseUS Todo Backupは有料版じゃないとうまくいかないみたいです。
あと、私が購入したデータスタンドはAmazonや価格コムで調べてみるとどうやら色々不具合がある商品でした。

感想から申しますと、PS3のHDDの交換した時と比べたら色々と、PS4の交換作業がめんどくさかったです。
あと、データのクローンのために態々専用のデータスタンドを購入することは決してお勧めしません。
なぜならクローンなんてそんな頻繁にやるような作業じゃないし、無くてもフリーでクローンするツールなんて沢山あります。
正直お金の無駄遣いです。

参考サイト

junkenemy.com

Unityのマメ知識

本日は技術についてというより、知っておくと少しお得な情報を記載致します。
みなさんは、久しぶりにUnityのプロジェクトファイルを開く時に、いつもUnity立ち上げてから確認していませんか?
実は、Unityを立ち上げなくても以下のパスのテキストファイルを確認することで、バージョンを確認することができるのです!

プロジェクトファイル名\ProjectSettings\ProjectVersion.txt

まあ、知っている方がいましたら、この記事はスルーで構いません…^^;

動的に生成したTextureとSpriteはしっかり管理しよう!

お久しぶりです。
随分長いことブログを放置していました。
さて今回はアプリ開発やゲーム開発で頭を悩ませるメモリリークについてです。
メモリリークとは、プログラムのゴミがどんどん蓄積されてメモリが「もう満帆無理っす!」ということでアプリが落ちてしまうバグです。
詳細な説明はWiki先生に記載されています。

メモリリーク - Wikipedia

私が経験したメモリリークの中で最近苦労したことは、Texture2DとSpriteを動的に生成する処理は、コンポーネントしているGameObjectがDestroy(破棄)されてもTexture2DとSpriteが残ってしまうということです。

public class CreateTexture : MonoBehaviour {

    public int m_createCount = 100;
    private List<Texture2D> texture2dList = new List<Texture2D>();
    private List<Sprite> spriteList = new List<Sprite>();


    // Use this for initialization
    void Start () {
        for(int i =0; i < m_createCount; i++)
        {
            Texture2D texture2D = new Texture2D(1000, 1000);
            Sprite sprite = Sprite.Create(texture2D, new Rect(0.0f, 0.0f, 1000, 1000), Vector2.zero);
            texture2dList.Add(texture2D);
            spriteList.Add(sprite);
        }
        
        DestroyTexture2D();
    }
    

    private void DestroyTexture2D()
    {
        Destroy(this.gameObject);
    }
}


上記のソースコードを実行させてProfilerのMemoryのName>Scene Memorで確認すると…

f:id:alien_program:20171112122905p:plain
GameObjectだけ破棄した結果

何と生成したTexture2DとSpriteが残っているではないか!?
では処理を少し修正した状態でProfilerを確認してみましょう。


public class CreateTexture : MonoBehaviour {

    public int m_createCount = 100;
    private List<Texture2D> texture2dList = new List<Texture2D>();
    private List<Sprite> spriteList = new List<Sprite>();


    // Use this for initialization
    void Start () {
        for(int i =0; i < m_createCount; i++)
        {
            Texture2D texture2D = new Texture2D(1000, 1000);
            Sprite sprite = Sprite.Create(texture2D, new Rect(0.0f, 0.0f, 1000, 1000), Vector2.zero);
            texture2dList.Add(texture2D);
            spriteList.Add(sprite);
        }

        DestroyTexture2D();
    }
    

    private void DestroyTexture2D()
    {
        foreach(var tex in texture2dList)
        {
            Destroy(tex);
        }

        foreach(var spr in spriteList)
        {
            Destroy(spr);
        }
        Destroy(this.gameObject);
    }
}

f:id:alien_program:20171112123802p:plain
Texture2DとSpriteの破棄させた結果

今度はTexture2DとSpriteが消えている事を確認することができましたね。
どうやらUnityの仕様上、GameObjectが破棄されても生成されたTexture2DとSpriteが残ってしまうようです。
これは知らないと修正作業が少しめんどくさくなりますね。
後、Resources.UnloadUnusedAssetsで使わなくなったAsset(今回で言うところのTexture2DとSprite)を解放することができます。
ただしResources.UnloadUnusedAssetsは多様すると良くないらしいです…。


public class CreateTexture : MonoBehaviour {

    public int m_createCount = 100;
    private List<Texture2D> texture2dList = new List<Texture2D>();
    private List<Sprite> spriteList = new List<Sprite>();


    // Use this for initialization
    void Start () {
        for(int i =0; i < m_createCount; i++)
        {
            Texture2D texture2D = new Texture2D(1000, 1000);
            Sprite sprite = Sprite.Create(texture2D, new Rect(0.0f, 0.0f, 1000, 1000), Vector2.zero);
            texture2dList.Add(texture2D);
            spriteList.Add(sprite);
        }

        DestroyTexture2D();
    }
    

    private void DestroyTexture2D()
    {
        Destroy(this.gameObject);
    }


    private void OnDestroy()
    {
        Resources.UnloadUnusedAssets();
    }
}


なので皆さん、くれぐれもAssetの管理には気をつけましょう!


※参考サイトは以下になります。

tsubakit1.hateblo.jp

アセットバンドルのロードについて

どうも2週間ぶりです。
今回はアセットバンドルについてブログを記載したいと思います。

さてアセットバンドルとは何でしょうか?
簡単に申しますとUnity版Zipファイルみたいなものです。
主に使う場面としては、スマフォゲームの開発ではビルドした時にアプリの容量を100MB以下に納めないといけません。 ただし、実際のゲームでは、モデル、テクスチャ、サウンド等のデータを大量に使っています。
そこで出てくるのがアセットバンドルです。
ゲーム中に使用する素材(モデル、テクスチャ、サウンド等)をアセットバンドルという形式でサーバーに置いときます。 そして必要なタイミングで、そのアセットバンドルをダウンロードして各ファイル形式にロードして行きます。
分かりやすいように以下の図を作成しました。少しでも参考になればと思います。
f:id:alien_program:20170903184136p:plain

アセットバンドルの作り方は以下の公式サイトに詳しく記載されています。
Unity - マニュアル: アセットバンドルのビルド

アセットバンドルをロードする方法は以下の通りとなります。

    public Image m_image;

    private IEnumerator Start()
    {
        string path = Application.dataPath + "/AssetBundles/other/alien.asset";
        Sprite sprite = new Sprite();
        yield return Load<Sprite>(path, "alien.png", (a) => { sprite = a; });
        m_image.sprite = sprite;
    }

    private IEnumerator Load<T>(string path, string fileName, UnityEngine.Events.UnityAction<T> callback) where T : Object
    {
#if UNITY_EDITOR
        AssetBundle bundle = AssetBundle.LoadFromFile(path);

        AssetBundleRequest request = bundle.LoadAssetAsync<T>(fileName);

        yield return request;

        T value = request.asset as T;
#else
        while (!Caching.ready)
        {
            yield return null;
        }


        WWW www = WWW.LoadFromCacheOrDownload(path, 5);

        AssetBundle bundle = www.assetBundle;

        AssetBundleRequest request = bundle.LoadAssetAsync<T>(fileName);

        yield return request;

        T value = request.asset as T;

        bundle.Unload(false);

        www.Dispose();
#endif

        callback(value);

        yield return null;
    }

上記に記載致しましたLoadメソッドで、アセットバンドルのロードを行っています。
色々調べて行くと、アセットバンドルをロードする場所がローカルなのかサーバーなのかによって処理の手順が変わります。 なので今回は、#if文を使って処理を分けてあります(ただし今回は実機での検証を行っていません)。
また必ずアセットバンドルのロードが終わった後、指定したファイル形式で取得するようにしてあります。
なので、ジェネリックで取得したいクラス(ファイル形式)を設定し、第三引数にコールバック(コルーチンだとrefが使えないため)を設定しました。

参考サイト Unity - マニュアル: アセットバンドル

アニメーション全般のAssetについて

どうも皆様、一週間ぶりです。
今回はアニメーション全般を行う上で便利なアセットをご紹介致します。

iTween

https://www.assetstore.unity3d.com/jp/#!/content/84

結構有名なAssetだと思います。基本的な使い方としてハッシュに移動先の座標やアニメーションに緩急を付けるやり方になります。例として以下の処理を記載しました。
処理の内容は、指定した時間が経過したら指定した座標までGameObjectが緩急を付けて移動、到着後は自身のFinishEventメソッドを実行するという内容です。

public class MoveCharacter : MonoBehaviour {
        [SerializeField]
    private Vector3 toPosition;
    [SerializeField]
    private float time;

    private void Start()
    {
        iTween.MoveTo(this.gameObject, iTween.Hash(
            "position", toPosition,
            "time", time,
            "easeType", iTween.EaseType.easeInCubic,
            "oncomplete", "FinishEvent",
            "oncompletetarget", this.gameObject));
    }

    private void FinishEvent()
    {
        Debug.Log("finish event!!");
    }
}

ご覧の通り、たった数行のスクリプトで移動処理が簡単に行うことが出来ます。
ただしこちらは、AnimationCurveで作成した独自の緩急がそのまま使用できなかったり、複雑なことをやろうとするとハッシュが長くなる等(好みの問題ではありますが…)が少し懸念されます。

DoTween

https://www.assetstore.unity3d.com/jp/#!/content/27676

こちらもiTweenに劣らぬ高性能なアニメーション全般のAssetです。
上記で記載致しましたiTweenの処理と同じ内容を以下に記載致しましょう。

using DG.Tweening;

public class MoveCharacterDo : MonoBehaviour {
    [SerializeField]
    private LogBehaviour log;
    [SerializeField]
    private Vector3 toPosition;
    [SerializeField]
    private float time = 3.0f;
    [SerializeField]
    private AnimationCurve curve;

    private void Start()
    {
        DOTween.Init();
        DOTween.logBehaviour = LogBehaviour.Default;
        
        this.transform.DOMove(toPosition, time).SetEase(curve).OnComplete(()=> { Debug.Log("FinishEvent"); });
    }
}

どうでしょう?こちらも短い行で移動処理が出来てしまいました。
更にSetEaseを使用することでAnimationCurveが使えるため、独自の緩急でアニメーションを行うことが出来ます。
書き方がreactive extensionっぽいので、UniRXなどを使ったことがある人にとってはそれほど違和感はないかと思います。

以上となります。正直、どのAssetがお勧めかと言われても「好み、使用する場面」によって変わります。
ただ一つ言えることは便利だからと言って、安易にアセットを使いまくることは危険だと言うことです。
なぜなら、Assetを使用と言うことはAssetの使用方法(ルール)に乗っ取りソースコードを構築することになります。もしそのAssetに問題(廃止、Assetのバグ)で使用することが困難になった場合、今まで作ってきたソースコードが全て水の泡にになるかもしれません。
なのでAssetを使用する場合は、周りの評判やどう言った構造で作られているかを時間かけても調べるべきだと考えられます。

Updateを使わずにコルーチンとAnimationCurveでGameObjectの移動処理を行う

Update関数でGameObjectの移動処理を行うことがありますが、正直私はメンバー変数やブロックが増える理由からあまりおススメしていません。
そこで今回は、一例としてコルーチンとAnimationCurveを使うことでUpdate関数を使わずにGameObjectの移動処理を行う方法をお伝えします。

    public AnimationCurve m_curve; //移動量
    public Vector3 m_targetPosition;    //移動先
    public float m_moveTime;   //到着時間
    public UnityEvent m_beginEvent; //移動前のイベント
    public UnityEvent m_exitEvent;  //移動後のイベント


    public IEnumerator MoveIE()
    {
        float currentTime = 0;

        //ベクトルの減算により移動先の向きと方向を求める
        Vector3 startPosition = this.transform.position;
        Vector3 position = m_targetPosition - startPosition;

        //移動開始前にm_beginEventを実行
        m_beginEvent.Invoke();

        while(currentTime < m_moveTime)
        {
            currentTime += Time.deltaTime;

            //現在の時間を到着時間を割って、移動値(0.0~1.0)を取得
            float scale = m_curve.Evaluate(currentTime / m_moveTime);

            //始点の座標と移動量をかけた向きと距離のベクトルを加算して代入
            this.transform.position = startPosition + (position * scale);

            yield return null;
        }

        //到着後にm_exitEventを実行
        m_exitEvent.Invoke();
    }


このソースコードについて説明しますと、到着時間までwhileを回しAnimationCurveの値を取得し続けています。 while文の中にyield return nullを入れることで1フレーム間隔で処理を繰り返してくれます。 取得したAnimationCurveの値を以下のように座標に代入することによって滑らかにGameObjectを移動させたり直線的に移動させることができます。

this.transform.position = startPosition + (position * scale);

更にwhile開始前と終了後にイベント関数(m_beginEvent、m_exitEvent)を入れることによって、正確にイベントを実行させることもできます。
なのでわざわざUpdate関数を使わなくても、ソースコードをごちゃごちゃさせずに移動処理を実行することができます。