I converted the whole Gaffer post about 4 months ago, including the RK4 (integrator function..etc) and it works silky smooth. If anyone is interested I can post it.
Here's part of it (non-RK4). Supply your own ball.png.
Press the Space Bar to see the Tween in action.
Part 1 (main.bmx):
Part 2 (Vec3.bmx):
Here's part of it (non-RK4). Supply your own ball.png.
Press the Space Bar to see the Tween in action.
Part 1 (main.bmx):
'FRLRT SuperStrict Import "Vec3.bmx" AppTitle$ = "Fixed Rate Logic with Render Tweening" Graphics 800,600 Local newTime:Int = MilliSecs() Local oldtime:Int = newTime Local dt# = 1.0/100.00 '100 FPS logic Local deltaTime#, accumulator#, alpha#, t# Local ball:TBody = CreateBody() ball.current.vPos.x = 400 ball.current.vPos.y = 500 ball.current.vVel.Set(500,-500,0) SetBlend ALPHABLEND Global imgBall:TImage = LoadImage("ball.png") MidHandleImage imgBall Global RenderTween:Int = False While Not KeyHit(KEY_ESCAPE) Cls newTime = MilliSecs() deltaTime# = (newTime - oldTime) * 0.001 oldTime = newTime accumulator :+ deltaTime While accumulator >= dt 'Apply some gravity ApplyLinearForce ball, 0, ball.fMass * 981.0, 0 ball.Logic(dt) accumulator :- dt 't :+ dt Wend alpha = accumulator / dt ball.Render(alpha) If KeyHit(KEY_SPACE) Then RenderTween = 1 - RenderTween DrawText "Press the SPACE BAR to turn on render tweening mode.",5,5 DrawText "RenderTween: "+RenderTween,5,50 Flip 1 'Eliminates Tearing Wend End Type TBody Field fMass:Float = 1.0 Field current:TState = New TState Field previous:TState = New TState Method Logic(dt#) 'Basic Linear Physics self.previous = self.current.copy() self.current.vAcc = self.current.vForces.DividedByScalar(self.fMass) self.current.vVel = self.current.vVel.Plus(self.current.vAcc.TimesScalar(dt)) self.current.vPos = self.current.vPos.Plus(self.current.vVel.TimesScalar(dt)) self.current.vForces.Zero() 'Simple Collision If self.current.vPos.y <= 0 Or self.current.vPos.y >= 600 self.current.vVel.y = -self.current.vVel.y EndIf If self.current.vPos.x <= 0 Or self.current.vPos.x >= 800 self.current.vVel.x = -self.current.vVel.x EndIf End Method Method Render(alpha#) Local x#, y# If RenderTween x# = self.current.vPos.x * alpha + self.previous.vPos.x * (1.0 - alpha) y# = self.current.vPos.y * alpha + self.previous.vPos.y * (1.0 - alpha) Else x = self.current.vPos.x y = self.current.vPos.y EndIf DrawImage imgBall, x, y End Method End Type Function CreateBody:TBody() Return New TBody End Function Type TState Field vPos:TVec3 = Vec3() Field vVel:TVec3 = Vec3() Field vAcc:TVec3 = Vec3() Field vForces:TVec3 = Vec3() Field vAngAcc:TVec3 = Vec3() Field vAngVel:TVec3 = Vec3() Field vMoments:TVec3 = Vec3() Method Copy:TState() Local t:TState = New TState t.vPos = self.vPos.Copy() t.vVel = self.vVel.Copy() t.vAcc = self.vAcc.Copy() Return t End Method End Type Function ApplyLinearForce(body:TBody, xf#, yf#, zf#) Local vF:TVec3 = Vec3(xf, yf, zf) body.current.vForces = body.current.vForces.Plus( vF ) End Function
Part 2 (Vec3.bmx):
Rem bbdoc: EndRem Type TVec3 Field x:Float, y:Float, z:Float Method Copy:TVec3() Return Vec3(self.x, self.y, self.z) End Method Method CrossProduct:TVec3(v:TVec3) Local res:TVec3 = New TVec3 res.x = self.y * v.z - self.z * v.y res.y = -self.x * v.z + self.z * v.x res.z = self.x * v.y - self.y * v.x Return res End Method Method DividedBy:TVec3(v:TVec3) Local res:TVec3 = New TVec3 res.x = self.x / v.x res.y = self.y / v.y res.z = self.z / v.z Return res End Method Method DotProduct:Float(v:TVec3) Return self.x * v.x + self.y * v.y + self.z * v.z End Method Method DividedByScalar:TVec3(divisor#) Local res:TVec3 = New TVec3 res.x = self.x / divisor res.y = self.y / divisor res.z = self.z / divisor Return res End Method Method Inverse:TVec3() Local v:TVec3 = New TVec3 v.x = -self.x v.y = -self.y v.z = -self.z Return v End Method Method Magnitude:Float() Return Sqr(self.x * self.x + self.y * self.y + self.z * self.z) End Method Method Minus:TVec3(v:TVec3) Local res:TVec3 = New TVec3 res.x = self.x - v.x res.y = self.y - v.y res.z = self.z - v.z Return res End Method Method Normalize() Local m# = self.Magnitude() self.x :/ m self.y :/ m self.z :/ m End Method Method Plus:TVec3(v:TVec3) Local res:TVec3 = New TVec3 res.x = self.x + v.x res.y = self.y + v.y res.z = self.z + v.z Return res End Method Method Set(x#, y#, z#) self.x = x self.y = y self.z = z End Method Method Show(cap:String, xpos:Int, ypos:Int, vspc:Int = 12) DrawText cap+".x=" + self.x,xpos,ypos DrawText cap+".y=" + self.y,xpos,ypos + vspc * 1 DrawText cap+".z=" + self.z,xpos,ypos + vspc * 2 End Method Method Times:TVec3(v:TVec3) Local res:TVec3 = New TVec3 res.x = self.x * v.x res.y = self.y * v.y res.z = self.z * v.z Return res End Method Method TimesScalar:TVec3(scalar#) Local res:TVec3 = New TVec3 res.x = self.x * scalar res.y = self.y * scalar res.z = self.z * scalar Return res End Method Method Zero() self.x = 0 self.y = 0 self.z = 0 End Method End Type Rem bbdoc: EndRem Function Vec3:TVec3(x#=0, y#=0, z#=0) Local v:TVec3 = New TVec3 v.x = x v.y = y v.z = z Return v End Function