The applyForce(V) and applyOffsetForce(V,V) are definitely very powerful and helpful functions. To answer your question, the Havok physics engine uses very strange units. The applyForce(V) function actually uses lb*in/s^2 (pound-inches per second squared) as apposed to newtons which are kg*m/s^2 (kilogram-meters per second squared). The gravity of Garry's Mod is measured in in/s^2 (inches per second squared) and the default value is 600 in/s^2, so using this, the force needed to float an object is the gravity times the mass of the entity. This needs to be changed slightly though in the e2 gate because of the interval() function. If we just multiply the gravity by the mass of the object, we would need to set the interval to 1000 ms in order to get the desired effect. This is a problem though because if we set the interval to 1000 ms, the movement will be very jerky. To fix this we multiply the calculated needed force by the interval divided by 1000 (Interval/1000). In theory this should give you a floating effect (kind of like if you had no graved the prop), but because of inaccuracies in the physics, you must also multiply this by an error corrector that you have to find by hand. Here is some sample code to make a prop float like it has been no graved:
Code:
@name NoGrav
@inputs T:entity Up
@outputs
@persist Interval
Interval = 10
interval(Interval)
ConversionToSeconds = Interval/1000
ErrorMultiplier = 1.5022
if(T) {
Mass = T:mass()
if(Up){
T:applyForce(vec(0,0,1)*ErrorMultiplier*T:mass()*gravity()*ConversionToSeconds)
}
} This will make the prop float, but in order to make a prop stop in midair, you must use a different method. This can be using delta or a more complicated linear control system like PID.
Here is a very good description of PID (the pseudo code section is the most useful). I have used PID in my expressions to move props. Here is an example of a rocket controller expression gate that I made that uses PID:
Code:
@name Torpedo Controler
@inputs Target:entity
@inputs Launch
@outputs Detonate
@persist TargetPos:vector TargetAngle:angle TargetRoll Torpedo:entity
@persist PreviousError:vector Error:vector
@persist Integral:vector
@persist AnglePreviousError:angle AngleError:angle
@persist AngleIntegral:angle Counter
interval(1)
if(first())
{
TargetPos = Torpedo:pos()
Torpedo = entity():isWeldedTo()
}
if(Launch)
{
MovedTarget = Torpedo:pos() - (Target:pos()+vec(0,0,30))
TargetPitch = angnorm( atan( MovedTarget:z()/(sqrt(MovedTarget:y()^2+MovedTarget:x()^2)) ) )
if(MovedTarget:x() < 0)
{
TargetYaw = atan( MovedTarget:y()/MovedTarget:x() )
}
elseif(MovedTarget:x() >= 0)
{
TargetYaw = angnorm( atan( MovedTarget:y()/MovedTarget:x() ) + 180)
}
TargetAngle = ang(clamp(TargetPitch, -50, 50),TargetYaw,0)
#Movement
Speed = 20
TargetPos = Launch * Speed * Torpedo:forward() + Torpedo:pos()
#Detonation
Distance = Torpedo:pos():distance(Target:pos())
if(Distance < 100 & Launch)
{
Detonate = 1
}
else
{
Detonate = 0
}
#Position Stabilization
Kp = 30
Ki = 1
Kd = 0.06
Error = TargetPos - Torpedo:pos()
Integral = Integral + Error*0.001
Derivative = (Error - PreviousError)/0.001
PosForceUC = Kp*Error + Ki*Integral + Kd*Derivative
PreviousError = Error
PosForceMulti = Torpedo:mass()
PosForceUC = PosForceUC*PosForceMulti
PosForce = PosForceUC
Torpedo:applyForce( PosForce )
#Angle Stabilization
Kp = 500
Ki = 1
Kd = 20
AngleError = (TargetAngle - Torpedo:angles())
AngleIntegral = AngleIntegral + AngleError*0.01
AngleDerivative = (AngleError - AnglePreviousError)/0.01
AngleForceUC = Kp*AngleError + Ki*AngleIntegral + Kd*AngleDerivative
AnglePreviousError = AngleError
AngForceMulti = ang(Torpedo:inertia():y(), Torpedo:inertia():z(), Torpedo:inertia():x())
AngleForceUC = AngleForceUC*AngForceMulti
AngleForce = ang( clamp(AngleForceUC:pitch(), -1000000000, 1000000000), clamp(AngleForceUC:yaw(), -1000000000, 1000000000), clamp(AngleForceUC:roll(), -1000000000, 1000000000) )
Torpedo:applyAngForce( AngleForce )
} I hope this helped. Good luck coding and if you have any questions don't be afraid to ask.

Bookmarks