usingUnityEngine;usingSystem.Collections.Generic;publicclassSwarmSystem:MonoBehaviour{publicGameObjectswarmPrefab;publicGameObjectanchorPrefab;publicintsize=1;publicVector3collapseCenter;publicSkinnedMeshRenderermeshRender;publicboolshowGuides=false;publicboolshowMesh=false;privateList<Transform>units=newList<Transform>();privateList<Transform>anchors=newList<Transform>();privateList<Vector3>noiseDirecs=newList<Vector3>();privateList<Vector3>boneStarts=newList<Vector3>();privateList<Vector3>boneEnds=newList<Vector3>();privateList<Transform>boneTransforms=newList<Transform>();privateList<Transform>boneTips=newList<Transform>();privateList<int>boneCenterStarts=newList<int>();privateList<float>boneCenterDists=newList<float>();privateList<int>unitTris=newList<int>();privateList<List<int>>triGraph;publicfloatNoise{get;set;}publicfloatCollapse{get;set;}privateMeshgetCurrentMesh(){Meshmesh=newMesh();meshRender.BakeMesh(mesh);returnmesh;}privatebooltriangleIntersection(Vector3p1,Vector3p2,Vector3p3,Rayray,outVector3intersect){Vector3e1,e2;Vector3p,q,t;floatdet,invDet,u,v;floatEpsilon=0.001f;intersect=Vector3.zero;e1=p2-p1;e2=p3-p1;//calculating determinant p=Vector3.Cross(ray.direction,e2);det=Vector3.Dot(e1,p);//if determinant is near zero, ray lies in plane of triangle otherwise notif(det>-Epsilon&&det<Epsilon)returnfalse;invDet=1.0f/det;t=ray.origin-p1;u=Vector3.Dot(t,p)*invDet;//Check for ray hitif(u<0||u>1)returnfalse;//Prepare to test v parameterq=Vector3.Cross(t,e1);v=Vector3.Dot(ray.direction,q)*invDet;//Check for ray hitif(v<0||u+v>1)returnfalse;//ray does intersectif((Vector3.Dot(e2,q)*invDet)>Epsilon){intersect=ray.origin+ray.direction*(Vector3.Dot(e2,q)*invDet);returntrue;}// No hit at allreturnfalse;}privatebooltriangleIndexIntersection(intidx,Vector3[]vertices,int[]triangles,Rayray,outVector3intersect){returntriangleIntersection(meshRender.transform.TransformPoint(vertices[triangles[idx*3]]),meshRender.transform.TransformPoint(vertices[triangles[idx*3+1]]),meshRender.transform.TransformPoint(vertices[triangles[idx*3+2]]),ray,outintersect);}privateVector3meshIntersection(intunitIdx,Vector3[]vertices,int[]triangles,Vector3boneCenter){Vector3intersect;Rayray=newRay(boneCenter,anchors[unitIdx].position-boneCenter);if(unitTris[unitIdx]!=-1){if(triangleIndexIntersection(unitTris[unitIdx],vertices,triangles,ray,outintersect)){returnintersect;}foreach(inttriintriGraph[unitTris[unitIdx]]){if(triangleIndexIntersection(tri,vertices,triangles,ray,outintersect)){unitTris[unitIdx]=tri;returnintersect;}}}for(inti=0;i<triangles.Length;i+=3){if(triangleIndexIntersection(i/3,vertices,triangles,ray,outintersect)){unitTris[unitIdx]=i/3;returnintersect;}}returnanchors[unitIdx].position;}privatevoidcalculateTriGraph(){Meshmesh=meshRender.sharedMesh;Vector3[]vertices=mesh.vertices;floatepsilon=0.001f;triGraph=newList<List<int>>();Dictionary<int,int>vertexAlias=newDictionary<int,int>();List<int>vertexByMag=newList<int>();for(inti=0;i<vertices.Length;i++)vertexByMag.Add(i);vertexByMag.Sort((x,y)=>vertices[x].magnitude.CompareTo(vertices[y].magnitude));for(inti=0;i<vertices.Length;i++){intminEq=i;intj=i;while(j>=0&&vertices[i].magnitude-vertices[j].magnitude<epsilon){if((vertices[i]-vertices[j]).magnitude<epsilon)minEq=j;j--;}vertexAlias.Add(i,minEq);}List<List<int>>vertexTouch=newList<List<int>>();for(inti=0;i<vertices.Length;i++)vertexTouch.Add(newList<int>());for(inti=0;i<mesh.triangles.Length;i+=3){vertexTouch[vertexAlias[mesh.triangles[i]]].Add(i/3);vertexTouch[vertexAlias[mesh.triangles[i+1]]].Add(i/3);vertexTouch[vertexAlias[mesh.triangles[i+2]]].Add(i/3);}for(inti=0;i<mesh.triangles.Length;i+=3){List<int>tris=newList<int>();for(intk=0;k<3;k++){foreach(inttriinvertexTouch[vertexAlias[mesh.triangles[i+k]]]){if(tri!=i/3&&!tris.Contains(tri))tris.Add(tri);}}triGraph.Add(tris);}}privatevoidmapBones(){boneTips.Clear();foreach(TransformbinmeshRender.bones){boneTransforms.Add(b.parent);if(b.GetChild(0).childCount==0){boneTransforms.Add(b);boneTips.Add(b);}}}privatevoidrefreshMesh(){boneStarts.Clear();boneEnds.Clear();foreach(TransformbinmeshRender.bones){boneStarts.Add(b.parent.position);boneEnds.Add(b.position);if(boneTips.Contains(b)){boneStarts.Add(b.position);boneEnds.Add(b.GetChild(0).position);}}}privatevoidcalculateBoneCenters(){boneCenterStarts.Clear();boneCenterDists.Clear();foreach(Transformunitinunits){floatminDist=float.PositiveInfinity;Vector3boneCenter=transform.position;intboneIdx=0;for(inti=0;i<boneStarts.Count;i++){Vector3start=boneStarts[i];Vector3end=boneEnds[i];Vector3c=start+Vector3.Project(unit.position-start,end-start);floatdist=(c-unit.position).magnitude;Vector3direc=end-start;if(dist<minDist&&(c-start).magnitude<direc.magnitude&&(c-end).magnitude<direc.magnitude){minDist=dist;boneCenter=c;boneIdx=i;}}boneCenterStarts.Add(boneIdx);boneCenterDists.Add((boneCenter-boneStarts[boneIdx]).magnitude);unit.parent=boneTransforms[boneIdx];}}privateVector3findBoneCenter(intunitIdx,outVector3boneDirec){Vector3start=boneStarts[boneCenterStarts[unitIdx]];boneDirec=boneEnds[boneCenterStarts[unitIdx]]-start;returnstart+(boneCenterDists[unitIdx]*boneDirec);}privatevoidrandomMeshSpawn(){Meshmesh=getCurrentMesh();List<float>accTriSizes=newList<float>();for(inti=0;i<mesh.triangles.Length;i+=3){floatprev=0;if(i>0)prev=accTriSizes[(i/3)-1];floatarea=Vector3.Cross(mesh.vertices[mesh.triangles[i+1]]-mesh.vertices[mesh.triangles[i]],mesh.vertices[mesh.triangles[i+2]]-mesh.vertices[mesh.triangles[i]]).magnitude/2;accTriSizes.Add(prev+area);}floatoutOf=accTriSizes[accTriSizes.Count-1];for(inti=0;i<size;i++){floatv=Random.value*outOf;intk=0;while(accTriSizes[k+1]<v)k++;floatr1=Mathf.Sqrt(Random.value);floatr2=Random.value;Vector3p=(1-r1)*mesh.vertices[mesh.triangles[k*3]]+(r1*(1-r2))*mesh.vertices[mesh.triangles[k*3+1]]+(r1*r2)*mesh.vertices[mesh.triangles[k*3+2]];GameObjectanchor=(GameObject)Instantiate(anchorPrefab,meshRender.transform.TransformPoint(p),Quaternion.identity);GameObjectspawn=(GameObject)Instantiate(swarmPrefab,meshRender.transform.TransformPoint(p),Quaternion.identity);spawn.transform.parent=transform;anchor.transform.parent=transform;units.Add(spawn.transform);anchors.Add(anchor.transform);noiseDirecs.Add(Random.onUnitSphere);unitTris.Add(k);}}publicvoidactivate(boolturnOn){foreach(Transformuinunits)u.gameObject.SetActive(turnOn);}publicTransformrootBone(){returnmeshRender.bones[0];}publicvoidsetNoise(floatnoise){}voidStart(){mapBones();refreshMesh();calculateTriGraph();randomMeshSpawn();calculateBoneCenters();}voidFixedUpdate(){refreshMesh();Meshmesh=getCurrentMesh();Transformroot=meshRender.bones[0].parent;Vector3[]vertices=mesh.vertices;int[]triangles=mesh.triangles;for(intu=0;u<units.Count;u++){Transformunit=units[u];Vector3boneDirec;Vector3boneCenter=findBoneCenter(u,outboneDirec);anchors[u].position=boneCenter+(Quaternion.AngleAxis(3f,boneDirec)*(anchors[u].position-boneCenter));anchors[u].position=meshIntersection(u,vertices,triangles,boneCenter);unit.position=anchors[u].position+Noise*noiseDirecs[u];Vector3coll=transform.position+collapseCenter;Vector3collapsedPos=Vector3.Project(unit.position-coll,Vector3.right)*0.3f+coll;unit.position=(1-Collapse)*unit.position+Collapse*collapsedPos;}}voidOnDrawGizmos(){if(showGuides){mapBones();refreshMesh();Gizmos.color=Color.cyan;for(inti=0;i<boneStarts.Count;i++){Gizmos.DrawLine(boneStarts[i],boneEnds[i]);}Gizmos.color=Color.red;Meshmesh=getCurrentMesh();if(showMesh)for(inti=0;i<mesh.triangles.Length;i+=3){Gizmos.DrawSphere(meshRender.transform.TransformPoint(mesh.vertices[mesh.triangles[i]]),0.1f);Gizmos.DrawSphere(meshRender.transform.TransformPoint(mesh.vertices[mesh.triangles[i+1]]),0.1f);Gizmos.DrawSphere(meshRender.transform.TransformPoint(mesh.vertices[mesh.triangles[i+2]]),0.1f);}inttri=0;Gizmos.color=Color.cyan;if(triGraph!=null){//Debug.Log(triGraph[tri].Count);foreach(inttintriGraph[tri]){Gizmos.DrawLine(meshRender.transform.TransformPoint(mesh.vertices[mesh.triangles[t*3]]),meshRender.transform.TransformPoint(mesh.vertices[mesh.triangles[t*3+1]]));Gizmos.DrawLine(meshRender.transform.TransformPoint(mesh.vertices[mesh.triangles[t*3+1]]),meshRender.transform.TransformPoint(mesh.vertices[mesh.triangles[t*3+2]]));Gizmos.DrawLine(meshRender.transform.TransformPoint(mesh.vertices[mesh.triangles[t*3+2]]),meshRender.transform.TransformPoint(mesh.vertices[mesh.triangles[t*3]]));}}Gizmos.color=Color.magenta;Gizmos.DrawLine(meshRender.transform.TransformPoint(mesh.vertices[mesh.triangles[tri*3]]),meshRender.transform.TransformPoint(mesh.vertices[mesh.triangles[tri*3+1]]));Gizmos.DrawLine(meshRender.transform.TransformPoint(mesh.vertices[mesh.triangles[tri*3+1]]),meshRender.transform.TransformPoint(mesh.vertices[mesh.triangles[tri*3+2]]));Gizmos.DrawLine(meshRender.transform.TransformPoint(mesh.vertices[mesh.triangles[tri*3+2]]),meshRender.transform.TransformPoint(mesh.vertices[mesh.triangles[tri*3]]));Gizmos.color=Color.green;Gizmos.DrawLine(transform.position+collapseCenter-Vector3.right*5,transform.position+collapseCenter+Vector3.right*5);}}}