Custom Movement (again)

Okay, basic idea here is controlling a player’s movement by parenting them to an entity which uses custom physics. The problem is my last several attempts at custom physics failed miserably.

I need to know how to do a few things:
[ul]Interpolated path of motion between 2 points.

http://img401.imageshack.us/img401/971/diagram1.png

[/ul]
[ul]Movement limited to one axis.

http://img169.imageshack.us/img169/4710/diagram2.png

[/ul]
[ul]Movement limited to an axis normal to a surface.

http://img169.imageshack.us/img169/7720/diagram3.png

[/ul]
Can someone write me a quick example script so I can get it to work properly. I haven’t a clue how to do any of these.

Well I would think you could…
Umm…

For the last one, you could force the client to move himself toward the wall at all times based on his eye trace, but that would be a really shitty way to do it. (ie. +backward if the client is looking inside a range of angles from the wall)

Parent the player to the entity, run a trace under the entity and set angles relative to it’s hit normal, while running movement calculations?

Ahg. The difficult thing I see here would be to run movement calculations for the player once on a surface greater than 45 degree’s.

Ah! movement limited to 1 axis, you say?

http://dl.getdropbox.com/u/99862/HatCatcher.zip

Remember that?

While the camera/controls may have been funky, locking the player to a path worked fine.

I remember that! :xd: That was fun to judge.

So what about the other ones?

[editline]03:26PM[/editline]

Your code is interesting. Do you have a node path in the map, or are you creating nodes on the fly? I don’t want to have to place nodes for every wall in my map or create more than I need to.

I have a node path in the map, but you can easily place them dynamically if you so desire. It just Lerp’s the player’s view and movement between the two points. (All the confusing movement code is primarily to TRY to get the camera to not fuck up. :S)

Could you post what restricts them to an axis?

When you use Lerp for movement, it’s just a constant loop of SetPos. The game automatically smooths the movement between the points.

I just want to see a pseudo-function so I have some idea.

[lua]

local gravity = Vector(0,0,-160)
function GM:SetupMove(ply, data)
local fwdvec = ply:GetAngles():Forward()
ply.VelocityMod = Vector(0,0,0)
–[[if ply:KeyDown(IN_BACK) and (not ply.CanAddAngle or ply.CanAddAngle == true) then
if not ply.ReverseTimerTick then ply.ReverseTimerTick = 0 end
ply.CanAddAngle = true
ply.ReverseNodeChanged = true
ply.ReverseTimerTick = ply.ReverseTimerTick + 1
if ply.ReverseTimerTick >= 80 then
ply.ReverseTimerTick = 0
ply.CanAddAngle = false
end
end]]
data:SetForwardSpeed(0)
if ply:KeyDown(IN_MOVERIGHT) then
data:SetSideSpeed(0)
if ply.LeftIsForward != true then
if (ply.Reversed != true or ply.ReverseNodeChanged == true) then
if (ply:KeyDown(IN_SPEED)) then
ply.VelocityMod = ply.VelocityMod + (fwdvec440)
data:SetForwardSpeed(440)
else
ply.VelocityMod = ply.VelocityMod + (fwdvec
220)
data:SetForwardSpeed(220)
end
else
ply.ReverseNodeChanged = true
if (ply:KeyDown(IN_SPEED)) then
ply.VelocityMod = ply.VelocityMod + (fwdvec*-440)
data:SetForwardSpeed(-440)
else
ply.VelocityMod = ply.VelocityMod + (fwdvec*-220)
data:SetForwardSpeed(-220)
end
end
else
if (ply.Reversed == true or ply.ReverseNodeChanged == true) then
if (ply:KeyDown(IN_SPEED)) then
ply.VelocityMod = ply.VelocityMod + (fwdvec440)
data:SetForwardSpeed(440)
else
ply.VelocityMod = ply.VelocityMod + (fwdvec
220)
data:SetForwardSpeed(220)
end
else
ply.ReverseNodeChanged = true
if (ply:KeyDown(IN_SPEED)) then
ply.VelocityMod = ply.VelocityMod + (fwdvec*-440)
data:SetForwardSpeed(-440)
else
ply.VelocityMod = ply.VelocityMod + (fwdvec*-220)
data:SetForwardSpeed(-220)
end
end
end
elseif ply:KeyDown(IN_MOVELEFT) then
data:SetSideSpeed(0)
if ply.LeftIsForward == true then
if (ply.Reversed != true or ply.ReverseNodeChanged == true) then
if (ply:KeyDown(IN_SPEED)) then
ply.VelocityMod = ply.VelocityMod + (fwdvec440)
data:SetForwardSpeed(440)
else
ply.VelocityMod = ply.VelocityMod + (fwdvec
220)
data:SetForwardSpeed(220)
end
else
ply.ReverseNodeChanged = true
if (ply:KeyDown(IN_SPEED)) then
ply.VelocityMod = ply.VelocityMod + (fwdvec*-440)
data:SetForwardSpeed(-440)
else
ply.VelocityMod = ply.VelocityMod + (fwdvec*-220)
data:SetForwardSpeed(-220)
end
end
else
if (ply.Reversed == true or ply.ReverseNodeChanged == true) then
if (ply:KeyDown(IN_SPEED)) then
ply.VelocityMod = ply.VelocityMod + (fwdvec440)
data:SetForwardSpeed(440)
else
ply.VelocityMod = ply.VelocityMod + (fwdvec
220)
data:SetForwardSpeed(220)
end
else
ply.ReverseNodeChanged = true
if (ply:KeyDown(IN_SPEED)) then
ply.VelocityMod = ply.VelocityMod + (fwdvec*-440)
data:SetForwardSpeed(-440)
else
ply.VelocityMod = ply.VelocityMod + (fwdvec*-220)
data:SetForwardSpeed(-220)
end
end
end
end
if (ply:KeyPressed(IN_BACK)) then
ply:ConCommand("+duck")
end
if (ply:KeyPressed(IN_FORWARD)) then
ply:ConCommand("+jump")
end
if (ply:KeyReleased(IN_BACK)) then
ply:ConCommand("-duck")
end
if (ply:KeyReleased(IN_FORWARD)) then
ply:ConCommand("-jump")
end
if ply:KeyDown(IN_JUMP) and ((ply:GetGroundEntity():IsValid() == true or ply:GetGroundEntity():IsWorld() == true) or ply.StillJumping == true) then
if not ply.JumpTimerTick then ply.JumpTimerTick = 0 end
ply.StillJumping = true
if ply.StillJumping == true then ply.JumpTimerTick = ply.JumpTimerTick + 1 end
if ply.JumpTimerTick >= 60 then
ply.StillJumping = false
ply.JumpTimerTick = 0
return false
end
if ply.JumpTimerTick >= 1 then ply.VelocityMod = ply.VelocityMod + Vector(0,0,ply:GetJumpPower()+70) end
end
if ply:KeyDown(IN_DUCK) then
ply.VelocityMod = (fwdvec*(ply.VelocityMod:Length()/2)) + ply.VelocityMod:Normalize()
end
data:SetVelocity(gravity+ply.VelocityMod)
return true
end

[/lua]

And as for think:
[lua]
function GM:PlayerThink(ply)
if ply:IsValid() and ply:Alive() then
if ply.viewer and ply.viewer:IsValid() then
if ply.FlippedNodes and ply.FlippedNodes == true then
ply.FlippedNodes = false
ply.viewer.NextNode,ply.viewer.LastNode = ply.viewer.LastNode,ply.viewer.NextNode
end
if ply.viewer.NextNode and ply.NodeChanged and ply.NodeChanged == true then
ply.NodeChanged = false
if ply.Reversed and ply.Reversed == true then
ply.viewer.LastNode = ply.viewer.CurrentNode
ply.viewer.CurrentNode = ply.viewer.NextNode
if GetPathEntityByTarget(ply.viewer.CurrentNode:GetName()) then
ply.viewer.NextNode = GetPathEntityByTarget(ply.viewer.CurrentNode:GetName())
else
ply.viewer.NextNode = self.ViewStart
end
else
ply.viewer.LastNode = ply.viewer.CurrentNode
ply.viewer.CurrentNode = ply.viewer.NextNode
if ply.viewer.CurrentNode[“target”] and ply.viewer.CurrentNode[“target”] != “” then
ply.viewer.NextNode = ents.FindByName(ply.viewer.CurrentNode[“target”])[1]
else
ply.viewer.NextNode = self.ViewEnd
end
end
elseif ply.viewer.LastNode and ply.viewer.LastNode:GetName() != ply.viewer.CurrentNode:GetName() and ply.ReverseNodeChanged and ply.ReverseNodeChanged == true then
ply.viewer.NextNode,ply.viewer.CurrentNode = ply.viewer.CurrentNode,ply.viewer.NextNode
ply.viewer.LastNode = GetPathEntityByTarget(ply.viewer.CurrentNode:GetName())
end
end

	if ply.NextNode and ply:GetPos():Distance(ply.NextNode:GetPos()) <= 50 then --value subject to change.
		if ply.Reversed and ply.Reversed == true then
			ply.LastNode = ply.CurrentNode
			ply.CurrentNode = ply.NextNode
			if GetPathEntityByTarget(ply.CurrentNode:GetName()) then
				ply.NextNode = GetPathEntityByTarget(ply.CurrentNode:GetName())
			else
				ply.NextNode = self.PlayerStart
				ply:Lock()
				if (not ply.Finished) or ply.Finished != true then
					ply.Finished = true
					self:ReturnedToStart(ply)
				end
			end
		else
			ply.LastNode = ply.CurrentNode
			ply.CurrentNode = ply.NextNode
			if ply.CurrentNode["target"] and ply.CurrentNode["target"] != "" then
				ply.NextNode = ents.FindByName(ply.CurrentNode["target"])[1]
			else
				ply.NextNode = self.PlayerEnd
				ply:Lock()
				if (not ply.Finished) or ply.Finished != true then
					ply.Finished = true
					self:FinnishedLevel(ply)
				end
			end
		end
		ply:SetPos(Vector(ply.CurrentNode:GetPos().x,ply.CurrentNode:GetPos().y,ply:GetPos().z+5))
		ply.NodeChanged = true
	end
	if (ply.ReverseNodeChanged == true) then
		ply.ReverseNodeChanged = false
		ply.NextNode,ply.CurrentNode = ply.CurrentNode,ply.NextNode
		ply.LastNode = GetPathEntityByTarget(ply.CurrentNode:GetName())
		if ply.Reversed then
			ply.Reversed = !ply.Reversed
		else
			ply.Reversed = true
		end
	end
	
	if ply.Reversed == true and ply.pitch then
		ply:SetEyeAngles(Angle(math.Clamp(math.NormalizeAngle(tonumber(ply.pitch) + 180)*-1,-60,50),(ply.NextNode:GetPos()-ply:GetPos()):Angle().y,0))
	elseif ply.pitch then
		ply:SetEyeAngles(Angle(math.Clamp(tonumber(ply.pitch),-60,50),(ply.NextNode:GetPos()-ply:GetPos()):Angle().y,0))
	else
		ply:SetEyeAngles(Angle(ply:EyeAngles().p,(ply.NextNode:GetPos()-ply:GetPos()):Angle().y,0))
	end
	
	if ply.viewer and ply.viewer:IsValid() then
		if ply.FlippedNodes and ply.FlippedNodes == true then
			ply.FlippedNodes = false
			ply.viewer.NextNode,ply.viewer.LastNode = ply.viewer.LastNode,ply.viewer.NextNode
		end
		if ply.viewer.NextNode and ply.NodeChanged and ply.NodeChanged == true then
			ply.NodeChanged = false
			if ply.Reversed and ply.Reversed == true then
				ply.viewer.LastNode = ply.viewer.CurrentNode
				ply.viewer.CurrentNode = ply.viewer.NextNode
				if GetPathEntityByTarget(ply.viewer.CurrentNode:GetName()) then
					ply.viewer.NextNode = GetPathEntityByTarget(ply.viewer.CurrentNode:GetName())
				else
					ply.viewer.NextNode = self.ViewStart
				end
			else
				ply.viewer.LastNode = ply.viewer.CurrentNode
				ply.viewer.CurrentNode = ply.viewer.NextNode
				if ply.viewer.CurrentNode["target"] and ply.viewer.CurrentNode["target"] != "" then
					ply.viewer.NextNode = ents.FindByName(ply.viewer.CurrentNode["target"])[1]
				else
					ply.viewer.NextNode = self.ViewEnd
				end
			end
		elseif ply.viewer.LastNode and ply.viewer.LastNode:GetName() != ply.viewer.CurrentNode:GetName() and ply.ReverseNodeChanged and ply.ReverseNodeChanged == true then
			ply.viewer.NextNode,ply.viewer.CurrentNode = ply.viewer.CurrentNode,ply.viewer.NextNode
			ply.viewer.LastNode = GetPathEntityByTarget(ply.viewer.CurrentNode:GetName())
		end
	end
	
	if (ply.NextNode:EntIndex() != self.PlayerStart:EntIndex()) then
		ply.DistFrac = math.Clamp(ply:GetPos():Distance(ply.CurrentNode:GetPos())/ply.NextNode:GetPos():Distance(ply.CurrentNode:GetPos()),0,1)
	elseif ply.Reversed != true or (ply.NextNode:EntIndex() == self.PlayerStart:EntIndex()) then
		ply.DistFrac = math.Clamp(1-ply:GetPos():Distance(ply.CurrentNode:GetPos())/ply.NextNode:GetPos():Distance(ply.CurrentNode:GetPos()),0,1)
	else
		ply.DistFrac = math.Clamp(ply:GetPos():Distance(ply.CurrentNode:GetPos())/ply.NextNode:GetPos():Distance(ply.CurrentNode:GetPos()),0,1)
	end
	ply.viewer:SetPos(LerpVector(ply.DistFrac,ply.viewer.CurrentNode:GetPos(),ply.viewer.NextNode:GetPos()))
	ply.viewer:PointAtEntity(ply)
	ply:SetViewEntity(ply.viewer)
	ply:SetNWVector("viewer_pos",ply.viewer:GetPos())
	ply:SetNWAngle("viewer_ang",ply.viewer:GetAngles())
end

--print(math.NormalizeAngle(ply:GetAngles():Right():Angle().y+90) > math.NormalizeAngle(ply.viewer:GetAngles().y))
if math.NormalizeAngle(ply:GetAngles():Right():Angle().y) > math.NormalizeAngle(ply.viewer:GetAngles().y) then 
	ply.LeftIsForward = true
else
	ply.LeftIsForward = false	
end

end

[/lua]

Think is dealing entirely with viewing positions/angles (the viewer is just set to look at the player) and player angles.
SetupMove is dealing with the player’s position. The stuff I was having trouble with was any variables like “LeftIsForward” or one with “Reverse” in it. (It’s hard figuring out weather the player is facing left or right one someone’s screen when the camera could be anywhere, the player and camera can be on any axises, and they can be on different axises to boot. I spent FOREVER trying to fix it. Didn’t work.)

Is all of that really necessary? I just wanted some bare bones stuff. :xd:

I’m positive you can find how the fellows at Noxious Net managed to make a side-scrolling gamemode like this one:

http://www.garrysmod.org/img/?t=dll&id=76122

I did a cursory search of the code and couldn’t find anything meaningful, but who knows.

I don’t want a side-scroller though. :v:

I’ve been messing around with GM:Move() lately, with no luck at all.

Movement limited to one axis? MarioBoxes uses nodraw brushes I think.

Just look at one of the maps.

Well, I need to be able to change the axis.

Not quite sure what you meen, but maybe you want to look into projection.

[editline]06:42PM[/editline]

But that’s two axises:tinfoil:

I don’t know…perhaps you can use variables and if it’s the time you want them to go through or whatever a certain axis then set it to 1 and else make them collide with the brush or something…

Well, right now I’ve gotten almost everything working. I just need to make a way to stop the player from continuing to go up or to the side when a wall is about to end. Are traces a good way to do that?

[editline]12:46PM[/editline]

Or is there a less expensive way?