Hallo,
hier folgt wie versprochen der zweite Teil.
Der Funktionsumfang ist diesmal einiges grösser, da es auch die 'endgültige' Library Version darstellen soll. Wie sich herausstellt scheint die Verletimplementation doch schneller zu sein, da man weniger rechnen muss für die Kollisionsantwort, dennoch bleibt der Vorteil der erhöhten Stabilität.
Einschränkungen der Library:
Die Funktionen unterstützen nur eine maximale Anzahl von 32 Vertice pro Körper, das sollte genügen, da man (momentan) noch keine konkaven Körper verwenden kann.
Nach eigenen kleineren Leistungstest kommt man auf etwa 100 Objekte, wenn sie nahe zusammen sind. Es wird eine Abstandskontrolle ausgeführt die nur Kollisionen testet, wenn sich die Körper auch treffen können.
Wie auch beim vorherigen Code sollte man den GarbageCollector mitlaufen lassen, weil sich sonst Fehler einschleichen können, durch Speicherüberflutung.
Einsatzmöglichkeiten der Library:
Nachdem man seinen Körper initialisiert hat, muss man ihn dem System bekannt geben ( addBody() ), dort wird der Körper mit Hilfe des etwas modifizierten Subtracting Ears Method von Noobody direkt in Meshes verwandelt. Nach dem Initialisieren der Kamera ( initGameCam() ) werden diese dann auch dargestellt, hierbei wird es so gehandhabt, dass die Kamera sich wie das 2D Koordinatensystem verhält (Origin in der Mitte des Bildschirms!)
Natürlich hat der Nutzer die Möglichkeit, sich auf die 2D Ebene zu beschränken, indem er die erstellten Meshes löscht ( DestroyMeshes() ) und die 2D Rendermethode ( GameRender() ) benutzt.
Die Geschwindigkeit der Simulation lässt sich mit dem Parameter dt# in der Funktion GameUpdate(dt#) steuern, getestet wurde stets mit 0.125, höhere Werte führen zu schnelleren Bewegungen, aber vorsicht, es kann vorkommen, dass die Simulation zu schnell wird und aus dem Ruder läuft!
Selbstverständlich können auch beide Rendermethoden parallel benutzt werden, man beachte allerdings den erhöhten Aufwand.bj
Der Code ist eigentlich dazu gedacht, dass man gewisse physikalische Objekte in sein Spiel integrieren kann. Zur strikten 2D Benutzung sollte er wohl noch etwas umgeschrieben werden (hauptsächlich die Zeile zur Meshgenerierung auskommentieren), da ich aber von 3D Meshbenutzung ausgehe, setze ich diese Version hierin.
Hier der Code der Library, es hat noch einige Kommentare drin, die getrost ignoriert werden können. Viele dienen zur Gliederung des Codes, einige zur Beschreibung der Funktionen und andere sind unseriöse Bemerkungen meinerseits.
BlitzBasic: [AUSKLAPPEN] [EINKLAPPEN] Global NaN#=1./Floor(0.1)
Function Clampf#(x#,min#,max#) If x<min Return min ElseIf x>max Return max EndIf Return x End Function
Function Wrapf#(x#,min#,max#) If x<min Return x-min+max ElseIf x>max Return x-max+min EndIf Return x End Function
Const RtD#=180./Pi Const DtR#=Pi/180. Function RadiansToDegrees(rad#) Return rad*RtD End Function
Function DegreesToRadians(deg#) Return deg*DtR End Function
Type Vector Field X# Field Y# Field used End Type
Function cVector.Vector(ix#,iy#) v.Vector=New Vector v\x=ix v\y=iy Return v End Function
Function opVector_Div(v.Vector,Scalar#) v\X=v\X/Scalar v\Y=v\Y/Scalar End Function
Function opVector_Mult(v.Vector,Scalar#) v\X=v\X*Scalar v\Y=v\Y*Scalar End Function
Function opVector_Add(v.Vector,other.Vector) v\X=v\X+other\X v\Y=v\Y+other\Y End Function
Function opVector_Sub(v.Vector,other.Vector) v\X=v\X-other\X v\Y=v\Y-other\Y End Function
Function opVector_CP#(v.Vector,other.Vector) Return v\X*other\Y-v\Y*other\X End Function
Function opVector_DP#(v.Vector,other.Vector) Return v\X*other\X+v\Y*other\Y End Function
Function opVectorN_Mult.Vector(v.Vector,Scalar#) vn.vector=New Vector vn\x=v\X*Scalar vn\y=v\Y*Scalar Return vn End Function
Function opVectorN_Div.Vector(v.Vector,Scalar#) vn.vector=New Vector vn\X=v\X/Scalar vn\Y=v\Y/Scalar Return vn End Function
Function opVectorN_Add.Vector(v.Vector,other.Vector) vn.vector=New Vector vn\x=v\X+other\X vn\y=v\Y+other\Y Return vn End Function
Function opVectorN_Sub.Vector(v.Vector,other.Vector) vn.vector=New Vector vn\x=v\X-other\X vn\y=v\Y-other\Y Return vn End Function
Function Normalise#(v.Vector) fLength#=Sqr(v\X*v\X+v\Y*v\Y) If fLength<=0 Return 0 EndIf v\X=v\X/fLength v\Y=v\Y/fLength Return fLength End Function
Function Direction.Vector(v.Vector) temp.vector=New Vector temp\x=v\X temp\y=v\Y Normalise(temp) Return temp End Function
Function RotateV(v.Vector,fAngle#) Local tx#=v\X v\X=v\X*Cos(fAngle)-v\Y*Sin(fAngle) v\Y=tx*Sin(fAngle)+v\Y*Cos(fAngle) End Function
Function RotateVC(v.Vector,xCentre.Vector,fAngle#) Local D.Vector=opVectorN_Sub(v,xCentre) RotateV(D,fAngle) v\X=xCentre\X+D\X v\Y=xCentre\Y+D\Y End Function
Function ClampV(v.Vector,min.Vector,max.Vector) If v\X<min\X v\X=min\X ElseIf v\X>max\X v\X=max\X EndIf If v\Y<min\Y v\Y=min\Y ElseIf v\Y>max\Y v\Y=max\Y EndIf End Function
Function Randomise(v.Vector,xMin.Vector,xMax.Vector) v\X=Rnd(xMin\X,xMax\X) v\Y=Rnd(xMin\Y,xMax\Y) End Function
Function Length#(v.Vector) Return Sqr(v\X*v\X+v\Y*v\Y) End Function
Type Matrix Field e11# Field e12# Field e21# Field e22# End Type
Function cMatrix.Matrix(e11#,e12#,e21#,e22#) m.matrix=New Matrix m\e11=e11 : m\e12=e12 m\e21=e21 : m\e22=e22 Return m End Function
Function cMatrix_Identity.Matrix() Return cMatrix(1,0,0,1) End Function
Function cMatrix_Rot.Matrix(fAngle#) m.matrix=New Matrix Local c#=Cos(fAngle) Local s#=Sin(fAngle) m\e11=c : m\e12=s m\e21=-s : m\e22=c Return m End Function
Function MatrixEntry#(m.Matrix,i,j) If i=1 If j=1 Return m\e11 ElseIf j=2 Return m\e12 EndIf ElseIf i=2 If j=1 Return m\e21 ElseIf j=2 Return m\e22 EndIf EndIf Return 0 End Function
Function opMatrixN_MMult.Matrix(m.Matrix,other.Matrix) mn.matrix=New Matrix mn\e11=m\e11*other\e11+m\e12*other\e21 mn\e21=m\e21*other\e11+m\e22*other\e21 mn\e12=m\e11*other\e12+m\e12*other\e22 mn\e22=m\e21*other\e12+m\e22*other\e22 Return mn End Function
Function opMatrixN_MPow.Matrix(m.Matrix,other.Matrix) mn.matrix=New Matrix mn\e11=m\e11*other\e11+m\e12*other\e12 mn\e21=m\e21*other\e11+m\e22*other\e12 mn\e12=m\e11*other\e21+m\e12*other\e22 mn\e22=m\e21*other\e21+m\e22*other\e22 Return mn End Function
Function opMatrixN_Mult(m.Matrix,Scalar#) mn.matrix=New Matrix mn\e11=m\e11*Scalar mn\e12=m\e12*Scalar mn\e21=m\e21*Scalar mn\e22=m\e22*Scalar End Function
Function opVectorNs_Mult.Vector(v.Vector,m.Matrix) vn.Vector=New Vector vn\x=v\X*m\e11+v\Y*m\e12 vn\y=v\X*m\e21+v\Y*m\e22 Return vn End Function
Function opVectorNs_Pow.Vector(v.Vector,m.Matrix) vn.Vector=New Vector vn\x=v\X*m\e11+v\Y*m\e21 vn\y=v\X*m\e12+v\Y*m\e22 Return vn End Function
Function opVectors_Mult(v.Vector,m.Matrix) Local tx#=v\X Local ty#=v\Y v\X=tx*m\e11+ty*m\e12 v\Y=tx*m\e21+ty*m\e22 End Function
Function opVectors_Pow(v.Vector,m.Matrix) Local tx#=v\X Local ty#=v\Y v\X=tx*m\e11+ty*m\e21 v\Y=tx*m\e12+ty*m\e22 End Function
Function BuildBox(axVertices.Vector[32],width#,height#) axVertices[0]=cVector(-width/2,-height/2) axVertices[1]=cVector(width/2,-height/2) axVertices[2]=cVector(width/2,height/2) axVertices[3]=cVector(-width/2,height/2) Return End Function
Function BuildBlob(axVertices.Vector[32],iVerticeCount,radiusx#,radiusy#) If iVerticeCount=1 axVertices[0]=cVector(0,0) Return EndIf Local a_#=RadiansToDegrees(Pi/iVerticeCount) Local da_#=RadiansToDegrees(2*Pi/iVerticeCount) For i=0 To iVerticeCount-1 a_=a_+da_ axVertices[i]=cVector(Cos(a_)*radiusx,Sin(a_)*radiusy) Next Return End Function
Function RenderP(xOffset.Vector,xOrient.Matrix,axVertices.Vector[32],iVerticeCount) j=iVerticeCount-1 For i=0 To iVerticeCount-1 P1.Vector=opVectorN_Add(xOffset,opVectorNs_Mult(axVertices[i],xOrient)) P2.Vector=opVectorN_Add(xOffset,opVectorNs_Mult(axVertices[j],xOrient)) Line P1\x,P1\y,P2\x,P2\y j=i Next End Function
Global Coll_t# Function CollideP(A.Vector[32],Anum,PA.Vector,VA.Vector,OA.Matrix,B.Vector[32],Bnum,PB.Vector,VB.Vector,OB.Matrix,N.Vector,t#) Coll_t=t xOrient.Matrix=opMatrixN_MPow(OA,OB) xOffset.Vector=opVectorNs_Pow(opVectorN_Sub(PA,PB),OB) xVel.Vector=opVectorNs_Pow(opVectorN_Sub(VA,VB),OB) Local xAxis.Vector[64] Local taxis#[64] iNumAxes=0 If fVel2>0.000001 If Not IntervalIntersect(A,Anum,B,Bnum,xAxis[iNumAxes],xOffset,xVel,xOrient,taxis[iNumAxes],Coll_t) Return False EndIf taxis[iNumAxes]=IntInt_taxis iNumAxes=iNumAxes+1 EndIf j=Anum-1 For i=0 To Anum-1 E0.Vector=A[j] E1.Vector=A[i] E.Vector=opVectorN_Sub(E1,E0) xAxis[iNumAxes]=opVectorNs_Mult(cVector(-E\y,E\x),xOrient) If Not IntervalIntersect(A,Anum,B,Bnum,xAxis[iNumAxes],xOffset,xVel,xOrient,taxis[iNumAxes],Coll_t) Return False EndIf taxis[iNumAxes]=IntInt_taxis iNumAxes=iNumAxes+1 j=i Next j=Bnum-1 For i=0 To Bnum-1 E0.Vector=B[j] E1.Vector=B[i] E.Vector=opVectorN_Sub(E1,E0) xAxis[iNumAxes]=cVector(-E\y,E\x) If Not IntervalIntersect(A,Anum,B,Bnum,xAxis[iNumAxes],xOffset,xVel,xOrient,taxis[iNumAxes],Coll_t) Return False EndIf taxis[iNumAxes]=IntInt_taxis iNumAxes=iNumAxes+1 j=i Next If Bnum=2 E.Vector=opVectorN_Sub(B[1],B[0]) xAxis[iNumAxes]=E If Not IntervalIntersect(A,Anum,B,Bnum,xAxis[iNumAxes],xOffset,xVel,xOrient,taxis[iNumAxes],Coll_t) Return False EndIf taxis[iNumAxes]=IntInt_taxis iNumAxes=iNumAxes+1 EndIf If Anum=2 E.Vector=opVectorN_Sub(A[1],A[0]) xAxis[iNumAxes]=opVectorNs_Mult(E,xOrient) If Not IntervalIntersect(A,Anum,B,Bnum,xAxis[iNumAxes],xOffset,xVel,xOrient,taxis[iNumAxes],Coll_t) Return False EndIf taxis[iNumAxes]=IntInt_taxis iNumAxes=iNumAxes+1 EndIf If Not FindMTD(xAxis,taxis,iNumAxes,N) Return False EndIf Coll_t=fMTD_t If opVector_DP(N,xOffset)<0 opVector_Mult(N,-1) EndIf opVectors_Mult(N,OB) Return True End Function
Global GetI_min#, GetI_max# Function GetInterval(axVertices.Vector[32],iVerticeCount,xAxis.Vector) GetI_min=opVector_DP(axVertices[0],xAxis) GetI_max=GetI_min For i=1 To iVerticeCount-1 d#=opVector_DP(axVertices[i],xAxis) If d<GetI_min GetI_min=d ElseIf d>GetI_max GetI_max=d EndIf Next End Function
Global IntInt_taxis# Function IntervalIntersect(A.Vector[32],Anum,B.Vector[32],Bnum,xAxis.Vector,xOffset.Vector,xVel.Vector,xOrient.Matrix,taxis#,tmax#) IntInt_taxis=taxis GetInterval(A,Anum,opVectorNs_Pow(xAxis,xOrient)) min0#=GetI_min max0#=GetI_max GetInterval(B,Bnum,xAxis) min1#=GetI_min max1#=GetI_max h#=opVector_DP(xOffset,xAxis) min0=min0+h max0=max0+h d0#=min0-max1 d1#=min1-max0 If d0>0 Or d1>0 v#=opVector_DP(xVel,xAxis) t0#=-d0/v t1#=d1/v If t0>t1 temp#=t0 t0=t1 t1=temp EndIf If t0>0 IntInt_taxis=t0 Else IntInt_taxis=t1 EndIf If IntInt_taxis<0 Or IntInt_taxis>tmax Return False EndIf Return True Else If d0>d1 IntInt_taxis=d0 Else IntInt_taxis=d1 EndIf Return True EndIf End Function
Global fMTD_t# Function FindMTD(xAxis.Vector[64],taxis#[64],iNumAxes,N.Vector) mini=-1 t#=0 : fMTD_t=0 N\X=0 N\Y=0 For i=0 To iNumAxes-1 If taxis[i]>0 If taxis[i]>t mini=i t=taxis[i] N\X=xAxis[i]\X : N\Y=xAxis[i]\Y Normalise(N) EndIf EndIf Next If mini<>-1 fMTD_t=t Return True EndIf mini=-1 For i=0 To iNumAxes-1 n_#=Normalise(xAxis[i]) taxis[i]=taxis[i]/n_ If taxis[i]>t Or mini=-1 mini=i t=taxis[i] N\X=xAxis[i]\X : N\Y=xAxis[i]\Y EndIf Next fMTD_t=t If mini<>-1 Return True Else Return False EndIf End Function
Global PPOS_t# Function ProjectPointOnSegment(V.Vector,A.Vector,B.Vector,W.Vector) AV.Vector=opVectorN_Sub(V,A) AB.Vector=opVectorN_Sub(B,A) t#=opVector_DP(AV,AB)/opVector_DP(AB,AB) If t<0 t=0 ElseIf t>1 t=1 EndIf PPOS_t=t W\X=A\X+t*AB\X W\Y=A\Y+t*AB\y Return True End Function
Function Transform.Vector(Vertex.Vector,P.Vector,V.Vector,xOrient.Matrix,t_#) T.Vector=opVectorN_Add(P,opVectorNs_Mult(Vertex,xOrient)) If t_>0 opVector_Add(T,opVectorN_Mult(V,t_)) EndIf Return T End Function
Function FindSupportPoints(N.Vector,t#,A.Vector[32],Anum,PA.Vector,VA.Vector,OA.Matrix,S.Vector[4]) Norm.Vector=opVectorNs_Pow(N,OA) Local d#[32] dmin#=opVector_DP(A[0],Norm) d[0]=dmin For i=1 To Anum-1 d[i]=opVector_DP(A[i],Norm) If d[i]<dmin dmin=d[i] EndIf Next Snum=0 threshold#=0.0001 Local s_#[2] sign_=False Perp.Vector=cVector(-Norm\y,Norm\x) For i=0 To Anum-1 If d[i]<dmin+threshold Contact.Vector=Transform(A[i],PA,VA,OA,t) c#=opVector_DP(Contact,Perp) If Snum<2 s_[Snum]=c S[Snum]=Contact Snum=Snum+1 If Snum>1 If s_[1]>s_[0] sign_=True Else sign_=False EndIf EndIf Else If sign_ min#=s_[0] max#=s_[1] xMin.Vector=S[0] xMax.Vector=S[1] Else min#=s_[1] max#=s_[0] xMin.Vector=S[1] xMax.Vector=S[0] EndIf If c<min min=c xMin=Contact ElseIf c>max max=c xMax=Contact EndIf EndIf EndIf Next Return Snum End Function
Global CSPTC_Cnum Function ConvertSupportPointsToContacts(N.Vector,S0.Vector[4],S0num,S1.Vector[4],S1num,C0.Vector[4],C1.Vector[4]) CSPTC_Cnum=0 : Cnum=0 If S0num=0 Or S1num=0 Return False EndIf If S0num=1 And S1num=1 C0[Cnum]=S0[0] C1[Cnum]=S1[0] Cnum=Cnum+1 CSPTC_Cnum=Cnum Return True EndIf xPerp.Vector=cVector(-N\Y,N\X) min0#=opVector_DP(S0[0],xPerp) max0#=min0 min1#=opVector_DP(S1[0],xPerp) max1#=min1 If S0num=2 max0=opVector_DP(S0[1],xPerp) If max0<min0 temp#=min0 min0=max0 max0=temp T.Vector=S0[0] S0[0]=S0[1] S0[1]=T EndIf EndIf If S1num=2 max1=opVector_DP(S1[1],xPerp) If max1<min1 temp#=min1 min1=max1 max1=temp T.Vector=S1[0] S1[0]=S1[1] S1[0]=T EndIf EndIf If min0>max1 Or min1>max0 CSPTC_Cnum=Cnum Return False EndIf If min0>min1 Pseg.Vector=cVector(0,0) If ProjectPointOnSegment(S0[0],S1[0],S1[1],Pseg) C0[Cnum]=S0[0] C1[Cnum]=Pseg Cnum=Cnum+1 EndIf Else Pseg.Vector=cVector(0,0) If ProjectPointOnSegment(S1[0],S0[0],S0[1],Pseg) C0[Cnum]=Pseg C1[Cnum]=S1[0] Cnum=Cnum+1 EndIf EndIf If Abs(max0-min0)>0.0001 And Abs(max1-min1)>0.0001 If max0<max1 Pseg.Vector=cVector(0,0) If ProjectPointOnSegment(S0[1],S1[0],S1[1],Pseg) C0[Cnum]=S0[1] C1[Cnum]=Pseg Cnum=Cnum+1 EndIf Else Pseg.Vector=cVector(0,0) If ProjectPointOnSegment(S1[1],S0[0],S0[1],Pseg) C0[Cnum]=Pseg S1[Cnum]=S1[1] Cnum=Cnum+1 EndIf EndIf EndIf CSPTC_Cnum=Cnum Return True End Function
Global FC_Cnum Function FindContacts(A.Vector[32],Anum,PA.Vector,VA.Vector,OA.Matrix,B.Vector[32],Bnum,PB.Vector,VB.Vector,OB.Matrix,N.Vector,t#,CA.Vector[4],CB.Vector[4],Cnum) FC_Cnum=Cnum Local S0.Vector[4] Local S1.Vector[4] S0num=FindSupportPoints(N,t,A,Anum,PA,VA,OA,S0) S1num=FindSupportPoints(opVectorN_Mult(N,-1),t,B,Bnum,PB,VB,OB,S1) If Not ConvertSupportPointsToContacts(N,S0,S0num,S1,S1num,CA,CB) FC_Cnum=CSPTC_Cnum Return False EndIf FC_Cnum=CSPTC_Cnum Return True End Function
Function CalculateMass#(A.Vector[32],Anum,density#) If Anum<2 Return 5*density EndIf mass#=0 j=Anum-1 For i=0 To Anum-1 P0.Vector=A[j] P1.Vector=A[i] mass=mass+Abs(opVector_CP(P0,P1)) j=i Next If Anum<=2 mass=10 EndIf mass=mass*density*0.5 Return mass End Function
Function CalculateInertia#(A.Vector[32],Anum,mass#) If Anum=1 Return 0 EndIf denom#=0 numer#=0 inertia#=0 j=Anum-1 For i=0 To Anum-1 P0.Vector=A[j] P1.Vector=A[i] a_#=Abs(opVector_CP(P0,P1)) b_#=opVector_DP(P1,P1)+opVector_DP(P1,P0)+opVector_DP(P0,P0) denom=denom+(a_*b_) numer=numer+a_ j=i Next inertia#=(mass/6.)*(denom/numer) Return inertia End Function
Type Body Field axVertices.Vector[32] Field iVerticeCount Field xVelocity.Vector Field xPosition.Vector Field fDensity# Field fMass# Field fInvMass# Field fInertia# Field fInvInertia# Field fOrientation# Field fAngVelocity# Field xOrientation.Matrix Field xNetForce.Vector Field fNetTorque# Field fMaxRadius# Field fMaxRadiusSqare# Field vx# Field vy# Field px# Field py# Field vertx#[32] Field verty#[32] End Type
Function cBody_Empty.Body() b.Body=New Body Return b End Function
Function cBody_Box.Body(xPosition.Vector,fDensity#,width#,height#) b.Body=New Body iVerticeCount=4 Local axVertices.Vector[32] BuildBox(axVertices,width,height) InitialiseB(b,xPosition,fDensity,axVertices,iVerticeCount) Return b End Function
Function cBody_Blob.Body(xPosition.Vector,fDensity#,radiusx#,radiusy#) b.Body=New Body iVerticeCount=Rand(3,10) Local axVertices.Vector[32] BuildBlob(axVertices,iVerticeCount,radiusx,radiusy) InitialiseB(b,xPosition,fDensity,axVertices,iVerticeCount) Return b End Function
Function InitialiseB(B.Body,xPosition.Vector,fDensity#,axVertices.Vector[32],iVerticeCount) B\xVelocity=cVector(0,0) B\fAngVelocity=0 B\xOrientation=cMatrix_Identity() B\xNetForce=cVector(0,0) B\fNetTorque=0 B\xPosition=xPosition B\fDensity=fDensity B\fMass=CalculateMass(axVertices,iVerticeCount,fDensity) B\fInertia=CalculateInertia(axVertices,iVerticeCount,B\fMass) If B\fMass=NaN B\fMass=0 EndIf If B\fInertia=NaN B\fInertia=0 EndIf If B\fMass>0.0001 B\fInvMass=1/B\fMass Else B\fInvMass=0 EndIf If B\fInertia>0.0001 B\fInvInertia=1/B\fInertia Else B\fInvInertia=0 EndIf maxRad#=0 For i=0 To iVerticeCount-1 B\axVertices[i]=axVertices[i] actRad#=(B\xPosition\X-B\axVertices[i]\X)^2+(B\xPosition\Y-B\axVertices[i]\Y)^2 If actRad>maxRad maxRad=actRad EndIf Next B\iVerticeCount=iVerticeCount B\fMaxRadiusSqare=maxRad B\fMaxRadius=Sqr(maxRad) End Function
Function IsUnmovable(B.Body) If B\fMass<0.0001 Return True Else Return False EndIf End Function
Function AddForce1(B.Body,F.Vector) If IsUnmovable(B) Return EndIf opVector_Add(B\xNetForce,F) End Function
Function AddForce2(B.Body,F.Vector,P.Vector) If IsUnmovable(B) Return EndIf opVector_Add(B\xNetForce,F) B\fNetTorque=B\fNetTorque+opVector_CP(opVectorN_Sub(P,B\xPosition),F) End Function
Function UpdateB(B.Body,dt#) If IsUnmovable(B) B\xVelocity\X=0 B\xVelocity\Y=0 B\fAngVelocity=0 Return EndIf opVector_Add(B\xPosition,opVectorN_Mult(B\xVelocity,dt)) B\fOrientation=B\fOrientation+B\fAngVelocity*dt B\fOrientation=Wrapf(B\fOrientation,-2*Pi,2*Pi) B\xOrientation=cMatrix_Rot(RadiansToDegrees(B\fOrientation)) opVector_Add(B\xVelocity,opVectorN_Mult(B\xNetForce,B\fInvMass*dt)) B\fAngVelocity=B\fAngVelocity+B\fNetTorque*(B\fInvInertia*dt) B\xNetForce\X=0 : B\xNetForce\Y=0 B\fNetTorque=0 End Function
Function RenderB(B.Body) If IsUnmovable(B) Color 125,125,125 Else Color 255,255,255 EndIf RenderP(B\xPosition,B\xOrientation,B\axVertices,B\iVerticeCount) End Function
Function CollideB(A.Body,B.Body,dt#) If IsUnmovable(A) And IsUnmovable(B) Return False EndIf t#=dt N.Vector=cVector(0,0) If CollideP(A\axVertices,A\iVerticeCount,A\xPosition,A\xVelocity,A\xOrientation,B\axVertices,B\iVerticeCount,B\xPosition,B\xVelocity,B\xOrientation,N,t) t#=Coll_t Local CA.Vector[4] Local CB.Vector[4] Local Cnum FindContacts(A\axVertices,A\iVerticeCount,A\xPosition,A\xVelocity,A\xOrientation,B\axVertices,B\iVerticeCount,B\xPosition,B\xVelocity,B\xOrientation,N,t,CA,CB,Cnum) Cnum=FC_Cnum xContact.Contact=cContact(CA,CB,Cnum,N,t,A,B) Solve(xContact) Return True EndIf Return False End Function
Const eMaxContacts=2
Type Contact Field axBodies.Body[2] Field xContacts.Vector[eMaxContacts*2] Field xNormal.Vector Field t# Field iNumContacts End Type
Function cContact.Contact(CA.Vector[4],CB.Vector[4],Cnum,N.Vector,t#,pxBodyA.Body,pxBodyB.Body) C.Contact=New Contact C\iNumContacts=0 C\axBodies[0]=pxBodyA C\axBodies[1]=pxBodyB C\xNormal=N C\t=t For i=0 To Cnum-1 AddContactPair(C,CA[i],CB[i]) Next Return c End Function
Function AddContactPair(C.Contact,CA.Vector,CB.Vector) If C\iNumContacts>=eMaxContacts Return EndIf C\xContacts[C\iNumContacts+0*eMaxContacts]=CA C\xContacts[C\iNumContacts+1*eMaxContacts]=CB C\iNumContacts=C\iNumContacts+1 End Function
Function Solve(C.Contact) If C\t<0 ResolveOverlapG(C) EndIf ResolveCollisionG(C) End Function
Function ResolveOverlapG(C.Contact) If C\axBodies[0]=Null Or C\axBodies[1]=Null Return EndIf For i=0 To C\iNumContacts-1 ResolveOverlap(C,C\xContacts[i+0*eMaxContacts],C\xContacts[i+1*eMaxContacts]) Next End Function
Function ResolveOverlap(C.Contact,C0.Vector,C1.Vector) m0#=C\axBodies[0]\fInvMass m1#=C\axBodies[1]\fInvMass m#=m0+m1 If C0=Null Or C1=Null Return EndIf D.Vector=opVectorN_Sub(C1,C0) fRelaxation#=s_fSep opVector_Mult(D,fRelaxation) D0.Vector=Null D1.Vector=Null If m0>0 D0=opVectorN_Mult(D,m0/m) opVector_Add(C\axBodies[0]\xPosition,D0) EndIf If m1>0 D1=opVectorN_Mult(D,-m1/m) opVector_Add(C\axBodies[1]\xPosition,D1) EndIf End Function
Function ResolveCollisionG(C.Contact) If C\axBodies[0]=Null Or C\axBodies[1]=Null Return EndIf For i=0 To C\iNumContacts-1 ResolveCollision(C,C\xContacts[i+0*eMaxContacts],C\xContacts[i+1*eMaxContacts]) Next End Function
Function ResolveCollision(C.Contact,C0.Vector,C1.Vector) If C0=Null Or C1=Null Return EndIf m0#=C\axBodies[0]\fInvMass m1#=C\axBodies[1]\fInvMass i0#=C\axBodies[0]\fInvInertia i1#=C\axBodies[1]\fInvInertia P0.Vector=C\axBodies[0]\xPosition P1.Vector=C\axBodies[1]\xPosition V0.Vector=C\axBodies[0]\xVelocity V1.Vector=C\axBodies[1]\xVelocity w0#=C\axBodies[0]\fAngVelocity w1#=C\axBodies[1]\fAngVelocity fCoR#=s_fCoR fCoF#=s_fCoF ResolveCollisionB(opVectorN_Mult(C\xNormal,-1),C\t,fCoF,fCoR,C1,P1,V1,w1,m1,i1,C0,P0,V0,w0,m0,i0) C\axBodies[0]\fAngVelocity=RC_w1 C\axBodies[1]\fAngVelocity=RC_w0 End Function
Global RC_w0#, RC_w1# Function ResolveCollisionB(Ncoll.Vector,t#,fCoF#,fCoR#,C0.Vector,P0.Vector,V0.Vector,w0#,m0#,i0#,C1.Vector,P1.Vector,V1.Vector,w1#,m1#,i1#) RC_w0=w0 RC_w1=w1 If t>0 tcoll#=t Else tcoll#=0 EndIf Q0.Vector=opVectorN_Add(P0,opVectorN_Mult(V0,tcoll)) Q1.Vector=opVectorN_Add(P1,opVectorN_Mult(V1,tcoll)) R0.Vector=opVectorN_Sub(C0,Q0) R1.Vector=opVectorN_Sub(C1,Q1) T0.Vector=cVector(-R0\y,R0\x) T1.Vector=cVector(-R1\y,R1\x) VP0.Vector=opVectorN_Sub(V0,opVectorN_Mult(T0,w0)) VP1.Vector=opVectorN_Sub(V1,opVectorN_Mult(T1,w1)) Vcoll.Vector=opVectorN_Sub(VP0,VP1) vn_#=opVector_DP(Vcoll,Ncoll) VN.Vector=opVectorN_Mult(Ncoll,vn_) VT.Vector=opVectorN_Sub(Vcoll,VN) If vn_>0 Return EndIf vt_#=Normalise(VT) J.Vector=Null JT.Vector=cVector(0,0) JN.Vector=cVector(0,0) t0_#=opVector_CP(R0,Ncoll)*opVector_CP(R0,Ncoll)*i0 t1_#=opVector_CP(R1,Ncoll)*opVector_CP(R1,Ncoll)*i1 m#=m0+m1 denom#=m+t0_+t1_ jn_#=vn_/denom JN=opVectorN_Mult(Ncoll,-(1+fCoR)*jn_) If dbg_useFriction JT=opVectorN_Mult(Direction(VT),fCoF*jn_) EndIf J=opVectorN_Add(JN,JT) dV0.Vector=opVectorN_Mult(J,m0) dV1.Vector=opVectorN_Mult(J,-m1) dw0#=-opVector_CP(R0,J)*i0 dw1#=opVector_CP(R1,J)*i1 If m0>0 opVector_Add(V0,dV0) w0=w0+dw0 EndIf If m1>0 opVector_Add(V1,dV1) w1=w1+dw1 EndIf If vn_<0 And fCoF>0 cone#=-vt_/vn_ If cone#<fCoF Nfriction.Vector=opVectorN_Mult(Direction(VT),-1) fCoS#=s_fCoS ResolveCollisionB(Nfriction,0,0,fCoS,C0,P0,V0,w0,m0,i0,C1,P1,V1,w1,m1,i1) w0=RC_w0 w1=RC_w1 EndIf EndIf RC_w0=w0 RC_w1=w1 End Function
Const s_fCoF#=0.2 Const s_fCoR#=0.3 Const s_fCoS#=0.4 Const s_fSep#=0.5 Global s_xGravity.Vector=cVector(0,0.5)
Global dbg_useFriction=True Global dbg_useGravity=True
Const s_MAX_BODIES=100 Global s_axBody.Body[s_MAX_BODIES] Global s_pxPlayer.Body Global s_iBodyCount=15
Function GameUpdate(dt#) For i=0 To s_iBodyCount-1 If s_axBody[i]<>Null If dbg_useGravity AddForce1(s_axBody[i],opVectorN_Mult(s_xGravity,s_axBody[i]\fMass)) EndIf EndIf Next For i=0 To s_iBodyCount-1 If s_axBody[i]<>Null For j=i+1 To s_iBodyCount-1 If s_axBody[j]<>Null If IsUnmovable(s_axBody[i])=0 Or IsUnmovable(s_axBody[j])=0 BodyDist#=(s_axBody[i]\xPosition\X-s_axBody[j]\xPosition\X)*(s_axBody[i]\xPosition\X-s_axBody[j]\xPosition\X)+(s_axBody[i]\xPosition\Y-s_axBody[j]\xPosition\Y)*(s_axBody[i]\xPosition\Y-s_axBody[j]\xPosition\Y) MaxDist#=s_axBody[i]\fMaxRadiusSqare+s_axBody[j]\fMaxRadiusSqare If BodyDist<MaxDist CollideB(s_axBody[i],s_axBody[j],dt) EndIf EndIf EndIf Next EndIf Next For i=0 To s_iBodyCount-1 If s_axBody[i]<>Null If Not IsUnmovable(s_axBody[i]) UpdateB(s_axBody[i],dt) EndIf EndIf Next End Function
Function GameRender() LockBuffer BackBuffer() For b.body=Each Body RenderB(b) Next UnlockBuffer BackBuffer() End Function
Function GC() Local B.Body For B.Body=Each Body B\vx=B\xVelocity\X B\vy=B\xVelocity\Y B\px=B\xPosition\X B\py=B\xPosition\Y For i=0 To B\iVerticeCount-1 B\vertx[i]=B\axVertices[i]\X B\verty[i]=B\axVertices[i]\Y Next Next gx#=s_xGravity\X : gy#=s_xGravity\Y Delete Each Vector Delete Each Matrix Delete Each Contact s_xGravity=cVector(gx,gy) For B.Body=Each Body B\xVelocity=cVector(B\vx,B\vy) B\xPosition=cVector(B\px,B\py) B\xOrientation=cMatrix_Rot(RadiansToDegrees(B\fOrientation)) B\xNetForce=cVector(0,0) For i=0 To B\iVerticeCount-1 B\axVertices[i]=cVector(B\vertx[i],B\verty[i]) Next Next End Function
Function addBody(B.Body) s_axBody[s_iBodyCount]=B s_Mesh[s_iBodyCount]=GenerateMesh(B) s_iBodyCount=s_iBodyCount+1 End Function
Function addVertice(B.Body,V.Vector) B\axVertices[B\iVerticeCount]=V B\iVerticeCount=B\iVerticeCount+1 End Function
Global s_Mesh[s_MAX_BODIES] Global s_GameCam
Function initGameCam() s_GameCam=CreateCamera() PositionEntity s_GameCam,0,0,GraphicsWidth()/2 RotateEntity s_GameCam,0,180,180 End Function
Type TmpTrig Field V1.Vector Field V2.Vector Field V3.Vector End Type
Function GenerateMesh(B.Body) SubtractingEars(B,B\axVertices[0]) Mesh=CreateMesh() EntityFX Mesh,16 Surf=CreateSurface(Mesh) For t.TmpTrig=Each TmpTrig v1=AddVertex(Surf,t\v1\x,t\v1\y,0) v2=AddVertex(surf,t\v2\x,t\v2\y,0) v3=AddVertex(Surf,t\v3\x,t\v3\y,0) AddTriangle(surf,v1,v2,v3) Next Delete Each TmpTrig Return Mesh End Function
Function SubtractingEars(B.Body,Vertex.Vector) For i=0 To B\iVerticeCount-1 If Vertex=B\axVertices[i] vIndex=i Exit EndIf Next Pred.Vector=PredVertex(B,vIndex) Succ.Vector=SuccVertex(B,vIndex) If LineInPoly(B,Pred,Succ,Vertex) Or VertexCount(B)=3 Triangle.TmpTrig=New TmpTrig Triangle\V1=Vertex Triangle\V2=Pred Triangle\V3=Succ Vertex\used=True EndIf If VertexCount(B)>=3 SubtractingEars(B,Succ) EndIf End Function
Function PredVertex.Vector(B.Body,vIndex) While True vIndex=vIndex-1 If vIndex<0 vIndex=B\iVerticeCount-1 EndIf If Not B\axVertices[vIndex]\used Return B\axVertices[vIndex] EndIf Wend End Function
Function SuccVertex.Vector(B.Body,vIndex) While True vIndex=vIndex+1 If vIndex>B\iVerticeCount-1 vIndex=0 EndIf If Not B\axVertices[vIndex]\used Return B\axVertices[vIndex] EndIf Wend End Function
Function VertexCount(B.Body) count=0 For i=0 To B\iVerticeCount-1 If Not B\axVertices[i]\used count=count+1 EndIf Next Return count End Function
Function LineInPoly(B.Body,V1.Vector,V2.Vector,MidVertex.Vector) angle#=(360+ATan2(V1\Y-MidVertex\Y,V1\X-MidVertex\X)-ATan2(V2\Y-MidVertex\Y,V2\X-MidVertex\X)) Mod 360 If angle>180 Return False EndIf For i=0 To B\iVerticeCount-1 Succ.Vector=SuccVertex(B,i) If B\axVertices[i]<>V1 And B\axVertices[i]<>V2 And Succ<>V1 And Succ<>V2 If LineCollision(V1\X,V1\Y,V2\X-V1\X,V2\Y-V1\Y,B\axVertices[i]\X,B\axVertices[i]\Y,succ\x-B\axVertices[i]\X,succ\y-B\axVertices[i]\Y) Return False EndIf EndIf Next vx#=V1\Y-V2\Y vy#=V2\X-V1\X c#=-V1\X*vx-V1\Y*vy sign=0 For i=0 To B\iVerticeCount-1 If B\axVertices[i]<>V1 And B\axVertices[i]<>V2 If sign If sign<>Sgn(B\axVertices[i]\X*vx+B\axVertices[i]\Y*vy+c) Exit EndIf Else sign=Sgn(B\axVertices[i]\X*vx+B\axVertices[i]\Y*vy+c) EndIf EndIf Next Return True End Function
Function LineCollision(X1#,Y1#,VX1#,VY1#,X2#,Y2#,VX2#,VY2#) If VX1=0 Then VX1=0.001 If VY1=0 Then VY1=0.001 If VX2=0 Then VX2=0.001 If VY2=0 Then VY2=0.001 IntersectionX#=-(X1*VX2*VY1-(X2*VY2+(Y1-Y2)*VX2)*VX1)/(VX1*VY2-VX2*VY1) IntersectionT#=(IntersectionX-X1)/VX1 If IntersectionT>=0 And IntersectionT<=1 IntersectionT=(IntersectionX-X2)/VX2 If IntersectionT>=0 And IntersectionT<=1 Return True EndIf EndIf End Function
Function DestroyMeshes() For i=0 To s_iBodyCount-1 If s_Mesh[i]<>0 FreeEntity s_Mesh[i] s_Mesh[i]=0 EndIf Next If s_GameCam<>0 FreeEntity s_GameCam s_GameCam=0 EndIf End Function
Function UpdateMeshes() For i=0 To s_iBodyCount-1 PositionEntity s_Mesh[i],s_axBody[i]\xPosition\X,s_axBody[i]\xPosition\Y,0 RotateEntity s_Mesh[i],0,0,RadiansToDegrees(-s_axBody[i]\fOrientation) Next End Function
(ich hoffe die Codebox macht meine Teilungskommentare nicht wieder kaputt)
Und erneut eine kleine Sandbox zum Test:
WARNUNG: Benötigt den Library Code von oben. Hineinkopieren oder speichern als "RigidPhysicsLib.bb"
BlitzBasic: [AUSKLAPPEN] [EINKLAPPEN] Include "RigidPhysicsLib.bb"
Function GameInit() xBottom.Vector=cVector(0,240) xTop.Vector=cVector(0,-240) xLeft.Vector=cVector(-320,0) xRight.Vector=cVector(320,0) xCentre.Vector=cVector(0,0) s_axBody[0]=cBody_Box(xBottom,0,640,5) s_axBody[1]=cBody_Box(xTop,0,640,5) s_axBody[2]=cBody_Box(xLeft,0,5,480) s_axBody[3]=cBody_Box(xRight,0,5,480) s_axBody[4]=cBody_Box(xCentre,0,30,30) For i=5 To s_iBodyCount-1 s_axBody[i]=cBody_Blob(cVector(Rnd(-300,300),Rnd(-200,-100)),0.075,Rnd(10,25),Rnd(10,25)) s_axBody[i]\fAngVelocity=Rnd(0.1,0.5) s_axBody[i]\fOrientation=Rnd(0,Pi) s_axBody[i]\xOrientation=cMatrix_Rot(RadiansToDegrees(s_axBody[i]\fOrientation)) Next For i=0 To s_iBodyCount-1 s_Mesh[i]=GenerateMesh(s_axBody[i]) Next End Function
Global screenWidth#=640 Global screenHeight#=480 Global timer=CreateTimer(60) Graphics3D screenWidth,screenHeight,0,2 SetBuffer BackBuffer()
initGameCam()
Origin 320,240
GameInit()
While Not KeyHit(1) RenderWorld If MouseHit(1) If s_iBodyCount<s_MAX_BODIES-1 tmpBody.Body=cBody_Blob(cVector(MouseX()-GraphicsWidth()/2,MouseY()-GraphicsHeight()/2),0.1,Rnd(10,25),Rnd(10,25)) addBody(tmpBody) EndIf EndIf GameUpdate(0.125) UpdateMeshes() Color 255,255,255 fps=fps+1 If MilliSecs()-fpsTime>999 fpsCur=fps fpsTime=MilliSecs() fps=0 EndIf Text 300,220,fpsCur Text -310,-230,(s_iBodyCount-4) Flip 0 Cls WaitTimer(timer) GC() Wend
Der Code ist ausgiebig getestet, aber dennoch gilt das Prinzip [i]errare humanum est, solltet ihr also Fehler oder Verbesserungsvorschläge haben, bin ich immer offen dafür.
Ich möchte eigentlich versuchen, den Code nach C++ zu portieren, um eine DLL daraus zu machen, um noch etwas mehr Geschwindigkeit herauszuholen, aber dazu muss ich mir noch die Kommunikation zwischen C++ und BlitzBasic genauer betrachten.
Bis dahin:
GG, HF, GL
MfG,
Darth
Danksagung
Chris Hecker, Tutorial
Noobody, Triangulierungsmethode
|