private void Awake() { GetComponent <MeshRenderer>().material = GraphicsE.diffuseVertexColor; var template = MeshE.TetrahedronFlat(0.3f); templateVertices = template.vertices; templateVerticesLength = template.vertices.Length; templateTriangles = template.triangles; templateTrianglesLength = template.triangles.Length; swarmCount = Mathf.Min(65000 / templateVerticesLength, swarmCount); vertices = new Vector3[swarmCount * templateVerticesLength]; triangles = new int[swarmCount * templateTrianglesLength]; colors32 = new Color32[swarmCount * templateVerticesLength]; for (var i = 0; i < swarmCount; i++) { var boid = new Boid { position = Random.insideUnitSphere * spawnSphere, rotation = Random.rotation, velocity = Random.onUnitSphere * maxSpeed }; boids.Add(boid); SetBoidVertices(boid, i); SetBoidTriangles(i); SetBoidColors(boid, i); } mesh = new Mesh { name = "Boids", vertices = vertices, triangles = triangles, colors32 = colors32 }; mesh.RecalculateNormals(); mesh.MarkDynamic(); GetComponent <MeshFilter>().mesh = mesh; StartCoroutine(SimulateCoroutine()); }
/// <summary> /// Generate new colors and positions for boids /// </summary> public Mesh Generate(Config config) { this.config = config; // Avoid vertex count overflow config.swarmCount = Mathf.Min(65000 / config.template.vertexCount, config.swarmCount); // Optimization trick: in each frame we simulate only small percent of all boids maxSimulationSteps = Mathf.RoundToInt(config.swarmCount * config.simulationPercent); int vertexCount = config.swarmCount * config.template.vertexCount; draft = new MeshDraft { name = "Boids", vertices = new List <Vector3>(vertexCount), triangles = new List <int>(vertexCount), normals = new List <Vector3>(vertexCount), uv = new List <Vector2>(vertexCount), colors = new List <Color>(vertexCount) }; for (var i = 0; i < config.swarmCount; i++) { // Assign random starting values for each boid var boid = new Boid { position = Random.insideUnitSphere * config.spawnSphere, rotation = Random.rotation, velocity = Random.onUnitSphere * config.maxSpeed }; boids.Add(boid); draft.Add(config.template); } mesh = draft.ToMesh(); mesh.MarkDynamic(); // Set bounds manually for correct culling mesh.bounds = new Bounds(Vector3.zero, Vector3.one * config.worldSphere * 2); return(mesh); }
/// <summary> /// Run simulation /// </summary> public IEnumerator CalculateVelocities() { int simulationStep = 0; for (int currentIndex = 0; currentIndex < boids.Count; currentIndex++) { // Optimization trick: in each frame we simulate only small percent of all boids simulationStep++; if (simulationStep > maxSimulationSteps) { simulationStep = 0; yield return(null); } var boid = boids[currentIndex]; // Search for nearest neighbours neighbours.Clear(); for (int i = 0; i < boids.Count; i++) { Boid neighbour = boids[i]; Vector3 toNeighbour = neighbour.position - boid.position; if (toNeighbour.sqrMagnitude < interactionRadius) { neighbours.Add(neighbour); if (neighbours.Count == maxBoids) { break; } } } if (neighbours.Count < 2) { continue; } boid.velocity = Vector3.zero; boid.cohesion = Vector3.zero; boid.separation = Vector3.zero; boid.alignment = Vector3.zero; // Calculate boid parameters int separationCount = 0; for (int i = 0; i < neighbours.Count && i < maxBoids; i++) { Boid neighbour = neighbours[i]; boid.cohesion += neighbour.position; boid.alignment += neighbour.velocity; Vector3 toNeighbour = neighbour.position - boid.position; if (toNeighbour.sqrMagnitude > 0 && toNeighbour.sqrMagnitude < separationDistance * separationDistance) { boid.separation += toNeighbour / toNeighbour.sqrMagnitude; separationCount++; } } // Clamp all parameters to safe values boid.cohesion /= Mathf.Min(neighbours.Count, maxBoids); boid.cohesion = Vector3.ClampMagnitude(boid.cohesion - boid.position, maxSpeed); boid.cohesion *= cohesionCoefficient; if (separationCount > 0) { boid.separation /= separationCount; boid.separation = Vector3.ClampMagnitude(boid.separation, maxSpeed); boid.separation *= separationCoefficient; } boid.alignment /= Mathf.Min(neighbours.Count, maxBoids); boid.alignment = Vector3.ClampMagnitude(boid.alignment, maxSpeed); boid.alignment *= alignmentCoefficient; // Calculate resulting velocity Vector3 velocity = boid.cohesion + boid.separation + boid.alignment; boid.velocity = Vector3.ClampMagnitude(velocity, maxSpeed); if (boid.velocity == Vector3.zero) { // Prevent boids from stopping boid.velocity = Random.onUnitSphere * maxSpeed; } } }
private void SetBoidVertices(Boid boid, int index) { for (int i = 0; i < template.vertices.Count; i++) { draft.vertices[index*template.vertices.Count + i] = boid.rotation*template.vertices[i] + boid.position; } }
/// <summary> /// Generate new colors and positions for boids /// </summary> public Mesh Generate() { template = MeshDraft.Tetrahedron(0.3f); // Avoid vertex count overflow swarmCount = Mathf.Min(65000/template.vertices.Count, swarmCount); // Optimization trick: in each frame we simulate only small percent of all boids maxSimulationSteps = Mathf.RoundToInt(swarmCount*simulationPercent); int vertexCount = swarmCount*template.vertices.Count; // Paint template in random color template.colors.Clear(); var color = RandomE.colorHSV; // Assuming that we are dealing with tetrahedron, first vertex should be boid's "nose" template.colors.Add(color.Inverted()); for (int i = 1; i < template.vertices.Count; i++) { template.colors.Add(color); } draft = new MeshDraft { name = "Boids", vertices = new List<Vector3>(vertexCount), triangles = new List<int>(vertexCount), normals = new List<Vector3>(vertexCount), uv = new List<Vector2>(vertexCount), colors = new List<Color>(vertexCount) }; for (var i = 0; i < swarmCount; i++) { // Assign random starting values for each boid var boid = new Boid { position = Random.insideUnitSphere*spawnSphere, rotation = Random.rotation, velocity = Random.onUnitSphere*maxSpeed }; boids.Add(boid); draft.Add(template); } mesh = draft.ToMesh(); mesh.MarkDynamic(); return mesh; }
void AvoidTerrain(Boid boid) { if (transform == null) { return; } float s = 0.75f; int hits = 0; bool directHit = false; for (float x = -s; x <= s; x = x + s) { for (float y = -s; y <= s; y = y + s) { for (float z = -s; z <= s; z = z + s) { Vector3 p = new Vector3(boid.position.x + x, boid.position.y + y, boid.position.z + z); Vector3 pos = p + boid.velocity * Time.deltaTime * 2f; World3 worldPos = new World3(transform.TransformPoint(pos)); ushort block = World.GetBlock(worldPos); if ((block != Block.Air && block != Block.Null && worldPos.y < 16) || worldPos.y < -48) { hits++; if (x == 0 && y == 0 && z == 0) { directHit = true; } } } } } for (float x = -s; x <= s; x = x + s) { for (float y = -s; y <= s; y = y + s) { for (float z = -s; z <= s; z = z + s) { Vector3 p = new Vector3(boid.position.x + x, boid.position.y + y, boid.position.z + z); Vector3 pos = p + boid.velocity; World3 worldPos = new World3(transform.TransformPoint(pos)); ushort block = World.GetBlock(worldPos); if ((block != Block.Air && block != Block.Null && worldPos.y < 16) || worldPos.y < -48) { hits++; if (x == 0 && y == 0 && z == 0) { directHit = true; } } } } } if (hits == 0) { boid.rotation = Quaternion.FromToRotation(Vector3.up, boid.velocity); } else { boid.rotation = Quaternion.FromToRotation(Vector3.up, Vector3.RotateTowards(boid.velocity, -boid.velocity, Time.deltaTime * hits, 0f)); } if (directHit) { boid.velocity -= Vector3.up * 100f; boid.velocity = Vector3.ClampMagnitude(boid.velocity, maxSpeed); } if (hits > 0) { boid.velocity = Vector3.RotateTowards(boid.velocity, -boid.velocity, Time.deltaTime * hits * 5f, 0f); boid.velocity *= flutterBoost; boid.velocity += Vector3.up; boid.velocity = Vector3.ClampMagnitude(boid.velocity, maxSpeed); } }
/// <summary> /// Run simulation /// </summary> public IEnumerator Simulate() { simulationCount = 0; while (true) { for (int i = 0; i < boids.Count; i++) { // Optimization trick: in each frame we simulate only small percent of all boids simulationCount++; if (simulationCount > simulationUpdate) { simulationCount = 0; yield return null; } var boid = boids[i]; // Search for nearest neighbours neighbours.Clear(); for (int j = 0; j < boids.Count; j++) { var b = boids[j]; if ((b.position - boid.position).sqrMagnitude < interactionRadius) { neighbours.Add(b); if (neighbours.Count == maxBoids) { break; } } } if (neighbours.Count < 2) continue; boid.velocity = Vector3.zero; boid.cohesion = Vector3.zero; boid.separation = Vector3.zero; boid.alignment = Vector3.zero; // Calculate boid parameters separationCount = 0; for (var j = 0; j < neighbours.Count && j < maxBoids; j++) { other = neighbours[j]; boid.cohesion += other.position; boid.alignment += other.velocity; toOther = other.position - boid.position; if (toOther.sqrMagnitude > 0 && toOther.sqrMagnitude < separationDistance*separationDistance) { boid.separation += toOther/toOther.sqrMagnitude; separationCount++; } } // Clamp all parameters to safe values boid.cohesion /= Mathf.Min(neighbours.Count, maxBoids); boid.cohesion = Vector3.ClampMagnitude(boid.cohesion - boid.position, maxSpeed); boid.cohesion *= cohesionCoefficient; if (separationCount > 0) { boid.separation /= separationCount; boid.separation = Vector3.ClampMagnitude(boid.separation, maxSpeed); boid.separation *= separationCoefficient; } boid.alignment /= Mathf.Min(neighbours.Count, maxBoids); boid.alignment = Vector3.ClampMagnitude(boid.alignment, maxSpeed); boid.alignment *= alignmentCoefficient; // Calculate resulting velocity boid.velocity = Vector3.ClampMagnitude(boid.cohesion + boid.separation + boid.alignment, maxSpeed); if (boid.velocity == Vector3.zero) { // Prevent boids from stopping boid.velocity = Random.onUnitSphere*maxSpeed; } } } }
private IEnumerator Simulate() { simulationCount = 0; while (true) { for (int i = 0; i < swarmCount; i++) { simulationCount++; if (simulationCount > simulationUpdate) { simulationCount = 0; yield return(null); } var boid = boids[i]; neighbours.Clear(); for (int j = 0; j < swarmCount; j++) { var b = boids[j]; if ((b.position - boid.position).sqrMagnitude < cohesionRadius) { neighbours.Add(b); if (neighbours.Count == maxBoids) { break; } } } if (neighbours.Count < 2) { continue; } boid.velocity = Vector3.zero; boid.cohesion = Vector3.zero; boid.separation = Vector3.zero; separationCount = 0; alignment = Vector3.zero; for (var j = 0; j < neighbours.Count && j < maxBoids; j++) { other = neighbours[j]; boid.cohesion += other.position; alignment += other.velocity; toOther = other.position - boid.position; if (toOther.sqrMagnitude > 0 && toOther.sqrMagnitude < separationDistance * separationDistance) { boid.separation += toOther / toOther.sqrMagnitude; separationCount++; } } boid.cohesion /= Mathf.Min(neighbours.Count, maxBoids); boid.cohesion = Vector3.ClampMagnitude(boid.cohesion - boid.position, maxSpeed); boid.cohesion *= cohesionCoefficient; if (separationCount > 0) { boid.separation /= separationCount; boid.separation = Vector3.ClampMagnitude(boid.separation, maxSpeed); boid.separation *= separationCoefficient; } alignment /= Mathf.Min(neighbours.Count, maxBoids); alignment = Vector3.ClampMagnitude(alignment, maxSpeed); alignment *= alignmentCoefficient; boid.velocity = Vector3.ClampMagnitude(boid.cohesion + boid.separation + alignment, maxSpeed); if (boid.velocity == Vector3.zero) { boid.velocity = Random.onUnitSphere * maxSpeed; } } } }
private IEnumerator Simulate() { simulationCount = 0; while (true) { for (int i = 0; i < swarmCount; i++) { simulationCount++; if (simulationCount > simulationUpdate) { simulationCount = 0; yield return null; } var boid = boids[i]; neighbours.Clear(); for (int j = 0; j < swarmCount; j++) { var b = boids[j]; if ((b.position - boid.position).sqrMagnitude < cohesionRadius) { neighbours.Add(b); if (neighbours.Count == maxBoids) { break; } } } if (neighbours.Count < 2) continue; boid.velocity = Vector3.zero; boid.cohesion = Vector3.zero; boid.separation = Vector3.zero; separationCount = 0; alignment = Vector3.zero; for (var j = 0; j < neighbours.Count && j < maxBoids; j++) { other = neighbours[j]; boid.cohesion += other.position; alignment += other.velocity; toOther = other.position - boid.position; if (toOther.sqrMagnitude > 0 && toOther.sqrMagnitude < separationDistance*separationDistance) { boid.separation += toOther/toOther.sqrMagnitude; separationCount++; } } boid.cohesion /= Mathf.Min(neighbours.Count, maxBoids); boid.cohesion = Vector3.ClampMagnitude(boid.cohesion - boid.position, maxSpeed); boid.cohesion *= cohesionCoefficient; if (separationCount > 0) { boid.separation /= separationCount; boid.separation = Vector3.ClampMagnitude(boid.separation, maxSpeed); boid.separation *= separationCoefficient; } alignment /= Mathf.Min(neighbours.Count, maxBoids); alignment = Vector3.ClampMagnitude(alignment, maxSpeed); alignment *= alignmentCoefficient; boid.velocity = Vector3.ClampMagnitude(boid.cohesion + boid.separation + alignment, maxSpeed); if (boid.velocity == Vector3.zero) { boid.velocity = Random.onUnitSphere*maxSpeed; } } } }
/// <summary> /// Run simulation /// </summary> public IEnumerator Simulate() { simulationCount = 0; while (true) { for (int i = 0; i < boids.Count; i++) { // Optimization trick: in each frame we simulate only small percent of all boids simulationCount++; if (simulationCount > simulationUpdate) { simulationCount = 0; yield return(null); } var boid = boids[i]; // Search for nearest neighbours neighbours.Clear(); for (int j = 0; j < boids.Count; j++) { var b = boids[j]; if ((b.position - boid.position).sqrMagnitude < interactionRadius) { neighbours.Add(b); if (neighbours.Count == maxBoids) { break; } } } if (neighbours.Count < 2) { continue; } boid.velocity = Vector3.zero; boid.cohesion = Vector3.zero; boid.separation = Vector3.zero; boid.alignment = Vector3.zero; // Calculate boid parameters separationCount = 0; for (var j = 0; j < neighbours.Count && j < maxBoids; j++) { other = neighbours[j]; boid.cohesion += other.position; boid.alignment += other.velocity; toOther = other.position - boid.position; if (toOther.sqrMagnitude > 0 && toOther.sqrMagnitude < separationDistance * separationDistance) { boid.separation += toOther / toOther.sqrMagnitude; separationCount++; } } // Clamp all parameters to safe values boid.cohesion /= Mathf.Min(neighbours.Count, maxBoids); boid.cohesion = Vector3.ClampMagnitude(boid.cohesion - boid.position, maxSpeed); boid.cohesion *= cohesionCoefficient; if (separationCount > 0) { boid.separation /= separationCount; boid.separation = Vector3.ClampMagnitude(boid.separation, maxSpeed); boid.separation *= separationCoefficient; } boid.alignment /= Mathf.Min(neighbours.Count, maxBoids); boid.alignment = Vector3.ClampMagnitude(boid.alignment, maxSpeed); boid.alignment *= alignmentCoefficient; // Calculate resulting velocity boid.velocity = Vector3.ClampMagnitude(boid.cohesion + boid.separation + boid.alignment, maxSpeed); if (boid.velocity == Vector3.zero) { // Prevent boids from stopping boid.velocity = Random.onUnitSphere * maxSpeed; } } } }