はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【Unity】従来のPhysXに比べてUnity Physics最強だと思いたかったけど失敗した件【DOTS】

はじめに

ネットサーフィンをしていたところ、以下の動画を見つけました。

www.youtube.com

DOTSを利用しているUnity Physicsですが、これほどまで違うのかと感じます。

ただ私はまだ信用していません。なぜならまだ自分で試していないからです。

見せてもらいましょう、Unity Physicsの力とやらを。

f:id:hanaaaaaachiru:20211215005348g:plain
実験の様子

環境

MacBook Pro(16-inch, 2019)
 2.6 GHz 6コア Intel Core i7
 16 GB 2667 MHz DDR4
 Intel UHD Graphics 630 1536 MB
Unity 2020.3.1.18f1
Unity Physics Version 0.6.0-preview.3
Hybrid Renderer Version 0.11.0-preview.44
Universal RP Version 10.6.0

プロジェクトのビルド:Mono*1
ログ出力:なし

補足

Hybrid RendererのV2を利用するには色々と操作しなければいけません。(結構めんどくさい&忘れがち...)

Package ManagerからインストールしただけではV1が適応されている上、URPのバージョンが8.0.0固定だったりとかなり厄介です。

以下の公式に手順が載ってるので参照してみてください。
docs.unity3d.com

実験

Cubeをどんどん生成していき、30fps以上を維持できなくなるまでの最大のオブジェクト数を調べます。

またエディタ上だとUnity Physicsは遅いとの噂なので、Mac向けにビルドして計測しました。
【Unity】新しい物理演算、Unity Physicsについて - テラシュールブログ


加えて毎フレームInstantiateをしてしまっているのでその処理時間も含まれてしまったりと色々と適当測定ですが、許してください。

床の準備

f:id:hanaaaaaachiru:20211214224238p:plainf:id:hanaaaaaachiru:20211214224234p:plain
←PhysX,Unity Physics→

動く物体の準備

f:id:hanaaaaaachiru:20211214222915p:plainf:id:hanaaaaaachiru:20211214222918p:plain
←PhysX,Unity Physics→

インスタンス生成のスクリプト

まずはDOTSを使わないいつものパターン。

public class GameManager : MonoBehaviour
{
    [SerializeField] private GameObject prefab;
    [SerializeField] private Text textObj;
    
    private float _time;
    private long _count;
    private long _maxCount;

    private FPSCounter _fpsCounter;

    private void Start()
    {
        Application.targetFrameRate = 60;
        _fpsCounter = new FPSCounter(4);
    }

    private void Update()
    {
        _time += Time.unscaledDeltaTime;
        _fpsCounter.Update();

        if (_fpsCounter.FPS >= 30 && _count != _maxCount) _maxCount = _count;
        if (_time < 0.005f) return;
        
        Instantiate(prefab);
        _time = 0;
        _count++;
        textObj.text = $"FPS : {_fpsCounter.FPS}\nCount : {_count}\nMaxCount : {_maxCount}";
    }
}

次にDOTSパターン。

public class GameManagerDOTS : MonoBehaviour
{
    [SerializeField] private GameObject prefab;
    [SerializeField] private Text textObj;

    private Entity _entityPrefab;
    private EntityManager _entityManager;
    private BlobAssetStore _blobAssetStore;
    
    private float _time;
    private long _count;
    private long _maxCount;

    private FPSCounter _fpsCounter;
    
    private void Start()
    {
        Application.targetFrameRate = 60;
        _fpsCounter = new FPSCounter(4);
        
        // プレハブをEntityに変換
        _blobAssetStore = new BlobAssetStore();
        _entityManager = World.DefaultGameObjectInjectionWorld.EntityManager;
        var setting = GameObjectConversionSettings.FromWorld(World.DefaultGameObjectInjectionWorld, _blobAssetStore);
        _entityPrefab = GameObjectConversionUtility.ConvertGameObjectHierarchy(prefab, setting);
    }

    private void Update()
    {
        _time += Time.unscaledDeltaTime;
        _fpsCounter.Update();

        if (_fpsCounter.FPS >= 30 && _count != _maxCount) _maxCount = _count;
        if (_time < 0.005f) return;
        
        _entityManager.Instantiate(_entityPrefab);

        _time = 0;
        _count++;
        textObj.text = $"FPS : {_fpsCounter.FPS}\nCount : {_count}\nMaxCount : {_maxCount}";
    }

    private void OnDestroy()
    {
        _blobAssetStore.Dispose();
    }
}


FPSの測定は以下のコードを利用させていただきました。
【Unity】FPS を計測するスクリプト - コガネブログ

補足

以前までPrefabEntityに変換する際に以下のコードで動作していたのですが、エラーが出てくるようになっていまいました。

var setting = GameObjectConversionSettings.FromWorld(World.DefaultGameObjectInjectionWorld, null);
ArgumentNullException: A valid BlobAssetStore must be passed to construct a BlobAssetComputationContext
Parameter name: blobAssetStore

BlobAssetStoreなるものを生成して渡してあげればエラーは消えるようです。
Cant Convert GameObject to Entity in 0.3 version - Unity Forum

実験結果

f:id:hanaaaaaachiru:20211215013818p:plainf:id:hanaaaaaachiru:20211215013809p:plain
←PhysX,Unity Physics→
種類 MaxCount
いつもの 7993
Unity Physics 7023


......ん?

考察

まさかのUnity Physicsを利用しない方がFPSを保つことができていました。

ただ原因はこれまで書いていてちょくちょくコメントしていた箇所だと思います。

  • Update内のオブジェクト生成の処理
  • IL2CPPを利用していない

さいごに

さすがにこの結果はまずいので、また条件を変えながら実験してみたいと思います。

というか私自身DOTSの理解が浅く上手に活用できていない可能性大なので、なにかありましたらコメント等で教えていただけると嬉しいです。

ではまた。

*1:IL2CPPで実験したかったのですが、アプリを起動するとすぐにクラッシュしてしまったため。あとでIL2CPPで検証したいです。