Planetary Gravity on Players

The Issue:
I want a gravity system where players are able to walk around a roughly spherical object perpendicular to the center of the object, with their cameras rotating with the player as well, like the game Solar System Exploration 2.

What I’ve tried:
EgoMoose’s custom character: Suppose I create a planet with a mountain, Egomoose’s module will make my character latch onto the angled terrain itself, plus my character will also latch onto everything remotely angled.


(Excuse my art skills)
The yellow brick represents the character, and the red line represents the direction the feet are facing down on.
This image shows what EgoMoose’s module does, the character will latch onto any sort of angle. I want my character to be walking perpendicular to the center of the planet, not the ground.

4 Likes

Haven’t dealt with that sort of thing before, but you could possibly use a combination of AlignPosition (to get the player to stick to the surface), as well as a constantly updating AlignOrientation to position them parralel to the normal.

1 Like

Couldn’t you just CFrame the players feet to point at the center of the sphere? I’m guessing you could also give them a VectorForce that always pushes them to the center of the sphere so if they jump off at any weird angle they won’t fall off the planet.

1 Like

I’ve modified EgoMoose’s Gravity Controller before, and added a zone like system. You can find the part where it controls the pushing direction (UpVector), and just make it return the zone’s CorePoint looking at the user (CFrame.lookAt, and the lookvector of it) when they’re in the zone.

2 Likes

Could you provide more information about that with a video to showcase?

1 Like

Blind guess …

local spherePosition = Vector3.new(0, 10, 0)
local sphereRadius = 20

local function calculateGravityDirection(player)
    local playerPosition = player.Character.HumanoidRootPart.Position
    local direction = (spherePosition - playerPosition).unit
    return direction
end

game:GetService("RunService").Heartbeat:Connect(function()
    for _, player in pairs(game:GetService("Players"):GetPlayers()) do
        local character = player.Character
        if character then
            local humanoid = character:FindFirstChild("Humanoid")
            if humanoid then
                humanoid.WalkToPoint = character.HumanoidRootPart.Position + calculateGravityDirection(player) * sphereRadius
                humanoid:Move(Vector3.new(0, 0, 0), false)
                humanoid:Move(Vector3.new(0, 0, 0), true)
                character:SetPrimaryPartCFrame(CFrame.new(spherePosition, player.Character.HumanoidRootPart.Position))
            end
        end
    end
end)
1 Like

Here’s a video of my modified variant of EgoMoose’s Gravity Controller, to have zones.

1 Like

Ah, thanks! Could you give me a tutorial on how you modified it?

In the local script of the gravity controller, I set up a table at the top

local ValueTable = {
["Part"] = nil;
["DownFromPart"] = true;
["PullTowardsMiddle"] = false;
["StickOnThings"] = false;
}

Which updated everytime the player would enter a zone (using GetPartsInPart with an overlayparam). These are properties that are in a folder, which is inside of the zone, which states its purpose.
in the function “GetGravityUp” I had checks for the StickOnThings, which is just to not allow it to Stick to things, if it’s false.
ValueTable is also in the module’s “self” storage, so it can be read.
in the Module, there’s a function by the title OnGravityStep, where I did this

local oldGravity = Vector3.new(0,1,0)
	if self.ValueTable then
		-- get the normal
		if self.Character.HumanoidRootPart then
			if self.ValueTable["DownFromPart"] == true then
if self.ValueTable["Part"] ~= nil then
				oldGravity = self.ValueTable["Part"].CFrame.UpVector
else
oldGravity = Vector3.new(0,1,0)
end
			end
			if self.ValueTable["DownFromPart"] == false then
				if self.ValueTable["PullTowardsMiddle"] == true then
if self.ValueTable["Part"] ~= nil then
					oldGravity = (CFrame.lookAt(self.ValueTable["Part"].Position, self.Character.Torso.Position).LookVector)
else
oldGravity = Vector3.new(0,1,0)
end
				end
			end
		end
	end

That’s basically all I did. Write it your own way however, as I made this a while back, and it’s old. (and unclean)

1 Like

I tried modifying using your instructions, but instead of GetPartsInPart, I used ZonePlus to detect a player’s presence inside a zone instead. The modified script did not work.

Here’s the file to see what’s going on:
Gravity Controller 2.0.rbxl (142.6 KB)

Are you also setting up the self.ValueTable?
Look at the getgravityup function to see how self is used there and just set self.ValueTable to ValueTable

Yes, i’ve set it up here:


Here’s GetGravityUp in the local script, the ValueTable is already set as ValueTable.

With the modification, the gravity controller functions like it did before modifying.

Also, how did you get the camera to orientate with the character?

I had an issue with the camera prior. You may be using an older version of Gravity Controller.

Which version did you use? I’m using this open one: Gravity Controller - Roblox

I know something’s wrong here, because your zone is set, yet you’re not immediately pulled. It’s better off for you to figure out the end point for yourself, but you’re not setting up the gravity upwards direction correctly, at least by the look of it. You’re still sticking to things, because you haven’t closed in the raycast.

Here’s how my GravityUp looks like.

local Character = game.Players.LocalPlayer.Character or game.Players.LocalPlayer.CharacterAdded


function GetGravityUp(self, oldGravityUp)



self.ValueTable = ValueTable
	local ignoreList = {}
	for i, player in next, PLAYERS:GetPlayers() do
		ignoreList[i] = player.Character
	end
	
	-- get the normal
	
	local hrpCF = self.HRP.CFrame
	local isR15 = (self.Humanoid.RigType == Enum.HumanoidRigType.R15)
	
	local origin = isR15 and hrpCF.p or hrpCF.p + 0.35*oldGravityUp
	local radialVector = math.abs(hrpCF.LookVector:Dot(oldGravityUp)) < 0.999 and hrpCF.LookVector:Cross(oldGravityUp) or hrpCF.RightVector:Cross(oldGravityUp)
	
	local centerRayLength = 25
	local centerRay = Ray.new(origin, -centerRayLength * oldGravityUp)
	local centerHit, centerHitPoint, centerHitNormal = workspace:FindPartOnRayWithIgnoreList(centerRay, ignoreList)
	
	--DrawClass:Clear()
	--DrawClass.Draw3D.Ray(centerRay.Origin, centerRay.Direction)
	
	local downHitCount = 0
	local totalHitCount = 0
	local centerRayHitCount = 0
	local evenRayHitCount = 0
	local oddRayHitCount = 0
	
	local mainDownNormal = ZERO
	if (centerHit) then
		mainDownNormal = centerHitNormal
		centerRayHitCount = 0
	end
	
	local downRaySum = ZERO
	for i = 1, NUM_DOWN_RAYS do
		local dtheta = PI2 * ((i-1)/NUM_DOWN_RAYS)
		
		local angleWeight = 0.25 + 0.75 * math.abs(math.cos(dtheta))
		local isEvenRay = (i%2 == 0)
		local startRadius = isEvenRay and EVEN_DOWN_RAY_START_RADIUS or ODD_DOWN_RAY_START_RADIUS	
		local endRadius = isEvenRay and EVEN_DOWN_RAY_END_RADIUS or ODD_DOWN_RAY_END_RADIUS
		local downRayLength = centerRayLength
		
		local offset = CFrame.fromAxisAngle(oldGravityUp, dtheta) * radialVector
		local dir = (LOWER_RADIUS_OFFSET * -oldGravityUp + (endRadius - startRadius) * offset)
		local ray = Ray.new(origin + startRadius * offset, downRayLength * dir.unit)
		local hit, hitPoint, hitNormal = workspace:FindPartOnRayWithIgnoreList(ray, ignoreList)
		
		--DrawClass.Draw3D.Ray(ray.Origin, ray.Direction)

		if (hit) then
			if hit.CanCollide == true then
			if ValueTable["StickOnThings"] == true then
			downRaySum = downRaySum + angleWeight * hitNormal
			downHitCount = downHitCount + 1
			if isEvenRay then
				evenRayHitCount = evenRayHitCount + 1					
			else
				oddRayHitCount = oddRayHitCount + 1
					end
				end
			end
		end
	end
	
	local feelerHitCount = 0	
	local feelerNormalSum = ZERO
	
	for i = 1, NUM_FEELER_RAYS do
		local dtheta = 2 * math.pi * ((i-1)/NUM_FEELER_RAYS)
		local angleWeight =  0.25 + 0.75 * math.abs(math.cos(dtheta))	
		local offset = CFrame.fromAxisAngle(oldGravityUp, dtheta) * radialVector
		local dir = (FEELER_RADIUS * offset + LOWER_RADIUS_OFFSET * -oldGravityUp).unit
		local feelerOrigin = origin - FEELER_APEX_OFFSET * -oldGravityUp + FEELER_START_OFFSET * dir
		local ray = Ray.new(feelerOrigin, FEELER_LENGTH * dir)
		local hit, hitPoint, hitNormal = workspace:FindPartOnRayWithIgnoreList(ray, ignoreList)
		
		--DrawClass.Draw3D.Ray(ray.Origin, ray.Direction)
		
		if (hit) then
			if hit.CanCollide == true then
			if ValueTable["StickOnThings"] == true then
			feelerNormalSum = feelerNormalSum + FEELER_WEIGHTING * angleWeight * hitNormal --* hitDistSqInv
			feelerHitCount = feelerHitCount + 1
		end
			end
		end
	end
	
	if (centerRayHitCount + downHitCount + feelerHitCount > 0) then
		local normalSum = mainDownNormal + downRaySum + feelerNormalSum
		if (normalSum ~= ZERO) then
			return normalSum.unit
		end
	end
	
	return oldGravityUp
end

Controller.GetGravityUp = GetGravityUp

If it still doesn’t work, this presumably means your Gravity Controller is an outdated one compared to mine. I dont remember where I got my variant of it, but it has the working camera.

1 Like

Could you provide me a file to the place you showcased it in?
I’m struggling to find the right version.

I’ve figured it out.
If anyone wants it:
Gravity.rbxl (1.2 MB)

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.