CocosCreator ScrollView 最適化シリーズ: フレーム読み込み

CocosCreator ScrollView 最適化シリーズ: フレーム読み込み

1. はじめに

JS はシングルスレッドです。つまり、すべてのタスクをキューに入れる必要があり、次のタスクは現在のタスクが完了したときにのみ実行されます。前のタスクに長い時間がかかる場合、次のタスクは待機する必要があります。

Cocos Creator は、基本的に JS である Java Script/Type Script を使用して開発されており、上記の機能も備えています。特に、不適切に使用すると、インターフェースの遅延が発生する可能性が高くなります。

たとえば、 ScrollView のコンテンツに 500 個のノードを作成すると、次のインターフェースがスタックする可能性があります。

PS: 読み込み処理中に読み込みダイアログ ボックスが表示されましたが、スタックしたために表示されなかったようです。

この記事を読むことで、 「フレームロード」技術を使用して上記の問題を解決する方法を学ぶことができます。最終的な効果の比較は次のとおりです。

2. 行き詰まった問題の分析

通常の状況では、ScrollViewの子ノードを一定数作成する場合、コードは次のようになります。

パブリックダイレクトロード(長さ: 数値) {
    (i = 0; i < 長さ; i++) の場合 {
        this._initItem(i);
    }
}
 
プライベート_initItem(itemIndex: 数値) {
    itemNode を cc.instantiate(this.itemPrefab); にします。
    スクロールビューの幅を 10 に設定します。
    アイテムノードの高さ = アイテムノードの幅;
    親要素を this.scrollView.content に置き換えます。
    アイテムノードの位置を設定します。(0, 0);
}

一般的に、長さの値が 10 などの非常に小さい場合、プログラムは実行時に正常に見えるかもしれませんが、より注意深く観察すると、しばらく停止しますが、すぐに終了することがわかります。

特に、長さの値が50以上の特定のレベルに達すると、このコードは上記のスクリーンショットに示すように表示されます

結局のところ、問題は、このノードに対してcc.instantiatesetParent介してノードを作成するために必要な時間が、想像したほど短くなく、もちろん、想像したほど長くもないことです。ただし、一定数のノードが連続して作成されると、問題は拡大し、ノードの作成に時間がかかる可能性があります。

この問題をより視覚的に理解したい場合は、次の図のようになります。

直接ロード

明らかに、上の図によると、フレーム 1 から 4 は完全に占有されており、この期間中の他のすべてのロジックの実行が失敗します ([読み込み] ダイアログ ボックスが表示されない、回転アニメーションが停止するなど)。

それで、どうやって解決するのでしょうか?

3. 解決策(理論)

学生の中には、Promise を使用して非同期的に問題を解決することを考える人もいるかもしれません。ただし、この場合、Promise は、ノードを継続的に作成する赤いコードを少し後に実行するだけです。しかし、赤いコードが実行されると、その間はコードがスタックしたままになるため、Promise はこの状況に対処できません。

それで、どうやって解決すればいいのでしょうか?

その解決策の一つが、今日お話しする「フレームローディング」です。では、 「フレームローディング」とはどのように理解すればよいのでしょうか。

いつものように、これが写真です:

フレーム荷重

上の図で「フレームロード」が分かりやすくなりました。具体的な実行プロセスは以下のとおりです。

  1. まず、時間がかかり、行き詰まっているコードをいくつかの小さなセクションに分割します。
  2. そして、各フレームに、これらの小さなセグメントを実行するための少しの時間を割り当てます
  3. このようにして、各フレームで他のロジックを実行する時間を確保します(そのため、読み込みダイアログボックスが表示され、回転アニメーションが続行されます)。

理論は明確ですが、実際にどのように実行するのでしょうか?

例えば:

  1. コードを多くの小さなセクションに分割するにはどうすればよいでしょうか?
  2. これらの小さなセグメントを実行するために、各フレームに時間を割り当てるにはどうすればよいでしょうか?

現時点では、これを実現するには、ES6 (ES2015) コルーチンであるGeneratorを使用する必要があります。

4. ソリューション(コード)

2 番目のセクション (ScrollView の特定の数の子ノードを作成する) で使用したコードを例にとり、コードを複数の小さなセグメントに実装し、各フレームでこれらの小さなセグメントを実行する時間を割り当てます。

4.1 ジェネレータを使用してコードを複数の小さなセクションに分割する

分割前:

パブリックダイレクトロード(長さ: 数値) {
    (i = 0; i < 長さ; i++) の場合 {
        this._initItem(i);
    }
}
 
プライベート_initItem(itemIndex: 数値) {
    itemNode を cc.instantiate(this.itemPrefab); にします。
    スクロールビューの幅を 10 に設定します。
    アイテムノードの高さ = アイテムノードの幅;
    親要素を this.scrollView.content に置き換えます。
    アイテムノードの位置を設定します。(0, 0);
}

分割後:

/**
 * (新しいコード) 子ノードを生成するジェネレーターを取得します
 */
プライベート *_getItemGenerator(長さ: 数値) {
    (i = 0; i < 長さ; i++) の場合 {
        this._initItem(i) を生成します。
    }
}
 
/**
 * (分割前のコードと同じ)
 */
プライベート_initItem(itemIndex: 数値) {
    itemNode を cc.instantiate(this.itemPrefab); にします。
    スクロールビューの幅を 10 に設定します。
    アイテムノードの高さ = アイテムノードの幅;
    親要素を this.scrollView.content に置き換えます。
    アイテムノードの位置を設定します。(0, 0);
}

ここでの原則は、Generator を使用して 1 つの for ループですべてのノードを作成し、 for ループの各ステップを小さなセグメントに分割することです。

もちろん、この「分割」コードは分割ステップのみを実装しているため実行できません。実行するには、以下の2番目のコードが必要です。

4.2 実行するためにフレームごとに時間を割り当てる

今撮った写真を見てみましょう。

フレーム荷重

この図から、結果として得られるコードは

/**
 * フレーム読み込みを実装する*/
非同期フレーミングロード(長さ: 数値) {
    this.executePreFrame(this._getItemGenerator(length), 1) を待機します。
}
 
/**
 * フレーム内でジェネレータロジックを実行する*
 * @param generator ジェネレーター * @paramduration 期間 (ミリ秒)
 * ジェネレーター操作が実行されるたびに、実行の最大期間。
 * 値が 8ms であると仮定すると、1 フレーム (合計 16ms) で、このロジックの実行に 8ms が割り当てられることを意味します */
プライベートexecutePreFrame(ジェネレータ: ジェネレータ、期間: 数値) {
    新しい Promise を返します ((resolve, reject) => {
        gen = ジェネレータとします。
        // 実行関数を作成する let execute = () => {
 
            // 実行前に開始タイムスタンプを記録します。let startTime = new Date().getTime();
 
            // 次に、ジェネレータから分割されたコードセグメントを取得して実行し続けます for (let iter = gen.next(); ; iter = gen.next()) {
 
                // すべてのジェネレータが実行されたかどうかを確認します // そうであれば、タスクは完了です if (iter == null || iter.done) {
                    解決する();
                    戻る;
                }
 
                // 各小さなコードセグメントが実行された後、これらの小さなコードセグメントに対してこのフレームに割り当てた最大実行時間を超えていないかどうかを確認します。if (new Date().getTime() - startTime >duration) {
                    
                    // 制限を超えると、現在のフレームは実行されません。タイマーを開始して次のフレームを実行します。this.scheduleOnce(() => {
                        実行する();
                    });
                    戻る;
                }
            }
        };
 
        // 実行関数を実行します。execute();
    });
}
 

コードには多くのコメントが付けられていますが、言及する価値のある点がいくつかあります。

  1. これらの小さなタスクが完了したかどうかを知るために、私はPromiseを使います。完了したら、 resolve
  2. 各小さなコードセグメントの実行時間は固定されず、予想時間を超える可能性があります。たとえば、これらの小さなコード セグメントを実行するために、フレームごとに 1 ミリ秒を割り当てることが予想されます。最初の 3 つの小さなコード セグメントの実行時間がそれぞれ 0.2 ミリ秒、0.5 ミリ秒、0.4 ミリ秒であると仮定すると、私が示したコードでは、これらの 3 つの小さなコード セグメントが実行され、その後、現在のフレームが終了してこれらの小さなコード セグメントの実行が続行されます。ここでの時間消費はすでに 1.1 ミリ秒であり、私が設定した 1 ミリ秒より 0.1 ミリ秒長いためです。もちろん、これらの実行が最大 1 ミリ秒に厳密に従うようにコードを自分で変更して、タイムアウトなしの実行を実現することもできます (つまり、3 番目の小さなセグメントを実行しなくなります)。

今のところ、ある程度の「フレームロード」は実現できています~

このプロジェクトのすべての図とコードは Github リポジトリにあります。検証を実行する必要がある場合は、自分でコードを検証しなくても、プロジェクトを直接プルダウンできます。

👉👉https://github.com/zhitaocai/CocosCreator-ScrollVIewPlus👈👈

V. 結論

  1. タイトルは「ScrollView 最適化シリーズ」ですが、私はむしろ「フ​​レーム読み込みを使用して ScrollView を最適化する」という方向に傾いています。この記事では、ノードを作成する例を挙げていますが、あえて「分割フレーム作成」とは言いません。 「分幀加載」はパフォーマンス最適化ソリューションであり、「分割フレーム作成」、「分割フレーム操作」、「分割フレーム計算」、「分割フレームレンダリング」などになると考えているからです。
  2. フレーミングの実装ではthis.scheduleOnce関数を使用しましたが、実際にはupdate(dt:number)で実行してみることもできます。「テスト プロジェクト」を変更して検証してみましょう。
  3. TypeScriptでジェネレータを使用するには、Cocosプロジェクトのtsconfig.jsonも変更する必要がありますcompilerOptions.lib配列にes2015を追加します。

以上がCocosCreator ScrollView最適化シリーズのフレーム読み込みの詳細です。CocosCreator ScrollView最適化フレーム読み込みの詳細については、123WORDPRESS.COMの他の関連記事に注目してください。

以下もご興味があるかもしれません:
  • Cocos クリエイターのタッチ イベント アプリケーション (タッチして複数の子ノードを選択する例)
  • iOS開発でcocos2dを使用してタッチイベントを追加する方法
  • Cocos2d-x タッチイベントの例
  • CocosCreator最適化DrawCallの詳細な説明
  • CocosCreatorがスキル冷却効果を実装
  • cocoscreatorプレハブの詳しい説明
  • CocosCreator でレイヤー管理に常駐ノードを使用する方法
  • ゲーム開発におけるサウンド処理にCocosCreatorを使用する方法
  • CocosCreator システムイベントがどのように生成され、トリガーされるかについての詳細な説明

<<:  SQL文の最適化の一般的な手順の詳細な説明

>>:  Linuxでディスク使用量を確認する方法

推薦する

Vue3 AST パーサー - ソースコード分析

目次1. AST抽象構文木を生成する2. ASTのルートノードを作成する3. 子ノードの解析4. テ...

ウェブページ作成における絶対パスと相対パスの違い

1. 絶対パスまず、ローカル コンピューターでは、ファイルの絶対パスは、当然、ハード ディスク上でフ...

Element UI をインストールして vue3.0 でベクター グラフィックスを使用する方法

ここでは、v3 のインストールと使用にのみ焦点を当てます。v2 について学びたい場合は、公式 Web...

デザイナーはコーディングを学ぶ必要がありますか?

多くの場合、 Web デザインが完成した後でデザイナーの無知が露呈し、批判されることがあります。彼ら...

データベースを削除せずにMySQLのすべてのテーブルを素早く削除する方法

この記事では、例を使用して、データベースを削除せずに MySQL 内のすべてのテーブルをすばやく削除...

MySQLとOracleの違いのまとめ(機能性能の比較、選択、使用時のSQLなど)

1. 同時実行性同時実行性は OLTP データベースの最も重要な機能ですが、同時実行性にはリソース...

JavaScript でオブジェクトのプロパティを削除する方法

1. 削除delete は、オブジェクトのプロパティを残さずに削除する唯一の方法ですが、その「代替」...

Linux Cron によるパラメータ付き PHP コードのスケジュール実行

1. 引き続き PHP スクリプトを使用して実行します。コマンドラインに入力: php /home/...

WeChatミニプログラムでの仮想リストの実装例

目次序文分析する初期レンダリング方法初期最適化さらなる最適化方法2序文ほとんどのミニプログラムには、...

Dockerコンテナのネットワークポート設定プロセスの詳細な説明

ネットワークポートの公開実際、Docker にはネットワーク ポートの公開に関わる 2 つのパラメー...

MySQL 結合クエリ構文と例

接続クエリ:これは、2 つのクエリ (またはテーブル) の各行をペアで接続した結果です。つまり、1 ...

フレームセットの高さを設定する際のインターフェース変形の解決策

現在、プロジェクトを作成しました。インターフェースは次のとおりです。これはフレームセットを使用して行...

CSS 境界線の半分または部分的に表示される実装コード

1. 疑似クラスを使用して境界線の半分を表示する <!DOCTYPE html> <...

ES6分解課題の原理と応用

目次配列分割代入オブジェクトの分解代入分割割り当ての適用変数の値の交換関数から複数の値を返すマップ構...

WeChatアプレットbindtapとcatchtapの違いの詳細な説明

目次1. イベントとは何ですか? 2. イベントの使い方3. バインドタップとキャッチタップの違い4...