Simulated Physics

I asked in the newbie questions forum, but since this apparently is not simple I’ll ask here. Right now I’m using **[PhysObj.ComputeShadowControl

http://wiki.garrysmod.com/favicon.ico](wiki.garrysmod.com/?title=PhysObj.ComputeShadowControl)** inside of the **[ENT.PhysicsSimulate

http://wiki.garrysmod.com/favicon.ico](wiki.garrysmod.com/?title=ENT.PhysicsSimulate)** hook.

Here’s what I’m trying to do: My SENT is like a vehicle of sorts, but it cruises at a set distance above the ground, except when it goes off of ledges, in which case you can glide (fly). Right now I’m working on the cruising aspect. The SENT has several variables that alter how the movement works.

[ul]
[li]Speed[/li]This is the constant speed that the entity tries to achieve unless told otherwise.
[li]Acceleration[/li]This is in units per second. It does not effect the enitity’s speed after it has reached its desired speed.
[li]Turn[/li]This is how fast the entity turns when the player presses their strafe keys. It is in degrees per second.
[li]Handling[/li]This is where things get tricky. I’ll try and explain this as best I can.
Handling determines how quickly the current move vector changes to the one that the player is trying to move in based on current speed. The turn variable and the handling variable do not effect one another, but they do combine to represent the control the player has over the vehicle. In other words, Turn + Handling = Control.
Here are some examples of how this would work:
[list][li]High turn + low handling = Spinning around when turning while sliding in your current move vector.[/li][li]Low turn + high handling = Very gradual turning.[/li][li]High turn + high handling = Moving and turning exactly where you intend to.[/li][li]Low turn + low handling = No control at all.[/ul][/li]Most of the time, the mix will be somewhere in between. I’m just using extremes so you get the picture.
[li]Glide[/li]This one is dependant on the input of the forward/back keys. Forward means aim down, back means aim up. The number here determines how high and far we can glide, the degrees of pitch change per second, the maximum upward pitch before no longer travelling forward and up, our speed and acceleration in air, and the amount of time it takes before descent. All of this is also in direct relation to the previous values presented.
[/list]
Now that you understand the issue (I hope), it’s time for the math, which is what I suck at.

Here’s what I have so far:
[lua]
function ENT:PhysicsSimulate( phys, delta )

phys:Wake()
local ply = self.Rider -- Our player controlling the SENT
local turn, speed
if self.Rider then
	turn, speed = --This is what I need to find...
else
	return SIM_NOTHING 
end

local accel = 5 --Our acceleration
local maxspeed = 25 --Our speed
local curvec = speed or self:GetVelocity() --Our current speed

if curvec:Length() >= maxspeed then
	accel = 1
end

local newpos = (curvec*delta) + speed * (0.5 * accel * (delta^2)) --Our next position to move to
local dist = (self:GetPos() - newpos):Length()
local sta = self:GetVelocity():Length()/dist --seconds to arrive

self.ShadowParams.secondstoarrive = sta			// How long it takes to move to pos and rotate accordingly - only if it _could_ move as fast as it want - damping and max speed/angular will make this invalid (Cannot be 0! Will give errors if you do)
self.ShadowParams.pos = newpos 					// Where you want to move to
self.ShadowParams.angle = turn					// Angle you want to move to
self.ShadowParams.maxangular = 1 			// What should be the maximal angular force applied
self.ShadowParams.maxangulardamp = 3 		// At which force/speed should it start damping the rotation
self.ShadowParams.maxspeed = 4 			// Maximal linear force applied
self.ShadowParams.maxspeeddamp = 5			// Maximal linear force/speed before  damping
self.ShadowParams.dampfactor = 0.8 				// The percentage it should damp the linear/angular force if it reaches it's max amount
self.ShadowParams.teleportdistance = 0 			// If it's further away than this it'll teleport (Set to 0 to not teleport)
self.ShadowParams.deltatime = deltatime 		// The deltatime it should use - just use the PhysicsSimulate one

phys:ComputeShadowControl(self.ShadowParams)

end
[/lua]
In short, none of this works yet. I haven’t a clue as to how to make all of the ShadowParams work cohesively.

Anyone know how I can achieve this kind of movement I’ve described?

This sounds like a Jet Moto type of device. I wanted to make this work within e2.

It’s for a gamemode I’m making. I can’t really release any more information until I have this done though.

So you want to figure out what angle the ‘ship’ is to move to? How will the player be controlling the direction? Pointing the mouse? Keyboard? (using the mouse would be the most simple way imo)

More details :smiley:

Read it. I said it’s controlled by the WASD keys (i.e. Strafe and forward/back).

Damnit, should have read that a little better.

And let me get this a little clearer about the turning control. One of the values is how fast you turn, one of them is how accurately that turn is (so holding left strafe with a low Handling would end up in you turning too far)

Think of it like this. A car is speeding down a highway and does a sharp turn. If handling is low on this car, it spins out of control until its speed is low enough to get back into control. If the handling is high, the car will basically turn and move exactly where it was meant to.

So maybe something like:

[lua]
function Turning()
if( TurnValueBlah ) then // something unique, so as not to conflict. make sure turnvalueblah is actually existant
if( ply:KeyDown( side ) ) then

TurnValueBlah = TurnValueBlah + ((TurnValueBlah+TurningSpeed)/Handeling) // So the longer we hold the key down, the faster we turn
if( TurnValueBlah > MaxTurnSpeed ) then
TurnValueBlah = MaxTurnSpeed
end
elseif( ply:KeyDown( otherside ) ) then

TurnValueBlah = TurnValueBlah - ((TurnValueBlah+TurningSpeed)/Handeling) // So the longer we hold the key down, the faster we turn
if( TurnValueBlah < MinTurnSpeed ) then
TurnValueBlah = MinTurnSpeed
end
elseif( !ply:KeyDown( side ) and !ply:KeyDown( otherside ) ) then
if(
TurnValueBlah > 1 ) then // we have turned in one direction

TurnValueBlah = TurnValueBlah - ((TurnValueBlah+TurningSpeed)/Handeling)
elseif( TurningSpeed < -1 ) then

TurnValueBlah = TurnValueBlah + ((TurnValueBlah+TurningSpeed)/Handeling)
end
end
end
TurnAngleBlah = Angle( TurnAngleBlah.p, TurnAngleBlah.y, TurnAngleBlah.r ) // you would add TurnValBlah to the pitch, yaw, or roll (i forget which order they go in, and which one controll rotation on the axis you want)
return TurnAngleBlah
end
[/lua]

And yes, im tired too. You kinda get the point from the pseudo code. It might work, but as i said, im tired… So thats my excuse.

I’m about to fall asleep. If I don’t reply soon after you post code, it means I’ll be back in 8 or 10 hours. :smile:

[editline]02:58AM[/editline]

What?

Gah, stupid laptop fucked all the tabbing up :confused:

[editline]07:16PM[/editline]

I suppose that means you went off. At least now i have time to make something decent :smiley:

[editline]09:05PM[/editline]

~130 views and im the only post?

It sounds very much like you should just look into how wings work (and a glider is essentially just a wing), check the wing-stool or some of the wings made in e2 (although I’m not sure if people ever released those). Most of this can be faked pretty easily, but getting the right feel requires that you do look into how wings work, shouldn’t be terribly complicated.

Here’s a basic code that half worked. The effect I was going for was the ability to hover above any surface (caused me a lot of pain).

[lua]function ENT:PhysicsSimulate( phys, deltatime )

if self.OnTrack and self.Pilot and self.InFlight and self.OnTrack then
	
	local track = nil
	for _,v in ipairs(ents.FindInSphere(self.Entity:GetPos(),200)) do
		if v:GetClass() == "env_track" then track = v end
	end
	
	local tracktr = util.TraceLine({start = self.Entity:GetPos(),endpos = self.Entity:GetPos() - self.Entity:GetUp() * 300,filter=self.Entity})
	
	if !track or track.Entity != track then return end

	phys:Wake()
		if self.Pilot:KeyDown(IN_FORWARD) then self.Accel = math.Approach(self.Accel,1000,2.5)
		elseif self.Pilot:KeyDown(IN_BACK) then self.Accel = math.Approach(self.Accel,-1000,-2.5)
		else
			if self.Accel &gt; 0 then self.Accel = math.Approach(self.Accel,0,-4.5) 
			else self.Accel = math.Approach(self.Accel,0,4.5) 
			end
		end

	local pr={}
		pr.secondstoarrive	= 0.5;
		pr.pos				= tracktr.HitPos + tracktr.HitNormal * 100 + self.Entity:GetForward()*self.Accel
		pr.maxangular		= 20000;
		pr.maxangulardamp	= 50;
		pr.maxspeed			= 1000000;
		pr.maxspeeddamp		= 500000;
		pr.dampfactor		= 1;
		pr.teleportdistance	= 5000;
		local fwd = self.Entity:GetForward()
		local hit = tracktr.HitNormal
		
		/* if self.LastNormal != hit:Angle() and CurTime() - self.NormalWait &gt; 1 then 
			self.LastNormal = hit:Angle()
			self.Angle = hit:Angle()
			self.NormalWait = CurTime()
		elseif CurTime() - self.NormalWait &lt; 1 then
			self.Angle = hit:Angle()
		end */

		if self.Pilot:KeyDown(IN_MOVERIGHT) then
			self.AngInc = self.AngInc + 1
			pr.pos = pr.pos + self.Entity:GetRight() * 150
		elseif self.Pilot:KeyDown(IN_MOVELEFT) then
			self.AngInc = self.AngInc - 1
			pr.pos = pr.pos - self.Entity:GetRight() * 150
		end
		
		self.Angle = hit:Angle()
		self.Angle.y = self.Entity:GetAngles().y
		self.Angle.pitch = self.Angle.pitch - 270
	//	self.Angle = self.Angle + self.Entity:GetRight() * self.AngInc
		
		pr.angle = self.Angle
	
		pr.deltatime		= deltatime
		phys:ComputeShadowControl(pr)
end

end [/lua]

I’ll have to try that.

Well, gliding for this is really more like a limited form of flying.

The main issue I’m having is making it move forward, and combining turning/handling.
I can see a lot of stuff you’re not utilizing in there. You could be smoothly changing the angles with shadowcontrol, and you shouldn’t need a trace for this.

Here’s my new code:
[lua]
function ENT:CalcSpeed()

local dSpeed = self.DesiredSpeed --This is our top speed
local cSpeed = self:GetVelocity():Length() --This is our current speed

local Accel = self.Acceleration --This is the time it takes to get there

local newSpeed = cSpeed

if (cSpeed &lt; dSpeed) then
	newSpeed = cSpeed + ( cSpeed * (Accel/dSpeed) )
else
	newSpeed = dSpeed
end


if !self.Damaged and self.Cruising then
end

return newSpeed

end

function ENT:CalcMove()

if !self.Rider then return false end

local ply = self.Rider
local doDismount = ( ply:KeyDown( IN_BACK ) and ply:KeyDown( IN_JUMP ) and true )

if doDismount then
	self:Dismount()
end

local turnDir = ( ply:KeyDown( IN_MOVELEFT ) and -1 ) or ( ply:KeyDown( IN_MOVERIGHT ) and 1 ) or 0	-- Left, right, or straight?
local glideDir = ( ply:KeyDown( IN_BACK ) and 1 ) or ( ply:KeyDown( IN_FORWARD ) and -1 ) or 0	-- Up, down, or level?

local traceGroundNorm = util.QuickTrace( self.Entity:GetPos(), self.Entity:GetPos() + vector_up * -self.Hover, { self, ply } ) -- Trace the ground

if traceGroundNorm.Hit then
	self.Cruising = true
else
	self.Gliding = true
end

local normAng = traceGroundNorm.HitNormal:Angle() or Angle(0,0,0) -- The angle of the normalized vector of the ground

// Values to clamp our rotations by
local pitchLimit = 15 + ( (self.Gliding and 60) or 0 ) -- If we are _, our _ limit will be _
local yawLimit = 45 + ( (self.Cruising and 45) or 0 ) -- If we are _, our _ limit will be _
local rollLimit = 35 + ( (self.Gliding and 55) or 0 ) -- If we are _, our _ limit will be _

local glideDeg = math.Clamp( ( self.TurnPower * glideDir ), -pitchLimit, pitchLimit ) -- Our pitch (up/down)
local turnDeg = math.Clamp( ( self.TurnPower * turnDir ), -yawLimit, yawLimit ) -- Our yaw (left/right)
local tiltDeg = math.Clamp( ( self.TurnPower * turnDir ), -rollLimit, rollLimit ) -- Our roll (left/right)

//local curAngles = self.Entity:GetAngles()
local turnAng = self.Entity:GetAngles()

if turnDir == 0 then
	turnAng = self.Entity:GetAng

// Modify our angles
turnAng:RotateAroundAxis( self.Entity:GetAngles():Forward(), glideDeg ) -- This should modify our pitch to what we want.
turnAng:RotateAroundAxis( self.Entity:GetAngles():Up(), turnDeg ) -- This should modify our yaw to what we want.
turnAng:RotateAroundAxis( self.Entity:GetAngles():Right(), tiltDeg ) -- This should modify our roll to what we want.

turnAng = turnAng + normAng

local desiredVec = turnAng:Forward() * self:CalcSpeed()
local currentVec = self.Entity:GetVelocity()

// Our newVec is what will come out as the final move vector; handlingVec is a vector added/subtracted by our current and desired vectors
local newVec, handlingVec = Vector(), Vector(1, 1, 1) * self.Handling

local currentVec, desiredVec = self.Entity:GetVelocity(), self.Entity:GetAngles():Forward()

// Here is the part that's supposed to make the machine slide when trying to turn
currentVec = currentVec - (handlingVec * turnDir)
desiredVec = desiredVec + (handlingVec * turnDir)

newVec = desiredVec
newVec.z = ( ( !self.Gliding and !self.Charging ) and traceGroundNorm.HitNormal * 5 ) or newVec.z

return newVec, turnAng

end

function ENT:PhysicsSimulate(phys, delta)

phys:Wake()

local moveVec, moveAng -- Define our values

if self.Rider then
	moveVec, moveAng = self:CalcMove() -- Set our values if we have a rider
else
	return SIM_NOTHING -- Otherwise we don't do anything
end

local speed = self:CalcSpeed()

moveVec = moveVec * speed

local newPos = (moveVec * delta) //+ self:GetVelocity():Length() * (0.5 * self.Acceleration * (delta^2)) --Our next position to move to

--print(tostring(newPos))

--self.ShadowParams.secondstoarrive = 1			// How long it takes to move to pos and rotate accordingly - only if it _could_ move as fast as it want - damping and max speed/angular will make this invalid (Cannot be 0! Will give errors if you do)
self.ShadowParams.pos = newPos 					// Where you want to move to
self.ShadowParams.angle = moveAng					// Angle you want to move to
self.ShadowParams.maxangular = 1000 			// What should be the maximal angular force applied
self.ShadowParams.maxangulardamp = 1000 		// At which force/speed should it start damping the rotation
self.ShadowParams.maxspeed = 1000 			// Maximal linear force applied
self.ShadowParams.maxspeeddamp = 1000*2			// Maximal linear force/speed before  damping
self.ShadowParams.dampfactor = 0.8 				// The percentage it should damp the linear/angular force if it reaches it's max amount
self.ShadowParams.teleportdistance = 0 			// If it's further away than this it'll teleport (Set to 0 to not teleport)
self.ShadowParams.deltatime = deltatime 		// The deltatime it should use - just use the PhysicsSimulate one

phys:ComputeShadowControl(self.ShadowParams)

end
[/lua]
I tried doing a total rewrite. It doesn’t work, but it’s getting closer.

Problems:
-It always moves to Vector(0,0,0).
-When turning, it sets the angles in comparison to one another. In other words, it changes pitch, then changes yaw in comparison to that, and then changes roll in comparison to that.
-Since it isn’t going anywhere, I can’t tell whether handling works, but I’m pretty sure it doesn’t.

Don’t mean to be nit picky, but the var on line 29 could be thrown into the if on 31, instead of making a var (even if it is local)

Right. Forgot. Did a lot of copy-pasta.

That still does not solve my problem.

I understand that I am not contributing anything to help your problem, grea$emonkey, but your OP has a typo in it.

What it is now:

What it Should be:

I am sorry for being so anal about this. I just got confused and thought I should try to fix the problem.

Actually, Low turn + low handling would be almost the same as low turn + high handling, except the first one would slide around more. There is a difference. Turning and handling are two separate things, but they act on one another to create a general concept of control.
But you’re right, the last one was a typo, not the second one.

My bad, it’s just you had low turn + high handling twice which confused me.

You win this round. Well played.

Anyways, it has started to work. I recoded it, but it is a little funky. Very hard to describe what it’s doing wrong.

[editline]02:22PM[/editline]

Thanks.