Wall stick/Gravity Controller

How can I toggle the gravity controller so seats will work

2 Likes

Using the new GitHub version; how can I add to the IgnoreList // Whitelist?

I.E. The gravity only kicks in on Parts called: “FloorPart” ?

1 Like

Thanks so much ! I was wondering if anyone had any idea how to get this to work with only focusing on the humanoidrootpart so I could use it with custom morphs? I was messing around with it for a bit, but I’m not entirely sure what parts of it I could adjust to get the script to work with anything that isn’t R15 or R6.

This controller has been a huge help in my space game, but is there anyway to bring the physics simulation server-side? I want to be able to have physics parts stick to moving objects, and it would also help the server control the player character a bit more to help prevent exploits.

How can I disable either of the modules and then enable them again?

3 Likes

Do you mean something like this?
https://gyazo.com/54a657b17fab6aef2cea5366f0807d29

3 Likes

Yes I think so. How did you do it?

1 Like

I would actually like to know how this was done too.

1 Like
  1. Edit the game in studio, Gravity Controller - Roblox
  2. Find the LocalScript in StarterCharacterScripts
  3. Uncomment the code starting from line 124:

If you press K, it will toggle the gravity controller between “normal” and “wall stick” mode.

Explanation:
The line that enables the “wall stick” mode:

Controller = GravityController.new(game.Players.LocalPlayer)

The line that disables it:

Controller:Destroy()
6 Likes

There is a problem regarding shift lock. When I turn with shift lock the character is a little delayed is there any way to fix this?

It’s b/c I have to emulate shift lock w/ a body mover. You can try going in the code and tuning the body mover, but it’ll probably always feel slightly off from standard shift lock.

2 Likes

Oh okay thanks but which script is it in? Because if that’s so is there anyway to disable it?

EgoMoose, do you see any reason why I might run into trouble replacing the gravity controller BodyGyro with an AlignOrientationConstraint?

I did mostly get what I needed from the Wallstick controller, but I wanted to see if using the gravity controller would be a better fit. I haven’t figured out yet exactly what I loose by not having the re-oriented cloned phantom physics world.

In my game, workspace.Gravity is 0 and there is custom gravity logic that places a VectorForce on any non-anchored non-massless assembly to push it where it belongs. I tied a custom UpVector callback to GravityController, and it’s working pretty good.

I also have things like grappling hooks and jetpacks which use other physics constraints to do their thing. I have run into certain bugs when the HRP with the BodyGyro is mechanically attached to another assembly by some constraint, where server and client disagree on the HRP position and the only thing that fixes them is to remove the BodyGyro.

I’m planning on hacking an AlignOrientationConstraint in it’s place, which is what I’m using on other objects that need to right themselves in the custom gravity.
Aside from the fact that it’s just more intuitive to use as a BodyGyro, is there any other shortcoming to the constraint system that led you to chose the gyro? Is there anything I’m going to be missing by using the constraint instead?

I’ll let you know how it works out, but I’d love any warnings if I’m about to do something that just won’t work.

I can’t think of any reason off the top of my head that the orientation constraint wouldn’t work. I probably chose to use a gyro b/c it’s faster to instance as opposed to creating 2 attachments + the constraint.

1 Like

I’m having a bit of an issue with the Wallstick controller. Everything is vanilla, and I’m even using the unmodified “Boots” module from the test place. I’d like to add a modification to the system that allows for a constant acceleration to everyone’s HumanoidRootParts, and I can’t quite get it working right because in Wallstick generalStep() you always set the velocity back to 0 before adding on the Physics.HRP.Velocity. I don’t quite understand how I would add this acceleration to Physics.HRP.Velocity since the desired acceleration is a world coordinate, and it seems like Physics.HRP.Velocity very much isn’t… How would I go about accomplishing this? Got any tips?

Swapping to the align constraint in the gravity controller from the gyro was pretty straightforward and solved some of my problems.

I had already solved some of them by switching to instantiating the parts on the server for replication purposes, but the gyro just didn’t want to play along.

In my previous vehicle gyros, I parented the attachment1 to workspace.Terrain, modifying the attachment.WorldAxis. Working on a hunch it will replicate and synchronize better, for the gravity controller gyro replacement, I created a align target part on the server and give ownership to the player.

I actually like this better because it also provides a visualization of what the alignment is trying to do. I’ll try to post some video later so I can ask the follow up question.


I just left the attachment in the proxy Part instance at zero rotation and orient the part cframe as the gyro cframe would have been.

Code, if you’re curious what I did.
Swap out gyro for align using proxy part ended up being a clean change:


	local vForce = Instance.new("VectorForce")
	vForce.Force = Vector3.new(0, 0, 0)
	vForce.ApplyAtCenterOfMass = true
	vForce.RelativeTo = Enum.ActuatorRelativeTo.World
	vForce.Attachment0 = attach
	vForce.Parent = hrp

	local gyro = nil --[[Instance.new("BodyGyro")
	gyro.P = 25000
	gyro.MaxTorque = Vector3.new(100000, 100000, 100000)
	gyro.CFrame = hrp.CFrame
	gyro.Parent = hrp]]

	sphere.Parent = model
	floor.Parent = model
	floor2.Parent = model

	model.Name = colliderName
	model.Parent = character
	
	local alignBlock = Instance.new("Part")
	alignBlock.Name = player.Name .. "_OrientationTarget"
	alignBlock.CanCollide = false
	alignBlock.Massless = true
	alignBlock.Transparency = 0.5
	local face = Instance.new("SurfaceSelection", alignBlock)
	face.Adornee = alignBlock
	face.TargetSurface = Enum.NormalId.Front
	local attAlignTarget = Instance.new("Attachment", alignBlock)
	attAlignTarget.Name = player.Name .. "_OrientationAttachment"
	local align = Instance.new("AlignOrientation")
	align.Name = player.Name .. "_AlignOrientation"
	align.Attachment0 = attach
	align.Attachment1 = attAlignTarget
	align.PrimaryAxisOnly = false	
	align.MaxTorque = 100000
	align.Responsiveness = 60
	alignBlock.Parent = character
	align.Parent = hrp
	alignBlock:SetNetworkOwner(player)
	--alignBlock.Anchored = true

So the cframe that was driving the gyro can go to the proxy part instead:

function ColliderClass:Update(force, cframe)
	self.VForce.Force = force
	if self.Gyro then
		self.Gyro.CFrame = cframe
	end
	if self.AlignBlock then
		self.AlignBlock.CFrame = cframe
	end
end

I suspect that animating the proxy part cframe every heartbeat is not going to always look the best to the server and other clients. I think long term I’m either going to tween updates based on a delta epsilon. Or use another align constraint to gently control the orientation of the proxy part if that replicates better.
It actually is starting to look to me like I’m heading toward needing a custom client prediction layer so player movement is smoother and more consistent from the perspective of the server and other clients.

For now, I just added HRP position to the manual orientation cframe animation your stock gravity controller does:

local function onGravityStep(self, dt)
	-- no changes until the end:
	local charRotation = CFrame.new(self.HRP.Position) * newCharRotation * newCharCF

	self.StateTracker:Update(self._gravityUp, self._collider:IsGrounded(false), isInputMoving)
	self._collider:Update(walkForce + gForce, charRotation)
end

Anyway, it works mostly. :slight_smile:
Thanks for all the amazing work you’ve done with character controllers.
When I get videos up to show what is not quite working, I’ll have to ask for some advice.

Gratuitous details
To minimize changes to your GravityController, I created a factory module to allow the collider to be created on server or client. I’ll try to push it up to my github fork later.
image

GravityController keeps a reference to it:

local Collider = require(script:WaitForChild("Collider"))
local StateTracker = require(script:WaitForChild("StateTracker"))
local ColliderPartFactory = require(script:WaitForChild("ColliderPartFactory"))

and lower down

-- mgm ServerInstanceCreate Refactor
GravityControllerClass.ColliderPartFactory = ColliderPartFactory

Collider has:

--[[ mgm ServerInstanceCreate Refactor : ]]
function ColliderClass.CreateColliderModel(gravCollider, gravController)
	local player = gravController.Player
	
	local model, sphere, vForce, floor, floor2, gyro, alignBlock, format 
		= table.unpack(gravController.ColliderPartFactory.GetOrCreateColliderModel(player))

In the gravity control tool:

local GravityControllerClass = require(ReplicatedStorage:WaitForChild("GravityController"))
local gravityController = nil

GravityControllerClass.ColliderPartFactory.GetOrCreateColliderModel = function(player)
	ArkClient.SendServerMessage("CreatePlayerGravityCollider", Players.LocalPlayer)
	--GravityControllerClass.ColliderPartFactory.CreateColliderModelParts(player)
	return GravityControllerClass.ColliderPartFactory.GetModelFromServer(player)
end

To override the behavior to send a message to the server and wait.

The actual model creation function can be called on client or server, but server works for my purposes.
ColliderPartFactory:


function ColliderPartFactory.CreateColliderModelParts(player)
	local humanoid, hrp, character = WaitForPlayerHumanoidAndRootPart(player)
	local hipHeight = getHipHeight(humanoid)
	local attach = getAttachement(humanoid, hrp)
	
	local colliderName = "Collider"
	
	if character:FindFirstChild(colliderName)~=nil then
		return ColliderPartFactory.WaitForColliderModelParts(character, colliderName, hrp)
	end

	local model = Instance.new("Model")
	local sphere = Instance.new("Part")
	sphere.Name = "Sphere"
	sphere.Massless = true
	sphere.Size = Vector3.new(2, 2, 2)
	sphere.Shape = Enum.PartType.Ball
	sphere.Transparency = 1
	sphere.CustomPhysicalProperties = CUSTOM_PHYSICAL

	local floor = Instance.new("Part")
	floor.Name = "FloorDectector"
	floor.CanCollide = false
	floor.Massless = true
	floor.Size = Vector3.new(2, 1, 1)
	floor.Transparency = 1

	local floor2 = Instance.new("Part")
	floor2.Name = "JumpDectector"
	floor2.CanCollide = false
	floor2.Massless = true
	floor2.Size = Vector3.new(2, 0.2, 1)
	floor2.Transparency = 1

	local weld = Instance.new("Weld")
	weld.C0 = CFrame.new(0, -hipHeight, 0.1)
	weld.Part0 = hrp
	weld.Part1 = sphere
	weld.Parent = sphere

	local weld = Instance.new("Weld")
	weld.C0 = CFrame.new(0, -hipHeight - 1.5, 0)
	weld.Part0 = hrp
	weld.Part1 = floor
	weld.Parent = floor

	local weld = Instance.new("Weld")
	weld.C0 = CFrame.new(0, -hipHeight - 1.1, 0)
	weld.Part0 = hrp
	weld.Part1 = floor2
	weld.Parent = floor2

	local vForce = Instance.new("VectorForce")
	vForce.Force = Vector3.new(0, 0, 0)
	vForce.ApplyAtCenterOfMass = true
	vForce.RelativeTo = Enum.ActuatorRelativeTo.World
	vForce.Attachment0 = attach
	vForce.Parent = hrp

	local gyro = nil --[[Instance.new("BodyGyro")
	gyro.P = 25000
	gyro.MaxTorque = Vector3.new(100000, 100000, 100000)
	gyro.CFrame = hrp.CFrame
	gyro.Parent = hrp]]

	sphere.Parent = model
	floor.Parent = model
	floor2.Parent = model

	model.Name = colliderName
	model.Parent = character
	
	local alignBlock = Instance.new("Part")
	alignBlock.Name = player.Name .. "_OrientationTarget"
	alignBlock.CanCollide = false
	alignBlock.Massless = true
	alignBlock.Transparency = 0.5
	local face = Instance.new("SurfaceSelection", alignBlock)
	face.Adornee = alignBlock
	face.TargetSurface = Enum.NormalId.Front
	local attAlignTarget = Instance.new("Attachment", alignBlock)
	attAlignTarget.Name = player.Name .. "_OrientationAttachment"
	local align = Instance.new("AlignOrientation")
	align.Name = player.Name .. "_AlignOrientation"
	align.Attachment0 = attach
	align.Attachment1 = attAlignTarget
	align.PrimaryAxisOnly = false	
	align.MaxTorque = 100000
	align.Responsiveness = 60
	alignBlock.Parent = character
	align.Parent = hrp
	alignBlock:SetNetworkOwner(player)
	--alignBlock.Anchored = true

	return {
		model,
		sphere,
		vForce,
		floor,
		floor2,
		gyro,
		alignBlock,
		"model,sphere,vForce,floor,floor2,gryo,alignBlock",
	}
end
2 Likes

Nevermind, I fixed the issue.

For some reason anytime I changed the physics HRP velocity directly, it would only change on the Y axis. This was really annoying, but I found I could get around it by putting bodyvelocities and bodyforces in the physics HRP itself. I could disable these within the :Set function so they didn’t interfere with the wallstick physics.

The HRP, or the physics world HRP?

I didn’t get good results on the standard chatter model HRP.

The physics world one works with all sorts of body movers just fine, you just need to make sure you disable them anytime you want the wallstick behavior to work properly.

Using constraints, I did not have to disable them. My gravity is 100% artificial, so the physics world HRP would just float in space if the VectorForce was disabled.

I just had to translate the vector to physics world space.