Pixi.js: Dosis pixi.js dapat menyediakan pemuat paralel async?

Dibuat pada 1 Feb 2018  ·  28Komentar  ·  Sumber: pixijs/pixi.js

Hai~ semuanya.
Saya pengguna baru di pixijs.

Saya mengetahui bahwa pixi.loaders.Loader tidak dapat memuat sumber daya saat yang lain sedang memuat. dan jika satu sumber daya dimuat, memuat dua kali akan memunculkan pengecualian yang tidak dapat ditangkap.

Jadi, Apakah ini dapat menyediakan pemuat paralel async yang dapat men-cache tugas beban paralel kemudian membuat serial menjalankannya?


ikuti adalah implementasi saya yang sederhana dan jelek pada proyek demo.

###implementasi kelas resourcesLoader###

import * as pixi from "pixi.js";
import * as _ from "lodash";

class ResourcesLoaderParams {
  loaderParams: string | any | any[];
  promises: Promise<PIXI.loaders.Resource>[] = [];
  resolves: ((value?: PIXI.loaders.Resource | PromiseLike<PIXI.loaders.Resource>) => void)[] = [];
  rejects: ((reason?: any) => void)[] = [];
}

export class resourcesLoader {
  constructor(public loader: pixi.loaders.Loader) {
  }

  loaderOptions: pixi.loaders.LoaderOptions;

  waitingList: ResourcesLoaderParams[] = [];
  loadingList: ResourcesLoaderParams[] = [];

  checkExist(urls: string): boolean {
    return !_.isNil(this.loader.resources[urls]);
  }

  get(urls: string) {
    return this.loader.resources[urls];
  }

  checkAndGet(urls: string): Promise<PIXI.loaders.Resource> {
    if (this.checkExist(urls)) {
      return Promise.resolve(this.get(urls));
    }
    return Promise.reject(this.get(urls));
  }

  loadResources(urls: string): Promise<PIXI.loaders.Resource> {
    // trim existed
    if (this.checkExist(urls)) {
      return this.checkAndGet(urls);
    }
    // check is in loading or waiting, then,  merge task
    // otherwise, create new loading task
    let Li = this.waitingList.find(T => T.loaderParams == urls);
    if (_.isNil(Li)) {
      Li = this.loadingList.find(T => T.loaderParams == urls);
    }
    let thisPromise = undefined;
    if (!_.isNil(Li)) {
      thisPromise = new Promise<PIXI.loaders.Resource>((resolve, reject) => {
        Li.resolves.push(resolve);
        Li.rejects.push(reject);
      });
    } else {
      let p = new ResourcesLoaderParams();

      p.loaderParams = urls;
      thisPromise = new Promise<PIXI.loaders.Resource>((resolve, reject) => {
        p.resolves.push(resolve);
        p.rejects.push(reject);
      });
      p.promises.push(thisPromise);

      if (this.waitingList.length == 0 && this.loadingList.length == 0) {
        this.waitingList.push(p);
        this.emitLoader();

      } else {
        this.waitingList.push(p);
      }
    }

    return thisPromise;
  }

  private emitLoader() {
    if (this.waitingList.length === 0) {
      return;
    }

    let list: ResourcesLoaderParams[] = [];

    let tempList = [];
    if (_.isArray(this.waitingList[0].loaderParams)) {
      list = [this.waitingList[0]];
      for (let i = 1; i != this.waitingList.length; ++i) {
        tempList.push(this.waitingList[i]);
      }
    } else {
      // first item confident not array
      let flag = false;
      for (let i = 0; i != this.waitingList.length; ++i) {
        if (!flag) {
          if (_.isArray(this.waitingList[i].loaderParams)) {
            --i;
            flag = true;
            continue;
          }
          list.push(this.waitingList[i]);
        } else {
          tempList.push(this.waitingList[i]);
        }
      }
    }
    this.waitingList = tempList;
    this.loadingList = list;

    // trim the loaded item
    this.loadingList = this.loadingList.filter(T => {
      if (this.checkExist(T.loaderParams)) {
        T.resolves.forEach(Tr => Tr(this.get(T.loaderParams)));
        return false;
      }
      return true;
    });

    if (this.loadingList.length === 0) {
      if (this.waitingList.length !== 0) {
        this.emitLoader();
      }
      return;
    }
    let param: any;
    if (this.loadingList.length === 1) {
      param = this.loadingList[0].loaderParams;
    } else {
      param = this.loadingList.map(T => T.loaderParams);
    }
    let loadingLoader = this.loader.add(param, this.loaderOptions).load(() => {
      this.loadingList.forEach(T => {
        console.log(T.loaderParams);
        T.resolves.forEach(Tr => Tr(this.loader.resources[T.loaderParams]));
      });
      this.loadingList = [];
      this.emitLoader();
    });
    // try catch error,  example double load
    // but seemingly cannot catch it
    loadingLoader.on("error", () => {
      this.loadingList.forEach(T => {
        console.log(T.loaderParams);
        T.rejects.forEach(Tr => Tr(T.loaderParams));
      });
      this.loadingList = [];
      this.emitLoader();
    });
    loadingLoader.onError.once(() => {
      this.loadingList.forEach(T => {
        console.log(T.loaderParams);
        T.rejects.forEach(Tr => Tr(T.loaderParams));
      });
      this.loadingList = [];
      this.emitLoader();
    });

  }

}

berikut pemakaiannya :

paralel membuat permintaan pemuatan dan membuat serial memuatnya kemudian async memanggil panggilan balik janji "lalu" pada sumber daya yang dimuat segera.
dan jika sumber daya dimuat, "resourcesLoader" akan dengan cepat menyelesaikan permintaan itu tanpa menunggu.
dan jika sumber daya sedang memuat atau menunggu, permintaan baru yang sama akan digabungkan ke tugas memuat atau menunggu.

this.loader = new resourcesLoader(pixi.loader);
this.loader.loadResources("/api/static/1.png").then(T => {
      this.image1 = new pixi.Sprite(T.texture);
    // ......
});
this.loader.loadResources("/api/static/2.png").then(T => {
      this.image2 = new pixi.Sprite(T.texture);
    // ......
});
this.loader.loadResources("/api/static/3.png").then(T => {
      this.image3 = new pixi.Sprite(T.texture);
    // ......
});

// ........

periksa dan baca/muat sumber daya dengan cepat

  updateImage(imageUrl: string) {
    this.loader.checkAndGet(imageUrl).catch(E => {
      return this.loader.loadResources(imageUrl);
    }).then(T => {
      this.image = new pixi.Sprite(T.texture);
      // .......
    });
  }

implementasi sederhana di atas hanya dapat memuat url sekarang. tetapi saya pikir itu dapat ditingkatkan agar mirip dengan API pemuat asli.

Jadi, Bisakah pixi.js resmi menyediakan pemuat paralel asinkron seperti ini?

Komentar yang paling membantu

ya
Jika Anda ingin menggunakan versi PIXI dengan middlewarenya sendiri, maka Anda akan menggunakan

const loader = new PIXI.loaders.Loader();

Loader sangat ringan, jadi jangan khawatir memiliki banyak loader.

Satu-satunya hal yang saya ketahui, adalah bahwa pemuat memungkinkan Anda mengatur berapa banyak aset yang dapat diunduh secara bersamaan, yang memiliki default 10. Jika Anda memiliki 3 pemuat, Anda dapat secara efektif mencoba memuat hingga 30 sumber daya di sekali (saya yakin browser akan membatasi lebih rendah dari itu). Jadi mungkin Anda akan membuat setiap loader dengan batas yang lebih rendah? Atau mungkin pemuat utama Anda memiliki batas besar untuk menurunkan aset awal, tetapi pemuat aset 'dalam-streaming-aset' Anda memiliki batas yang lebih rendah untuk fokus menurunkan setiap aset lebih cepat.

Semua 28 komentar

PixiJS menggunakan salah satu proyek hewan peliharaan kontributor inti kami https://github.com/englercj/resource-loader/

Mari kita lihat di sumber:

https://github.com/englercj/resource-loader/blob/master/src/Loader.js#L20

https://github.com/englercj/resource-loader/blob/master/src/Loader.js#L446

Oke, mari kita lihat di dokumen, mungkin pixi tidak menyertakan dokumen untuk hal itu karena ada di modul lain: http://pixijs.download/dev/docs/PIXI.loaders.Loader.html , ini dia, konkurensi.

Bagaimanapun, ada banyak bagian dari pixi yang dapat dilakukan dengan lebih baik untuk proyek tertentu dan saya mendorong orang untuk menggunakan implementasi mereka sendiri jika memungkinkan, yang sesuai dengan proyek mereka. Saya memberkati Anda karena menggunakan pemuat khusus, tolong buat itu menjadi sebuah plugin dan kami akan merujuknya di daftar plugin.

Saya mengetahui bahwa pixi.loaders.Loader tidak dapat memuat sumber daya saat yang lain sedang memuat

Tidak yakin di mana Anda mempelajari ini, itu benar-benar dapat (dan memang) memuat sumber daya secara paralel. Pembungkus Anda di sekitar pemuat sumber daya hanya menambahkan antrean asinkron kedua di atas yang sudah digunakan di dalam pemuat sumber daya. Ia bahkan memiliki param konstruktor untuk mengonfigurasi berapa banyak sumber daya yang dimuat secara bersamaan pada satu waktu.

jika satu sumber daya dimuat, memuat dua kali akan mengeluarkan pengecualian yang tidak dapat ditangkap.

Anda dapat menangkap kesalahannya, tetapi Anda tidak perlu melakukannya. Inti dari kesalahan itu adalah untuk memberi tahu Anda menggunakan loader secara tidak benar. Apa yang harus Anda lakukan untuk caching adalah menyimpan sumber daya di suatu tempat di luar loader dan menggunakan middleware .pre() untuk melewati pemuatan sumber daya yang di-cache. Ada contoh middleware caching memori sederhana di repo dan contoh menggunakannya sebagai .pre() middleware di readme .

Jika Anda berencana untuk menggunakan instance loader beberapa kali, Anda harus memanggil .reset() sebelum menggunakannya lagi. Itu akan menghapus status loader dan akan siap memuat lebih banyak data. Itu juga menghapus objek .resources (tidak ada penghapusan, hanya menjatuhkan referensi) jadi pastikan Anda menggunakan atau menyimpan sumber daya yang dimuat di tempat lain sebelum memanggil .reset() .

@englercj

tetapi jika Anda menggunakan kode ikuti untuk memuat sumber daya:

    pixi.loader.add("/api/static/1.png")
      .load(() => {
        console.log("/api/static/1.png");
      });
    pixi.loader.add("/api/static/2.png")
      .load(() => {
        console.log("/api/static/2.png");
      });
    pixi.loader.add("/api/static/3.png")
      .load(() => {
        console.log("/api/static/3.png");
      });

itu akan terjadi:

Error: Uncaught (in promise): Error: Cannot add resources while the loader is running.

dan saya mendapatkan alasan dari mana #4100 , loader tidak dapat memuatnya secara paralel pada sumber daya root.
jadi, saya pikir itu mungkin dapat memiliki cara paralel membuat tugas beban pada sumber daya root.

anyway, terima kasih Anda berkontribusi.

@ivanpopelyshev terima kasih~
Saya senang membuat plugin, dan perlu beberapa waktu untuk mempelajari cara melakukannya.
jadi, di mana dapat menemukan dokumen tentang Cara Membuat Plugin PIXI?

Ada dokumen tentang plugin perender, tetapi untuk yang lainnya aturannya adalah "lakukan apa pun yang Anda inginkan, berikan file JS pengguna yang akan disertakan setelah file vanilla pixi.js".

Pixi dibangun dengan kelas, tidak ada variabel tersembunyi dalam konteks tersembunyi.

@ivanpopelyshev terima kasih~

@Lyoko-Jeremie Anda menggunakan loader secara tidak benar. Penggunaan yang benar adalah dengan menambahkan semua sumber daya yang ingin Anda muat, _kemudian_ Anda memanggil fungsi load(). Saat ini, Anda memanggil beban setelah setiap penambahan.

@themoonrat tetapi dalam kasus saya, sumber daya mana yang perlu dimuat tidak dapat diketahui sebelum perlu dimuat.
Saya sedang menulis demo seperti peta. dalam hal ini, memuat gambar gambar mana yang tergantung pada pengguna yang perlu melihat di mana.
pengguna mungkin bergerak lebih cepat daripada kecepatan memuat yang akan menyebabkan masalah Cannot add resources while the loader is running. .
dan saya tidak dapat memuat semua gambar ke browser karena semua gambar sangat besar.

@Lyoko-Jeremie Anda dapat menyalahgunakan mekanisme sumber daya yang bergantung.

Buat sumber daya yang tidak pernah benar-benar dimuat (middleware berjalan selamanya), tambahkan anak-anak ke dalamnya.

Pemuat tulang belakang menunggu dua sumber daya anak tambahan untuk dimuat: https://github.com/pixijs/pixi-spine/blob/master/src/loaders.ts#L7 , Anda dapat membuat middleware yang menunggu selamanya.

Sebagai opsi lain, Anda dapat mengambil sumber daya dan mengantre dari pemuat sumber daya, tetapi membuat kelas Pemuat Anda sendiri yang menyimpan sumber daya secara berbeda.

@ivanpopelyshev bagaimana cara membuat sumber daya yang tidak pernah dimuat secara akut?
Saya tidak menemukan info ini dari tempat lain, saya pikir peringatan ini perlu ditulis untuk membimbing agar pengguna baru dapat mengetahuinya. karena saya pikir itu adalah fitur anti-sadar.

@Lyoko-Jeremie

Jika Anda memerlukan pemuat khusus untuk gim Anda - Anda harus membuat kode sendiri. Lebih baik jika Anda menyelamatkan bagian yang ok atau meretas kode yang ada untuk menghemat waktu, tetapi untuk itu Anda harus mempelajari semua kode dari 1) repo pemuat sumber daya 2) semua middlewares pixi 3) middlewares lanjutan (Spine).

Saya dapat menjawab pertanyaan Anda setelah Anda menghabiskan beberapa jam untuk mempelajari semua kode itu.

Atau, Anda dapat melihat cara kerja fromImage , lebih mudah, menggunakan cache dan Anda mungkin melakukan sesuatu seperti itu.

@ivanpopelyshev Saya suka melakukan itu, tapi mungkin tidak ada waktu yang memberi saya untuk melakukannya.
di hari sekarang, pembungkus resourcesLoader ini sudah cukup untuk demo ini.
dan saya akan mencoba membaca kode loader di waktu luang saya.
Terima kasih banyak.

Sebagai ide untuk orang lain yang mengalami masalah ini, salah satu opsinya adalah memiliki kumpulan pemuat sumber daya. Jika Anda memerlukan pemuatan sumber daya, tetapi pemuat yang ada sudah sibuk, buat pemuat baru dan muat sumber daya dengan cara itu. Jika pemuat yang ada telah selesai, setel ulang dan gunakan kembali. Kemudian buat kelas pembungkus Anda sendiri untuk memiliki satu fungsi 'muat aset ini', dan itu mengelola pemuat sumber daya di latar belakang

@themoonrat Ide bagus!!!
jadi, dapatkah membuat lebih banyak pemuat sumber daya tanpa batasan lain? dan tidak perlu mengikat ke komponen pixi apa pun?

ya
Jika Anda ingin menggunakan versi PIXI dengan middlewarenya sendiri, maka Anda akan menggunakan

const loader = new PIXI.loaders.Loader();

Loader sangat ringan, jadi jangan khawatir memiliki banyak loader.

Satu-satunya hal yang saya ketahui, adalah bahwa pemuat memungkinkan Anda mengatur berapa banyak aset yang dapat diunduh secara bersamaan, yang memiliki default 10. Jika Anda memiliki 3 pemuat, Anda dapat secara efektif mencoba memuat hingga 30 sumber daya di sekali (saya yakin browser akan membatasi lebih rendah dari itu). Jadi mungkin Anda akan membuat setiap loader dengan batas yang lebih rendah? Atau mungkin pemuat utama Anda memiliki batas besar untuk menurunkan aset awal, tetapi pemuat aset 'dalam-streaming-aset' Anda memiliki batas yang lebih rendah untuk fokus menurunkan setiap aset lebih cepat.

@themoonrat terima kasih atas penjelasannya~~ I love you~

Ini sepertinya terjawab, terima kasih atas tanggapan Anda semua.

Saya pikir yang pertama, saya akan mengubah pemuat sumber daya saya ke versi kumpulan, dan setiap pemuat memuat satu permintaan. Ini tidak perlu berkali-kali.

Tapi saya masih punya pertanyaan, bagaimana cara menangkap kesalahan Resource dengan nama "..." sudah ada?

Periksa bahwa sumber daya dengan nama "..." sudah ada di loader.resources sebelum Anda benar-benar menambahkannya.

Sekali lagi, dengan sifat proyek Anda (PETA BESAR), saya menyarankan Anda untuk mengubah sikap, bermeditasi untuk beberapa waktu dan mempersiapkan ratusan pertanyaan seperti itu.

Mungkin dalam beberapa hari Anda akan memulai pencarian forum tentang "cara menampilkan peta besar" dan menemukan posting saya yang tak terhitung jumlahnya tentang pixi-tilemap, grafik, jerat, dll

UPD. kami siap berbagi pengalaman dan menjawab pertanyaan yang sangat sulit

oh~ forum yang bagus. terima kasih~

Saya pikir kasus saya mungkin memiliki beberapa perbedaan dari permainan.
semua gambar adalah satelit dunia nyata seperti gambar, memiliki ukuran besar (20MB per pice), dan bentuk tidak beraturan dengan banyak bagian tembus, lubang, berputar dan tumpang tindih.
Anda dapat membayangkan bahwa ini adalah peta google palsu, dengan gambar gambar non-Ubin, karena dalam beberapa alasan tidak dapat dijahit dan disegmentasi di sisi server.

jadi, akan ada beberapa pertanyaan berbeda tentang ini, tetapi saya akan mencoba mencari solusi dengan kerja keras. (tidak tahu harus menangis atau tertawa..)

tidak tahu harus menangis atau tertawa..

Ya! Itulah semangat!

semua pice adalah satelit dunia nyata seperti gambar, memiliki ukuran besar (20MB per pice),

Apakah target ponsel atau PC? Untuk PC Anda dapat menggunakan tekstur terkompresi (dds + static gzip di server + pixi-compressed-textures).

Di ponsel Anda akan mengalami kelambatan serius saat pixi mengunggahnya ke gpu, kami belum memiliki pengunggah progresif. Ada masalah tentang itu.

Pada dasarnya, Anda memerlukan tampilan kamera 2x, dan ketika kamera menyentuh tepi persegi panjang yang "disiapkan", Anda menambahkan lebih banyak objek, dan memasukkan yang lama ke dalam semacam antrian. Saat ini pixi gc membongkar dari videomemory tekstur apa pun yang tidak dirender dalam 4 menit atau lebih. ( PIXI.settings.GC_MODE , renderer.texture_gc.mode ). Anda tetap memerlukan antrian Anda sendiri karena Anda menyimpan loader dan data unduhan awal di dalamnya, untuk digunakan kembali jika pengguna memindahkan kamera ke belakang.

terima kasih atas pemberitahuan Anda ~

sekarang bukan di ponsel, tetapi siapa yang tahu masa depan yang tidak dapat diprediksi?

di sisi lain. Untuk beberapa alasan, banyak pengguna masa depan mungkin menggunakan browser lama yang tidak mendukung WebGL. (alasan ini Anda mungkin tidak tahu jika Anda tidak pernah mengembangkan program untuk orang-orang Cina.)
jadi, saya juga perlu menguji kinerja kanvas ketika demo OK, dan membuat keputusan pada saat itu, apakah akan menampilkan halaman Banned atau tidak kepada pengguna brwser lama dan membiarkan mereka menginstal browser baru. (Tak berdaya dan tunda tangan...)

dan saya punya fitur Unfragmented Zoom, artinya Unlimited Zoom. (Wajah Anjing~)

Jika Anda yakin dengan audiens target Anda , lakukan untuk kanvas dan uji untuk kanvas. Jika memungkinkan untuk benar-benar mengukur pemirsa, jika Anda mengetahui sumber lalu lintas Anda, Anda dapat memperkirakan berapa banyak orang yang mengaktifkan webgl di browser mereka. Jika 99,5%, gunakan hanya webgl. JIKA win-xp dan IE10 maka .. yah ... semoga berhasil :)

Utas ini telah dikunci secara otomatis karena tidak ada aktivitas terbaru setelah ditutup. Silakan buka edisi baru untuk bug terkait.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat