Peta
Dalam 
artikel sebelumnya, saya melihat apa 
sistem Job yang baru, bagaimana cara kerjanya, cara membuat tugas, mengisinya dengan data dan melakukan perhitungan multi-threaded, dan hanya menjelaskan secara singkat di mana Anda dapat menggunakan sistem ini. Pada artikel ini, saya akan mencoba mem-parsing contoh spesifik di mana Anda dapat menggunakan sistem ini untuk mendapatkan lebih banyak kinerja.
Karena sistem ini awalnya dikembangkan dengan tujuan bekerja dengan data, itu bagus untuk menyelesaikan tugas-tugas pencarian jalur.
Unity telah memiliki 
pathfinder NavMesh yang baik, tetapi tidak berfungsi dalam proyek 2D, meskipun ada banyak solusi siap pakai pada 
aset yang sama. Baiklah, dan kami akan mencoba untuk membuat bukan hanya sistem yang akan mencari cara pada peta yang dibuat, tetapi membuat peta ini sangat dinamis, sehingga setiap kali ada perubahan, sistem akan membuat peta baru, dan semua ini tentu saja akan kami hitung menggunakan sistem tugas baru, agar tidak memuat utas utama.
Contoh pengoperasian sistem Dalam contoh ini, kotak dibangun di peta, ada bot dan hambatan. Kotak dibangun kembali setiap kali kami mengubah properti peta apa pun, baik ukuran maupun posisinya.
Untuk pesawat, saya menggunakan 
SpriteRenderer sederhana, komponen ini memiliki properti 
batas yang luar biasa yang dengannya Anda dapat dengan mudah mengetahui ukuran peta.
Pada dasarnya itu semua hanya untuk permulaan, tetapi kami tidak akan berhenti dan segera memulai bisnis.
Mari kita mulai dengan skrip. Dan yang pertama adalah naskah penghalang 
Rintangan .
Rintanganpublic class Obstacle : MonoBehaviour { } 
 Di dalam kelas 
Rintangan , kita akan menangkap semua perubahan hambatan pada peta, misalnya, mengubah posisi atau ukuran suatu objek.
Selanjutnya, Anda bisa membuat kelas peta peta, di mana kotak akan dibangun, dan mewarisinya dari kelas 
Kendala .
Peta public sealed class Map : Obstacle { } 
 Kelas 
Peta juga akan melacak semua perubahan pada peta untuk membangun kembali kisi jika perlu.
Untuk melakukan ini, isi kelas dasar 
Rintangan dengan semua variabel dan metode yang diperlukan untuk melacak perubahan objek.
Rintangan public class Obstacle : MonoBehaviour { public new SpriteRenderer renderer { get; private set;} private Vector2 tempSize; private Vector2 tempPos; protected virtual void Awake() { this.renderer = GetComponent<SpriteRenderer>(); this.tempSize = this.size; this.tempPos = this.position; } public virtual bool CheckChanges() { Vector2 newSize = this.size; float diff = (newSize - this.tempSize).sqrMagnitude; if (diff > 0.01f) { this.tempSize = newSize; return true; } Vector2 newPos = this.position; diff = (newPos - this.tempPos).sqrMagnitude; if (diff > 0.01f) { this.tempPos = newPos; return true; } return false; } public Vector2 size { get { return this.renderer.bounds.size;} } public Vector2 position { get { return this.transform.position;} } } 
 Di sini, variabel 
renderer akan memiliki referensi ke komponen 
SpriteRenderer , dan 
variabel tempSize dan 
tempPos akan digunakan untuk melacak perubahan dalam ukuran dan posisi objek.
Metode virtual 
Sedar akan digunakan untuk menginisialisasi variabel, dan metode virtual 
CheckChanges akan melacak perubahan saat ini dalam ukuran dan posisi objek dan mengembalikan hasil 
boolean .
Untuk saat ini, mari kita tinggalkan skrip 
Rintangan dan beralih ke skrip peta 
Peta itu sendiri, di mana kita juga mengisinya dengan parameter yang diperlukan untuk pekerjaan.
Peta public sealed class Map : Obstacle { [Range(0.1f, 1f)] public float nodeSize = 0.5f; public Vector2 offset = new Vector2(0.5f, 0.5f); } 
 Variabel 
nodeSize akan menunjukkan ukuran sel pada peta, di sini saya membatasi ukurannya dari 0,1 hingga 1 sehingga sel-sel pada grid tidak terlalu kecil, tetapi juga terlalu besar. Variabel 
offset akan digunakan untuk membuat inden peta saat membuat kisi sehingga kisi tidak membangun di sepanjang tepi peta.
Karena sekarang ada dua variabel baru di peta, ternyata perubahan mereka juga perlu dilacak. Untuk melakukan ini, tambahkan beberapa variabel dan overload metode 
CheckChanges di kelas 
Peta .
Peta public sealed class Map : Obstacle { [Range(0.1f, 1f)] public float nodeSize = 0.5f; public Vector2 offset = new Vector2(0.5f, 0.5f); private float tempNodeSize; private Vector2 tempOffset; protected override void Awake() { base.Awake(); this.tempNodeSize = this.nodeSize; this.tempOffset = this.offset; } public override bool CheckChanges() { float diff = Mathf.Abs(this.tempNodeSize - this.nodeSize); if (diff > 0.01f) { this.tempNodeSize = this.nodeSize; return true; } diff = (this.tempOffset - this.offset).sqrMagnitude; if (diff > 0.01f) { this.tempOffset = this.offset; return true; } return base.CheckChanges(); } } 
 Selesai Sekarang Anda dapat membuat sprite peta di atas panggung dan melemparkan skrip 
Peta di atasnya.

Kami akan melakukan hal yang sama dengan penghalang - buat sprite sederhana di atas panggung dan lemparkan skrip 
Hambatan ke atasnya.

Sekarang kita memiliki objek peta dan rintangan di atas panggung.
Skrip 
Peta akan bertanggung jawab untuk melacak semua perubahan pada peta, di mana dalam metode 
Pembaruan kami akan memeriksa setiap frame untuk perubahan.
Peta public sealed class Map : Obstacle {  private bool requireRebuild; private void Update() { UpdateChanges(); } private void UpdateChanges() { if (this.requireRebuild) { print(“  ,   !”); this.requireRebuild = false; } else { this.requireRebuild = CheckChanges(); } }  } 
 Jadi, dalam metode 
UpdateChanges, peta hanya akan melacak perubahannya sejauh ini. Anda bahkan dapat memulai permainan sekarang dan mencoba mengubah ukuran peta atau 
mengimbangi offset untuk memastikan bahwa semua perubahan dilacak.
Sekarang Anda perlu melacak perubahan hambatan itu sendiri di peta. Untuk melakukan ini, kami akan menempatkan setiap rintangan dalam daftar di peta, yang pada gilirannya akan memperbarui setiap bingkai dalam metode 
Pembaruan .
Di kelas 
Peta , buat daftar semua hambatan yang mungkin ada di peta dan beberapa metode statis untuk mendaftarkannya.
Peta public sealed class Map : Obstacle {  private static Map ObjInstance; private List<Obstacle> obstacles = new List<Obstacle>();  public static bool RegisterObstacle(Obstacle obstacle) { if (obstacle == Instance) return false; else if (Instance.obstacles.Contains(obstacle) == false) { Instance.obstacles.Add(obstacle); Instance.requireRebuild = true; return true; } return false; } public static bool UnregisterObstacle(Obstacle obstacle) { if (Instance.obstacles.Remove(obstacle)) { Instance.requireRebuild = true; return true; } return false; } public static Map Instance { get { if (ObjInstance == null) ObjInstance = FindObjectOfType<Map>(); return ObjInstance; } } } 
 Dalam metode 
RegisterObstacle statis, kami akan mendaftarkan hambatan 
Kendala baru di peta dan menambahkannya ke daftar, tetapi pertama-tama penting untuk mempertimbangkan bahwa peta itu sendiri juga diwarisi dari kelas 
Kendala dan oleh karena itu kita perlu memeriksa apakah kita mencoba mendaftarkan kartu itu sendiri sebagai hambatan.
Metode statis 
UnregisterObstacle , sebaliknya, menghilangkan hambatan dari peta dan menghilangkannya dari daftar ketika kita membiarkannya dihancurkan.
Pada saat yang sama, setiap kali kita menambah atau menghapus hambatan dari peta, kita perlu membuat ulang peta itu sendiri, jadi setelah menjalankan metode statis ini, setel variabel 
requireRebuild menjadi 
true .
Juga, untuk memiliki akses mudah ke skrip 
Peta dari skrip apa pun, saya membuat properti 
Instance statis yang akan mengembalikan kepada saya instance dari 
Peta ini .
Sekarang, mari kita kembali ke skrip 
Rintangan di mana kita akan mendaftarkan penghalang pada peta. Untuk melakukan ini, tambahkan beberapa metode 
OnEnable dan 
OnDisable untuk itu.
Rintangan public class Obstacle : MonoBehaviour {  protected virtual void OnEnable() { Map.RegisterObstacle(this); } protected virtual void OnDisable() { Map.UnregisterObstacle(this); } } 
 Setiap kali kita membuat penghalang baru saat bermain di peta, itu akan secara otomatis mendaftar di metode 
OnEnable , di mana ia akan diperhitungkan ketika membangun kisi baru dan menghapus diri kita dari peta dalam metode 
OnDisable ketika dihancurkan atau dinonaktifkan.
Tetap hanya melacak perubahan hambatan itu sendiri dalam skrip 
Peta dalam metode 
CheckChanges yang kelebihan beban.
Peta public sealed class Map : Obstacle {  public override bool CheckChanges() { float diff = Mathf.Abs(this.tempNodeSize - this.nodeSize); if (diff > 0.01f) { this.tempNodeSize = this.nodeSize; return true; } diff = (this.tempOffset - this.offset).sqrMagnitude; if (diff > 0.01f) { this.tempOffset = this.offset; return true; } foreach(Obstacle obstacle in this.obstacles) { if (obstacle.CheckChanges()) return true; } return base.CheckChanges(); }  } 
 Sekarang kita memiliki peta, rintangan - secara umum, semua yang Anda butuhkan untuk membangun kisi dan sekarang Anda dapat beralih ke hal yang paling penting.
Meshing
Kotak, dalam bentuknya yang paling sederhana, adalah susunan titik dua dimensi. Untuk membangunnya, Anda perlu mengetahui ukuran peta dan ukuran titik di atasnya, setelah beberapa perhitungan kami mendapatkan jumlah titik secara horizontal dan vertikal, ini adalah kisi kami.
Ada banyak cara untuk menemukan jalur di grid. Dalam artikel ini, bagaimanapun, hal utama adalah untuk memahami bagaimana cara yang benar menggunakan kemampuan sistem tugas, jadi di sini saya tidak akan mempertimbangkan opsi yang berbeda untuk menemukan jalan, kelebihan dan kekurangannya, tetapi saya akan mengambil opsi pencarian paling sederhana 
A * .
Dalam hal ini, semua titik pada grid harus memiliki, selain posisi, koordinat dan properti patensi.
Dengan patensi, saya pikir semuanya jelas mengapa diperlukan, tetapi koordinat akan menunjukkan urutan titik pada grid, koordinat ini tidak terikat secara khusus dengan posisi titik di ruang angkasa. Gambar di bawah ini menunjukkan grid sederhana yang menunjukkan perbedaan koordinat dari suatu posisi.
 Kenapa koordinatnya?
Kenapa koordinatnya?Faktanya adalah bahwa dalam kesatuan, untuk menunjukkan posisi suatu objek di ruang angkasa, 
float sederhana 
digunakan yang sangat tidak akurat dan dapat berupa angka pecahan atau negatif, sehingga akan sulit untuk menggunakannya untuk mengimplementasikan pencarian jalur pada peta. Koordinat dibuat dalam bentuk 
int yang jelas yang akan selalu positif dan dengan itu lebih mudah untuk bekerja dengan ketika mencari titik tetangga.
Pertama, mari kita tentukan objek titik, ini akan menjadi struktur 
Node sederhana.
Node public struct Node { public int id; public Vector2 position; public Vector2Int coords; } 
 Struktur ini akan berisi posisi 
posisi dalam bentuk 
Vector2 , di mana dengan variabel ini kita akan menggambar titik di ruang angkasa. 
Variabel koordinat koordinat dalam bentuk 
Vector2Int akan menunjukkan koordinat suatu titik di peta, dan variabel 
id , nomor akun numeriknya, dengan menggunakannya kita akan membandingkan titik-titik yang berbeda di grid dan memeriksa keberadaan suatu titik.
Patensi titik akan ditunjukkan dalam bentuk properti 
booleannya , tetapi karena kita tidak dapat menggunakan 
tipe data yang dapat 
dikonversi dalam sistem tugas, kami akan menunjukkan patennya dalam bentuk nomor 
int , karena ini saya menggunakan enumerasi sederhana 
NodeType , di mana: 0 bukan titik yang bisa dilewati, dan 1 lumayan.
NodeType dan Node public enum NodeType { NonWalkable = 0, Walkable = 1 } public struct Node { public int id; public Vector2 position; public Vector2Int coords; private int nodeType; public bool isWalkable { get { return this.nodeType == (int)NodeType.Walkable;} } public Node(int id, Vector2 position, Vector2Int coords, NodeType type) { this.id = id; this.position = position; this.coords = coords; this.nodeType = (int)type; } } 
 Juga, untuk kenyamanan bekerja dengan suatu titik, saya akan membebani metode 
Persamaan untuk membuatnya lebih mudah untuk membandingkan titik dan juga melengkapi metode verifikasi untuk keberadaan suatu titik.
Node public struct Node {  public override bool Equals(object obj) { if (obj is Node) { Node other = (Node)obj; return this.id == other.id; } else return base.Equals(obj); } public static implicit operator bool(Node node) { return node.id > 0; } } 
 Karena nomor 
id titik di grid akan mulai dengan 1 unit, saya akan memeriksa keberadaan titik sebagai syarat bahwa 
idnya lebih besar dari 0.
Pergi ke kelas 
Peta di mana kami akan menyiapkan segalanya untuk membuat peta.
Kami sudah memiliki pemeriksaan untuk mengubah parameter peta, sekarang kita perlu menentukan bagaimana proses membangun grid akan dilakukan. Untuk melakukan ini, buat satu variabel baru dan beberapa metode.
Peta public sealed class Map : Obstacle {  public bool rebuilding { get; private set; } public void Rebuild() {} private void OnRebuildStart() {} private void OnRebuildFinish() {}  } 
 Properti 
pembangunan kembali akan menunjukkan apakah proses 
meshing sedang berlangsung. Metode 
Rebuild akan mengumpulkan data dan tugas untuk membangun grid, maka metode 
OnRebuildStart akan memulai proses pembuatan grid dan metode 
OnRebuildFinish akan mengumpulkan data dari tugas.
Sekarang mari kita ubah metode 
UpdateChanges sedikit sehingga kondisi grid diperhitungkan.
Peta public sealed class Map : Obstacle {  public bool rebuilding { get; private set; } private void UpdateChanges() { if (this.rebuilding) { print(“  ...”); } else { if (this.requireRebuild) { print(“  ,   !”); Rebuild(); } else { this.requireRebuild = CheckChanges(); } } } public void Rebuild() { if (this.rebuilding) return; print(“ !”); OnRebuildStart(); } private void OnRebuildStart() { this.rebuilding = true; } private void OnRebuildFinish() { this.rebuilding = false; }  } 
 Seperti yang Anda lihat sekarang dalam metode 
UpdateChanges ada kondisi bahwa saat membangun mesh lama tidak mulai membangun yang baru, dan juga dalam metode 
Rebuild , tindakan pertama memeriksa apakah proses meshing sudah dalam proses.
Pemecahan masalah
Sekarang sedikit tentang proses membangun peta.
Karena kita akan menggunakan sistem tugas dan membangun grid secara paralel untuk membangun peta, saya menggunakan jenis tugas 
IJobParallelFor , yang akan dieksekusi beberapa kali. Agar tidak memuat proses konstruksi dengan satu tugas terpisah, kami akan menggunakan kumpulan tugas yang dikemas ke dalam satu 
JobHandle .
Paling sering, untuk membangun kisi, gunakan dua siklus yang saling bersarang untuk membangun, misalnya, secara horizontal dan vertikal. Dalam contoh ini, kita juga akan membangun grid terlebih dahulu secara horizontal dan kemudian secara vertikal. Untuk melakukan ini, kita menghitung jumlah titik horizontal dan vertikal dalam metode 
Rebuild , kemudian dalam metode 
Rebuild kita melalui siklus di sepanjang titik-titik vertikal, dan kita akan membangun yang horisontal secara paralel dalam tugas. Untuk lebih membayangkan proses pembangunan, lihat animasi di bawah ini.
Jumlah titik vertikal akan menunjukkan jumlah tugas, pada gilirannya, setiap tugas akan membangun poin hanya secara horizontal, setelah menyelesaikan semua tugas, poin-poin tersebut dijumlahkan dalam satu daftar. Itulah sebabnya saya perlu menggunakan tugas seperti 
IJobParallelFor untuk melewati indeks titik di grid secara horizontal ke metode 
Execute .
Jadi kami memiliki struktur titik, sekarang Anda dapat membuat struktur tugas 
Pekerjaan dan mewarisinya dari antarmuka 
IJobParallelFor , semuanya sederhana di sini.
Ayub public struct Job : IJobParallelFor { public void Execute(int index) {} } 
 Kami kembali ke metode 
Rebuild kelas 
peta , di mana kami akan membuat perhitungan yang diperlukan untuk pengukuran grid.
Peta public sealed class Map : Obstacle {  public void Rebuild() { if (this.rebuilding) return; print(“ !”); Vector2 mapSize = this.size - this.offset * 2f; int horizontals = Mathf.RoundToInt(mapSize.x / this.nodeSize); int verticals = Mathf.RoundToInt(mapSize.y / this.nodeSize); if (horizontals <= 0) { OnRebuildFinish(); return; } Vector2 center = this.position; Vector2 origin = center - (mapSize / 2f); OnRebuildStart(); }  } 
 Dalam metode 
Rebuild , kami menghitung ukuran persis peta 
mapSize , dengan mempertimbangkan lekukan, kemudian secara 
vertikal kami menulis jumlah titik secara vertikal, dan dalam 
horizontal jumlah titik secara horizontal. Jika jumlah titik vertikal adalah 0, maka kita berhenti membangun peta dan memanggil metode 
OnRebuildFinish untuk menyelesaikan proses. Variabel 
asal akan menunjukkan tempat dari mana kita akan mulai membangun kisi - dalam contoh, ini adalah titik kiri bawah pada peta.
Sekarang Anda dapat pergi ke tugas sendiri dan mengisinya dengan data.
Selama konstruksi grid, tugas akan membutuhkan array 
NativeArray di mana kita akan menempatkan titik, juga karena kita memiliki hambatan pada peta, kita juga akan perlu untuk meneruskannya ke tugas, untuk ini kita akan menggunakan array 
NativeArray lain, maka kita perlu ukuran poin dalam masalah , posisi awal dari tempat kita akan membangun poin, serta koordinat awal seri.
Ayub public struct Job : IJobParallelFor { [WriteOnly] public NativeArray<Node> array; [ReadOnly] public NativeArray<Rect> bounds; public float nodeSize; public Vector2 startPos; public Vector2Int startCoords; public void Execute(int index) {} } 
 Saya menandai array poin dengan atribut 
WriteOnly, karena dalam tugas itu hanya perlu untuk " 
menulis " poin yang diterima ke array, sebaliknya, array 
batas rintangan ditandai dengan atribut 
ReadOnly karena dalam tugas kita hanya akan " 
membaca " data dari array ini.
Nah, untuk sekarang, mari kita lanjutkan ke penghitungan poin sendiri nanti.
Sekarang kembali ke kelas 
Peta , di mana kami menunjukkan semua variabel yang terlibat dalam tugas.
Di sini, pertama-tama, kita perlu 
menangani tugas 
- tugas global, serangkaian hambatan dalam bentuk 
NativeArray , daftar tugas yang akan berisi semua poin yang diterima di grid dan 
Kamus dengan semua koordinat dan poin di peta, sehingga akan lebih mudah untuk mencari mereka nanti.
Peta public sealed class Map : Obstacle {  private JobHandle handle; private NativeArray<Rect> bounds; private HashSet<NativeArray<Node>> jobs = new HashSet<NativeArray<Node>>(); private Dictionary<Vector2Int, Node> nodes = new Dictionary<Vector2Int, Node>();  } 
 Sekarang lagi, kita kembali ke metode 
Rebuild dan terus membangun grid.
Pertama, inisialisasi array 
batas rintangan untuk meneruskannya ke tugas.
Bangun kembali public void Rebuild() {  Vector2 center = this.position; Vector2 origin = center - (mapSize / 2f); int count = this.obstacles.Count; if (count > 0) { this.bounds = new NativeArray<Rect>(count, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); } OnRebuildStart(); } 
 Di sini kita membuat instance 
NativeArray melalui konstruktor baru dengan tiga parameter. Saya memeriksa dua parameter pertama dalam artikel sebelumnya, tetapi parameter ketiga akan membantu kita menghemat sedikit waktu membuat array. Faktanya adalah bahwa kita akan menulis data ke array segera setelah pembuatannya, yang berarti kita tidak perlu memastikan bahwa itu dihapus. Parameter ini berguna untuk 
NativeArray yang hanya akan digunakan dalam mode 
baca dalam tugas.
Jadi, kemudian kita mengisi array 
batas dengan data.
Bangun kembali public void Rebuild() {  Vector2 center = this.position; Vector2 origin = center - (mapSize / 2f); int count = this.obstacles.Count; if (count > 0) { this.bounds = new NativeArray<Rect>(count, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); for(int i = 0; i < count; i++) { Obstacle obs = this.obstacles[i]; Vector2 position = obs.position; Rect rect = new Rect(Vector2.zero, obs.size); rect.center = position; this.bounds[i] = rect; } } OnRebuildStart(); } 
 Sekarang kita dapat beralih ke membuat tugas, untuk ini kita akan melalui siklus melalui semua baris vertikal grid.
Bangun kembali public void Rebuild() {  Vector2 center = this.position; Vector2 origin = center - (mapSize / 2f); int count = this.obstacles.Count; if (count > 0) { this.bounds = new NativeArray<Rect>(count, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); for(int i = 0; i < count; i++) { Obstacle obs = this.obstacles[i]; Vector2 position = obs.position; Rect rect = new Rect(Vector2.zero, obs.size); rect.center = position; this.bounds[i] = rect; } } for (int i = 0; i < verticals; i++) { float xPos = origin.x; float yPos = origin.y + (i * this.nodeSize) + this.nodeSize / 2f; } OnRebuildStart(); } 
 Untuk mulai dengan, di 
xPos dan 
yPos kita mendapatkan posisi horizontal awal seri.
Bangun kembali public void Rebuild() {  Vector2 center = this.position; Vector2 origin = center - (mapSize / 2f); int count = this.obstacles.Count; if (count > 0) { this.bounds = new NativeArray<Rect>(count, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); for(int i = 0; i < count; i++) { Obstacle obs = this.obstacles[i]; Vector2 position = obs.position; Rect rect = new Rect(Vector2.zero, obs.size); rect.center = position; this.bounds[i] = rect; } } for (int i = 0; i < verticals; i++) { float xPos = origin.x; float yPos = origin.y + (i * this.nodeSize) + this.nodeSize / 2f; NativeArray<Node> array = new NativeArray<Node>(horizontals, Allocator.Persistent); Job job = new Job(); job.startCoords = new Vector2Int(i * horizontals, i); job.startPos = new Vector2(xPos, yPos); job.nodeSize = this.nodeSize; job.bounds = this.bounds; job.array = array; } OnRebuildStart(); } 
 Selanjutnya, kita membuat array 
NativeArray sederhana di mana titik-titik dalam tugas akan ditempatkan, di sini untuk array 
array Anda perlu menentukan berapa banyak poin yang akan dibuat secara horizontal dan jenis alokasi 
Persistent , karena tugas dapat memakan waktu lebih dari satu frame.
Setelah itu, buat contoh tugas 
Pekerjaan itu sendiri, letakkan koordinat awal dari seri 
startCoords , posisi awal dari seri 
startPos , ukuran titik 
nodeSize , 
batas array hambatan, dan pada akhirnya array titik itu sendiri.
Tinggal 
menangani tugas dan daftar tugas global.
Bangun kembali public void Rebuild() {  Vector2 center = this.position; Vector2 origin = center - (mapSize / 2f); int count = this.obstacles.Count; if (count > 0) { this.bounds = new NativeArray<Rect>(count, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); for(int i = 0; i < count; i++) { Obstacle obs = this.obstacles[i]; Vector2 position = obs.position; Rect rect = new Rect(Vector2.zero, obs.size); rect.center = position; this.bounds[i] = rect; } } for (int i = 0; i < verticals; i++) { float xPos = origin.x; float yPos = origin.y + (i * this.nodeSize) + this.nodeSize / 2f; NativeArray<Node> array = new NativeArray<Node>(horizontals, Allocator.Persistent); Job job = new Job(); job.startCoords = new Vector2Int(i * horizontals, i); job.startPos = new Vector2(xPos, yPos); job.nodeSize = this.nodeSize; job.bounds = this.bounds; job.array = array; this.handle = job.Schedule(horizontals, 3, this.handle); this.jobs.Add(array); } OnRebuildStart(); } 
 Selesai Kami memiliki daftar tugas dan 
pegangan umum mereka, sekarang kami dapat menjalankan 
pegangan ini 
dengan memanggil metode 
Lengkapnya di metode 
OnRebuildStart .
Onrebuildstart private void OnRebuildStart() { this.rebuilding = true; this.handle.Complete(); } 
 Karena variabel 
pembangunan kembali akan menunjukkan bahwa proses 
meshing sedang berlangsung, metode 
UpdateChanges itu sendiri juga harus menentukan kondisi ketika proses ini akan berakhir menggunakan 
pegangan dan properti 
IsCompleted -nya.
Pembaruan perubahan private void UpdateChanges() { if (this.rebuilding) { print(“  ...”); if (this.handle.IsCompleted) OnRebuildFinish(); } else { if (this.requireRebuild) { print(“  ,   !”); Rebuild(); } else { this.requireRebuild = CheckChanges(); } } } 
 Setelah menyelesaikan tugas, metode 
OnRebuildFinish akan dipanggil di mana kami akan mengumpulkan poin yang diterima ke dalam satu daftar 
Kamus umum, dan yang paling penting, untuk menghapus sumber daya yang ditempati.
OnRebuildFinish  private void OnRebuildFinish() { this.nodes.Clear(); foreach (NativeArray<Node> array in this.jobs) { foreach (Node node in array) this.nodes.Add(node.coords, node); array.Dispose(); } this.jobs.Clear(); if (this.bounds.IsCreated) this.bounds.Dispose(); this.requireRebuild = this.rebuilding = false; } 
 Pertama, kita menghapus kamus node dari titik sebelumnya, kemudian menggunakan foreach loop untuk memilah-milah semua poin yang kita terima dari tugas dan menempatkannya dalam kamus node , di mana kuncinya adalah koordinat ( BUKAN posisi !) Dari titik tersebut, dan nilainya adalah titik itu sendiri. Dengan bantuan kamus ini, akan lebih mudah bagi kami untuk mencari titik tetangga di peta. Setelah mengisi, kami menghapus array array menggunakan metode Buang dan pada akhirnya kami menghapus daftar tugas pekerjaan itu sendiri .Anda juga perlu menghapus batasan batasan jika itu sebelumnya dibuat.Setelah semua tindakan ini, kami mendapatkan daftar semua poin di peta dan sekarang Anda dapat menggambarnya di atas panggung.Untuk melakukan ini, di kelas Peta , buat metode OnDrawGizmos di mana kita akan menggambar poin.Peta public sealed class Map : Obstacle {  #if UNITY_EDITOR private void OnDrawGizmos() {} #endif } 
 Sekarang melalui loop kita menggambar setiap titik.Peta public sealed class Map : Obstacle {  #if UNITY_EDITOR private void OnDrawGizmos() { foreach (Node node in this.nodes.Values) { Gizmos.DrawWireSphere(node.position, this.nodeSize / 10f); } } #endif } 
 Setelah semua tindakan ini, peta kami terlihat agak membosankan, untuk benar-benar mendapatkan kisi, Anda perlu titik untuk terhubung satu sama lain.Untuk mencari titik tetangga, kita hanya perlu menemukan titik yang diinginkan dengan koordinatnya dalam 8 arah, jadi di kelas Peta kita akan membuat array statis arah Arah dan metode pencarian sel dengan koordinat GetNode -nya .Peta public sealed class Map : Obstacle { public static readonly Vector2Int[] Directions = { Vector2Int.up, new Vector2Int(1, 1), Vector2Int.right, new Vector2Int(1, -1), Vector2Int.down, new Vector2Int(-1, -1), Vector2Int.left, new Vector2Int(-1, 1), };  public Node GetNode(Vector2Int coords) { Node result = default(Node); try { result = this.nodes[coords]; } catch {} return result; } #if UNITY_EDITOR private void OnDrawGizmos() {} #endif } 
 Metode GetNode akan mengembalikan titik dengan koordinat dari daftar node , tetapi Anda harus melakukan ini dengan hati-hati, karena jika koordinat Vector2Int salah, kesalahan akan terjadi, jadi di sini kami menggunakan blok pintas try catch exception , yang akan membantu memotong pengecualian dan tidak " menggantung " seluruh aplikasi dengan kesalahan.Selanjutnya, kita akan melalui siklus di semua arah dan mencoba untuk menemukan titik-titik tetangga dalam metode OnDrawGizmos , dan yang paling penting, jangan lupa untuk mempertimbangkan paten dari titik tersebut.Ondrawgizmos  #if UNITY_EDITOR private void OnDrawGizmos() { Color c = Gizmos.color; foreach (Node node in this.nodes.Values) { Color newColor = Color.white; if (node.isWalkable) newColor = new Color32(153, 255, 51, 255); else newColor = Color.red; Gizmos.color = newColor; Gizmos.DrawWireSphere(node.position, this.nodeSize / 10f); newColor = Color.green; Gizmos.color = newColor; if (node.isWalkable) { for (int i = 0; i < Directions.Length; i++) { Vector2Int coords = node.coords + Directions[i]; Node connection = GetNode(coords); if (connection) { if (connection.isWalkable) Gizmos.DrawLine(node.position, connection.position); } } } } Gizmos.color = c; } #endif 
 Sekarang Anda dapat dengan aman memulai permainan dan melihat apa yang terjadi.Dalam contoh ini, kami hanya membuat grafik itu sendiri menggunakan tugas, tetapi inilah yang terjadi setelah saya mengacaukan sistem algoritma A * itu sendiri , yang juga menggunakan sistem Pekerjaan untuk menemukan jalan, sumber di akhir artikel .Jadi Anda dapat menggunakan sistem tugas baru untuk tujuan Anda dan membangun sistem yang menarik tanpa banyak usaha.Seperti pada artikel sebelumnya, sistem tugas digunakan tanpa ECS , tetapi jika Anda menggunakan sistem ini bersama dengan ECS , Anda dapat mencapai hasil yang luar biasa dalam keuntungan kinerja. Good luck !Sumber Proyek Path Finder