UnityでEditor実行時も30FPS

基本的にフレームレートを指定したいときはApplication.targetFrameRateに設定すれば良いです。

Application.targetFrameRate = 30;

とすると30FPSになります。

しかし、これだけだとEditorで実行していると、なんかヌルヌル動くなぁと思っていました。

調べてみるとどうもスクリプトで指定するだけじゃあかんらしく、QualityでV Sync Countを"Don't Sync"にせんとあかんそうです。

Edit -> Project Settings -> Quality

を選択し、

f:id:wkpn:20180601164905p:plain

ここで設定できます。

こうしないとVSyncのタイミングに合わせて更新しちゃうってことなんですね。

だいたいのPCのVSyncは60FPSで行われるので60FPSになってしまうと。

知らんかった...

とあることをしたらアプリサイズが半分以下になった件

f:id:wkpn:20180708131035p:plain

最近「ネコの絵描きさん」というアプリをアプデしたんですが、テスト中にそのアプリサイズに驚きました!

なんと400M超え!

これはあかん!と思い対策を考え始めました。

そもそも僕がゆとりプログラマーだったのでこうなったので、以下は、ゆとりじゃないプログラマーの方にとっては当たり前のことしか書いていません...

対策

一番、容量が大きいのは何か分かっていました。
めっちゃ数の増えたお弟子さんたちの画像です。
アニメーションがあるので約400枚もあります。
f:id:wkpn:20180708125408p:plain
1枚あたり約0.5Mなので、これだけで約200M...

なんでこんなに重いんや?って、よく見るとデータフォーマットがRGBA 32bitになっています。

圧縮されとらんやないか!

どうやら、iOS版ではPVRTCという圧縮フォーマットが採用されているらしく、PVRTCが正方形の2の累乗サイズの画像しか扱えないから、圧縮されていなかったようです。

ちなみにUnityで利用するぶんには、正方形でさえあれば2の累乗サイズでなかったとしても内部的に2の累乗に変換してくれて圧縮してくれるっぽいです。

またASTCフォーマットという圧縮率が高い別のフォーマットもあるようですが、対応CPUがA8以降(iPhone6以降)らしいので、あと数年はPVRTC使おうかなって感じです。

正方形にするのか...

というわけで直接的に考えると画像1枚1枚を正方形にすれば良いのですが、それは面倒ですし、無駄も多いです。

そこで、アトラス化っていうのを試してみることにしました。

Unityをそこそこ使っている人ならみんな使っているアトラス化(たぶん...)

使っていないのは私ぐらいなものでしょう。

アトラス化っていうのは、複数のテクスチャを1枚のテクスチャにまとめることで、テクスチャの切り替えを少なくして描画負荷を下げましょうっていうものです。

このアトラス化の副次的な効果として、1枚にまとめられたアトラステクスチャは正方形の2の累乗サイズになるというのがあります。

これならPVRTCで圧縮できます。

アトラス化については、以下の記事が詳しかったです↓
kan-kikuchi.hatenablog.com

アトラス化... しかしバグる

アトラス化してみたところ...
f:id:wkpn:20180708131202p:plain

なんか、めっちゃバグってるー。

なんか画が反転したり、他の絵が混じってきたりしてます。
自分のゲームはほぼ全てuGUIで作っているので、そのせいかも...

諦めかけたのですが、Allow Rotation と Tight Packing のチェックを外すことで解決することができました!

こんなだったのが
f:id:wkpn:20180708125425p:plain
こんなかんじになる
f:id:wkpn:20180708125458p:plain

アトラステクスチャのサイズ

基本的には2048ぐらいにしておくのが良いと思います。

大きいテクスチャにまとめるメリットとしては、

  • テクスチャの切り替えコストが発生しにくくなる。

というのがあるのですが、デメリットとして、

  • ハードウェアによってはサポートされていない。
  • 1枚のテクスチャの未使用部分が多くなり無駄にメモリを圧迫する可能性がある。

というものがあります。

アトラスを細かく分けている場合、たまに1024とかにしたほうがトータルテクスチャサイズが小さくなることがあるので、そのときは1024にしても良いかも。

まとめ

というわけで、アトラス化することで400M超えから -> 150Mと半分以下にすることができました。

ちなみにAndroid版は、正方テクスチャでなくても圧縮が掛かるのでこのような問題はなかったです。

PVRTCに圧縮するとどうしても汚くなるので、そのあたりは自己責任でお願いします!


ネコの絵描きさん

ネコの絵描きさん

  • Ken Watanabe
  • ゲーム
  • 無料


play.google.com

物理シミュレーションを行わずにOnCollisionEnterを使う方法

別に物理シミュレーションはしたくないのだけど、OnCollisionEnterで処理をしたいなぁと思って、とりあえずColliderだけをつけてたんですが、全然OnCollisionEnterが呼ばれない...

「なんでや????」って思ってたんですが、どうやらRigidBodyがついていないとOnCollisionEnterは呼ばれない模様。

answers.unity3d.com

こちらの説明によると、RigidBodyをくっつけた上でIs KinematicのチェックボックスをONにすればOKとのこと。

やってみたら、うまくいきました。

RigidBody2Dの場合はIs Kinematicのチェックボックスがないので代わりにBody TypeでKinematicを選択すれば良さそうです。

SingletonMonobehaviour再び

以前作ったSingletonMonobehaviourはシーン遷移時に破棄されるものでした。

以前のもの↓
waken.hatenablog.com


シーンをまたいだ時に破棄されないようなものも欲しくなったので、Awake内でDontDestoryにしています。その他もちょこちょこ変えています。

Awakeを上書きされるとPersistentじゃなくなっちゃうんですが、doAwakeという関数をabstractにしており、絶対実装しないと駄目なようにしているので自制しやすいんじゃないかと思います。

public abstract class PersistentSingletonMonoBehaviour<T> : MonoBehaviour where T : PersistentSingletonMonoBehaviour<T>
{
	private static T instance;
	public static T Instance {
		get {
            if ( instance == null ) {
                instance = ( T )FindObjectOfType( typeof( T ) );
                if ( instance == null ) {
                    Debug.LogError ( typeof( T ) + " does not exist" );
                }
            }
			return instance;
		}
	}

    //@memo
    //継承先でAwakeを定義しちゃ駄目
    //代わりにdoAwakeを作る。
	private void Awake()
	{
		if( this != Instance )
		{
			Destroy( this.gameObject );
			Debug.Log( typeof( T ) + " has already attached to \"" + Instance.gameObject.name + "\"" );
			return;
		}

		DontDestroyOnLoad( this.gameObject );

		doAwake();
	}

	protected abstract void doAwake();
}

Hierarchy上のゲームオブジェクトを全て取得したい!

buravo46.hatenablog.com

こちらのブログにて、Object.FindObjectsOfTypeを使う方法とResources.FindObjectsOfTypeAllを使う方法が紹介されていました。

私の場合、ActiveでないGameObjectも取得したかったのでResources.FindObjectsOfTypeAllを使っていたのですが、これですとシーン上に存在しないオブジェクトも拾ってしまいます。

ブログの方ではstring path = AssetDatabase.GetAssetOrScenePath(obj);を使って判断をしていますが、AssetDatabaseはUnityエディタ上でしか使えません。

悩んでいたところ、gameObject.scene.isLoadというプロパティを教えていただきました!

これがtrueならシーン内にある、つまりHierarchy上にあるということになります!

例えば、こんな感じに使えます。

foreach( GameObject obj in UnityEngine.Resources.FindObjectsOfTypeAll<GameObject>() )
{
	if( obj.scene.isLoad )
	{
		Debug.Log( "私はヒエラルキーにいる" + obj.name + "と申します。" );
	}
}

SceneにはIsValid()というメソッドもあるので、未ロードのシーン内も取得したいなら、そちらを使うのも良さそうです。

iOSでゲーム画面のスクショを撮って保存しようとすると落ちる現象

Unity製ゲームでSNSシェア機能を入れようと思ったらSocial Connectorですね。

ゲーム画面をcaptureした後、Social Connectorを使ってTwitterなんかに投稿できるようにしようと思っていたのですが、いつの頃からかその機能を使おうとするとアプリが落ちる現象が発生していました。

どうやら、これ、iOS11からNSPhotoLibraryAddUsageDescriptionがないと駄目らしいです。

というわけでビルドのポストプロセスでNSPhotoLibraryAddUsageDescriptionを追加するようにしました。

以下のような感じのスクリプトを書いてEditorフォルダに入れておいています。

using System.Xml;
using System.IO;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEditor.iOS.Xcode;

public static class XcodePostProcessBuild
{
    [PostProcessBuild]
    public static void OnPostProcessBuild(BuildTarget target, string path)
    {
        if (target != BuildTarget.iOS)
        {
            return;
        }

        // Add Privacy Description For Adfurikun
        var plistPath = Path.Combine(path, "Info.plist");
        var plist = new PlistDocument();
        plist.ReadFromFile(plistPath);
        plist.root.SetString("NSPhotoLibraryAddUsageDescription", "Allows user to capture a photo or video to upload to their family circle and so on.");
        plist.WriteToFile(plistPath);
    }
}

Unityでsqlite3を使っていたところ、Android版で落ちる減少が発生

Unityでsqlite3を使っていたんですが、Android版でのみ、

E/Unity (22524): DllNotFoundException: Unable to load DLL 'sqlite3': The specified module could not be found.

というエラーをはいてアプリが落ちる現象が発生しておりました。

調べてみたところ、以下のような記事を見つけました。

answers.unity.com

ここに書いてある、それぞれのsqlite3.soを
Plugins/Android/libs/armeabi-v7a

Plugins/Android/libs/x86

にそれぞれ入れるんだよとのこと。

P.S
って思ってたんですが、以下のsqlite3.soをPlugins/Android以下に置くだけで良さそうです。今の所、不具合報告は来ていないです。

github.com

参考:
UnityでSQLiteを扱う方法