private async Task BuildNodeGraph(float regionRadius, float bodyRadius, float toleranceRadius, CancellationToken cancellation) { this.regionRadius = Mathf.Max(regionRadius, 0); this.bodyRadius = Mathf.Max(bodyRadius, 0); cancellation.ThrowIfCancellationRequested(); // Build stencil. var stencil = await VisionStencil.BuildAsync(regionRadius, bodyRadius, toleranceRadius); cancellation.ThrowIfCancellationRequested(); // Build vision edges. int visionMaxOffset = Mathf.FloorToInt(regionRadius); var tasks = new List <Task>(); foreach (var node in nodes) { if (node is null) { continue; } var currentNode = node; var task = Task.Run(() => { // See all nodes around current one; add edge in graph for every visible node. foreach (var destinationPosition in Enumerables.InSegment2( currentNode.Position - visionMaxOffset * Vector2Int.one, currentNode.Position + visionMaxOffset * Vector2Int.one)) { cancellation.ThrowIfCancellationRequested(); if (!Enumerables.IsIndex(destinationPosition, nodes)) { continue; } var visionPath = stencil.GetVisionPath(currentNode.Position, destinationPosition); if (visionPath is null) { continue; } bool visible = true; foreach (var passedPosition in visionPath) { var passedNode = nodes[passedPosition.x, passedPosition.y]; if (passedNode is null) { visible = false; break; } } if (visible) { var destinationNode = nodes[destinationPosition.x, destinationPosition.y]; currentNode.AddVisible(destinationNode); } } }); tasks.Add(task); cancellation.ThrowIfCancellationRequested(); } await Task.WhenAll(tasks); cancellation.ThrowIfCancellationRequested(); // Build reach edges. tasks.Clear(); foreach (var node in nodes) { if (node is null) { continue; } var currentNode = node; var task = Task.Run(() => { // Try reach all nodes around current one; add edge in graph for every reachable node. var reachable = DijkstraDense.FindAllReachable(currentNode.AsVision, regionRadius); cancellation.ThrowIfCancellationRequested(); foreach (var nodeAndDistance in reachable) { var reachedPosition = nodeAndDistance.Key.Position; var reachedNode = nodes[reachedPosition.x, reachedPosition.y]; currentNode.AddReachable(reachedNode, nodeAndDistance.Value); } cancellation.ThrowIfCancellationRequested(); }); tasks.Add(task); cancellation.ThrowIfCancellationRequested(); } await Task.WhenAll(tasks); cancellation.ThrowIfCancellationRequested(); }