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でディスク使用量を確認する方法

推薦する

VUE 3 テレポート コンポーネントと使用構文をすぐに使い始める

目次1. テレポートの紹介1.1. 複数のテレポートを使用する2. テレポートを使用する理由3. テ...

スライドボタン効果を実現するネイティブJS

Jsで作ったスライドボタンの具体的なコードは参考までに。具体的な内容は以下のとおりですまずエフェク...

HTML の基本 - ハイパーリンク スタイルを設定する簡単な例

*** ハイパーリンクのスタイル設定の例a:link クリックされる前のハイパーリンクの状態a:vi...

webpack イメージを base64 に変換する例

url-loader をダウンロード 糸を追加 -D URLローダー モジュール: { ルール: {...

Mysql で group_concat の長さ制限を変更する方法

MySQL には、「group_concat」という関数があります。通常の使用では問題がないかもしれ...

Mysqlマスタースレーブ同期の実装原理

1. MySQL マスター/スレーブ同期とは何ですか?マスター データベースのデータが変更されると、...

dockerでPostgreSQLを実行する方法

1. Dockerをインストールします。参考URL: Docker 入門インストールチュートリアル ...

MySQL での置換例の詳細な説明

MySQL での置換例の詳細な説明replace into は insert と似ていますが、rep...

Vue+SSMは画像アップロードのプレビュー効果を実現します

現在の要件は、ファイルのアップロード ボタンがあることです。ボタンをクリックすると、アップロードする...

Docker Consul コンテナ サービスの更新と見つかった問題の概要

目次1. コンテナサービスの更新とDockerコンサルの検出1. サービス登録と検出とは何ですか? ...

Vue ベースの円形スクロールリスト機能を実装する

注: 親コンテナーに高さと :data='Array' および overfolw:h...

Linux で Golang をインストールする方法

Go は、シンプルで信頼性が高く、効率的なソフトウェアを簡単に構築できるオープンソース プログラミン...

Dockerでリモートアクセスを有効にする方法

DockerデーモンソケットDocker デーモンは、 unix 、 tcp 、 fdの 3 種類の...

Vueのprops設定の詳細な説明

<テンプレート> <div class="demo">...

シンプルなプログレスバーを作成するための HTML+CSS

1. HTMLコードコードをコピーコードは次のとおりです。経験値: <span class=...