Modified GravityController by EgoMoose breaks upon respawn

Hi all,

I’m using a modified version of EgoMoose’s gravity controller where the center of gravity can change based on what “zone” you are in. I found this in this forum.

Problem is, upon respawn, the gravity controller seems to break entirely.

This does not occur in EgoMoose’s demo game here.

I’m unsure what code is causing the problem, but here are the primary code snippets:

Primary Gravity Controller by EgoMoose

image
The code below is from the GravityController module script.

local RunService = game:GetService("RunService")

local Utility = script:WaitForChild("Utility")
local CharacterModules = script:WaitForChild("CharacterModules")

local Maid = require(Utility:WaitForChild("Maid"))
local Signal = require(Utility:WaitForChild("Signal"))
local Camera = require(CharacterModules:WaitForChild("Camera"))
local Control = require(CharacterModules:WaitForChild("Control"))
local Collider = require(script:WaitForChild("Collider"))
local StateTracker = require(script:WaitForChild("StateTracker"))

-- CONSTANTS

local TRANSITION = 0.15
local WALK_FORCE = 200 / 3
local JUMP_MODIFIER = 1.2

local ZERO3 = Vector3.new(0, 0, 0)
local UNIT_Y = Vector3.new(0, 1, 0)

-- Class

local GravityControllerClass = {}
GravityControllerClass.__index = GravityControllerClass
GravityControllerClass.ClassName = "GravityController"

local CreatedControllers = {}

-- Public Constructors

function GravityControllerClass.new(player)
	local self = setmetatable({}, GravityControllerClass)
	self.Owner = player
	
	self.GravityPart = nil
	
	self.Player = player
	self.Character = player.Character
	self.Humanoid = player.Character:WaitForChild("Humanoid")
	self.HRP = self.Humanoid.RootPart

	self._gravityUp = UNIT_Y
	self._characterMass = 0

	self._camera = Camera.new(self)
	self._control = Control.new(self)
	self._collider = Collider.new(self)

	self._fallStart = self.HRP.Position.y
	self._prevPart = workspace.Terrain
	self._prevCFrame = CFrame.new()

	self.StateTracker = StateTracker.new(self)
	self.Maid = Maid.new()

	init(self)
	
	-- Add controller to the CreatedControllers table
	CreatedControllers[self.Owner] = self

	return self
end

function GravityControllerClass:GetController(Player) -- Get the specific tool by using the player
	print(CreatedControllers)
	if CreatedControllers[Player] then
		return CreatedControllers[Player]
	else
		print("Controller not found")
	end
end

-- Private Methods

local function getRotationBetween(u, v, axis)
	local dot, uxv = u:Dot(v), u:Cross(v)
	if dot < -0.99999 then return CFrame.fromAxisAngle(axis, math.pi) end
	return CFrame.new(0, 0, 0, uxv.x, uxv.y, uxv.z, 1 + dot)
end

local function getModelMass(model)
	local mass = 0
	for _, part in pairs(model:GetDescendants()) do
		if part:IsA("BasePart") and not part.Massless then
			mass = mass + part:GetMass()
		end
	end
	return mass
end

local function onJumpRequest(self)
	if not self.StateTracker.Jumped and self._collider:IsGrounded(true) then
		local vel = self.HRP.Velocity
		self.HRP.Velocity = vel + self._gravityUp*self.Humanoid.JumpPower*JUMP_MODIFIER
		self.StateTracker:RequestJump()
	end
end

local function onHeartbeat(self, dt)
	local standingPart = self._collider:GetStandingPart()
	
	if standingPart and self._prevPart and self._prevPart == standingPart then
		local offset = self._prevCFrame:ToObjectSpace(self.HRP.CFrame)
		self.HRP.CFrame = standingPart.CFrame * offset
	end

	self._prevPart = standingPart
	self._prevCFrame = standingPart and standingPart.CFrame
end

local function onGravityStep(self, dt)
	local camCF = workspace.CurrentCamera.CFrame

	-- update the gravity vector
	local oldGravity = self._gravityUp
	local newGravity = self:GetGravityUp(oldGravity)

	local sphericalArc = getRotationBetween(oldGravity, newGravity, camCF.XVector)
	local lerpedArc = CFrame.new():Lerp(sphericalArc, TRANSITION)

	self._gravityUp = lerpedArc * oldGravity

	-- get world move vector
	local fDot = camCF.ZVector:Dot(newGravity)
	local cForward = math.abs(fDot) > 0.5 and math.sign(fDot)*camCF.YVector or -camCF.ZVector
	
	local left = -cForward:Cross(newGravity).Unit
	local forward = -left:Cross(newGravity).Unit

	local move = self._control:GetMoveVector()
	local worldMove = forward*move.z - left*move.x
	
	local isInputMoving = false
	local length = worldMove.Magnitude
	if length > 0 then
		isInputMoving = true
		worldMove = worldMove / length
	end

	-- get the desired character cframe
	local hrpLook = -self.HRP.CFrame.ZVector
	local charForward = hrpLook:Dot(forward)*forward + hrpLook:Dot(left)*left
	local charRight = charForward:Cross(newGravity).Unit

	local newCharRotation = CFrame.new()
	local newCharCF = CFrame.fromMatrix(ZERO3, charRight, newGravity, -charForward)

	if self._camera.CameraModule:IsCamRelative() then
		newCharCF = CFrame.fromMatrix(ZERO3, -left, newGravity)
	elseif isInputMoving then
		newCharRotation = newCharRotation:Lerp(getRotationBetween(
			charForward,
			worldMove,
			newGravity
		), 0.7)
	end

	-- calculate forces
	local g = workspace.Gravity
	local gForce = g * self._characterMass * (UNIT_Y - newGravity)

	local cVelocity = self.HRP.Velocity
	local tVelocity = self.Humanoid.WalkSpeed * worldMove
	local gVelocity = cVelocity:Dot(newGravity)*newGravity
	local hVelocity = cVelocity - gVelocity

	if hVelocity:Dot(hVelocity) < 1 then
		hVelocity = ZERO3
	end

	local dVelocity = tVelocity - hVelocity
	local dVelocityM = dVelocity.Magnitude

	local walkForceM = math.min(10000, WALK_FORCE * self._characterMass * dVelocityM / (dt*60))
	local walkForce = walkForceM > 0 and (dVelocity / dVelocityM)*walkForceM or ZERO3

	local charRotation = newCharRotation * newCharCF
	local oldGravity = Vector3.new(0,1,0)

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

function init(self)
	self.Maid:Mark(self._camera)
	self.Maid:Mark(self._control)
	self.Maid:Mark(self._collider)

	self._characterMass = getModelMass(self.Character)
	self.Maid:Mark(self.Character.AncestryChanged:Connect(function()
		self._characterMass = getModelMass(self.Character)
	end))

	self.Humanoid.PlatformStand = true
	self.Maid:Mark(self.Humanoid:GetPropertyChangedSignal("Jump"):Connect(function()
		if self.Humanoid.Jump then
			onJumpRequest(self)
			self.Humanoid.Jump = false
		end
	end))

	self.Maid:Mark(self.StateTracker.Changed:Connect(function(state, speed)
		if state == Enum.HumanoidStateType.Freefall then
			self._fallStart = self.HRP.Position:Dot(self._gravityUp)
		end
	end))

	self.Maid:Mark(RunService.Heartbeat:Connect(function(dt)
		onHeartbeat(self, dt)
	end))

	RunService:BindToRenderStep("GravityStep", Enum.RenderPriority.Camera.Value - 1, function(dt)
		onGravityStep(self, dt)
	end)

	self.Humanoid.StateChanged:Wait()
	self.StateTracker.Changed:Fire(self.StateTracker.State, 0)
end

-- Public Methods

function GravityControllerClass:ResetGravity(gravity)
	self._gravityUp = gravity
	self._fallStart = self.HRP.Position:Dot(gravity)
end

function GravityControllerClass:GetFallHeight()
	if self.StateTracker.State == Enum.HumanoidStateType.Freefall then
		local height = self.HRP.Position:Dot(self._gravityUp)
		return height - self._fallStart
	end
	return 0
end

function GravityControllerClass:GetGravityUp(oldGravity)
	return oldGravity
end

function GravityControllerClass:Destroy()
	RunService:UnbindFromRenderStep("GravityStep")
	self.Maid:Sweep()
	self.Humanoid.PlatformStand = false
end

--

return GravityControllerClass

Note the change here to make it zone-based is in the GravityControllerClass. Included is a GravityPart variable that can be changed when player enters a zone, initialized to nil. This part is then referenced to determine the player’s center of gravity.

GravityControllerClass
function GravityControllerClass.new(player)
	local self = setmetatable({}, GravityControllerClass)
	self.Owner = player
	
	self.GravityPart = nil
	...
end

This script then is included in the zone part to update the GravityPart with a Touched event:

Zone Touched Script

image

local part = script.Parent
local PLAYERS = game:GetService("Players")
local GravityController = require(game:GetService("ReplicatedStorage"):WaitForChild("GravityController"))

part.Touched:Connect(function(hit)

	local Player = game.Players:GetPlayerFromCharacter(hit.Parent)
	if not Player then return end
	
	local Controller = GravityController:GetController(Player)
	Controller.GravityPart = part
end)

Lastly, this LocalScript is included in StarterCharacterScripts, which I’m assuming is responsible for initializing a player’s GravityController and updating their gravity data as they move around:

StarterCharacterScripts LocalScript

image

local PLAYERS = game:GetService("Players")
local RUNSERVICE = game:GetService("RunService")

local GravityController = require(game:GetService("ReplicatedStorage"):WaitForChild("GravityController"))
local Controller = GravityController.new(PLAYERS.LocalPlayer)

wait(1)

RUNSERVICE.Heartbeat:Connect(function(dt)
	if Controller.GravityPart ~= nil then
		local distance = (PLAYERS.LocalPlayer.Character.HumanoidRootPart.Position - Controller.GravityPart.Position).Magnitude
		Controller:ResetGravity(Vector3.new((PLAYERS.LocalPlayer.Character.HumanoidRootPart.Position.X - Controller.GravityPart.Position.X) / distance, (PLAYERS.LocalPlayer.Character.HumanoidRootPart.Position.Y - Controller.GravityPart.Position.Y) / distance, (PLAYERS.LocalPlayer.Character.HumanoidRootPart.Position.Z - Controller.GravityPart.Position.Z) / distance))
	end
end)

Here is a copy of the game file as well if it’s easier to just look around or test through there:

Zone Planet Gravity Testing.rbxl (86.6 KB)

Any help is greatly appreciated - thanks!

2 Likes

I’ll take a look, but I’d recommend talking to the creator of this system rather than making a new post.

EDIT: I’m noticing a lot of deprecated stuff in here, so that’s not exactly great, but I’ll try to deal with it.

2 Likes

The creator abandoned the module. Good luck on fixing it though as it’s quite a mess in there.

1 Like

Yeah exactly as @cakeycocoa said. I would ask the creator, but it’s no longer being supported. The fact that his still works though and mine doesn’t makes me think it has something to do with how I modified it, but I’m having a tough time finding what that could be or even just deciphering the original code.

2 Likes

From what the video shows, whatever part of the script that detects if you’re in a zone is disconnected after death, and doesnt reconnect. i’m not at home right now so I cant actually debug this, but if you havent found it out by tomorrow I’ll help

1 Like

Assuming you haven’t made changes to the original, I would try changing StreamingEnabled and SignalBehavior.

These two seems to be the culprits of many old things breaking in new places.

Edit:

The settings are Workspace.StreamingEnabled = false and Workspace.SignalBehavior = Enum.SignalBehavior.Immediate.

1 Like

I’ve done some brainstorming with ChatGPT and it suggested running an initialize function on a CharacterAdded event to reinitialize the gravity controller variables, particularly humanoid root part. I’m not home right now to share the code but it didn’t work unfortunately.

I will try looking at what @BendsSpace suggested when I get home.

1 Like

Bingo!

Workspace.StreamingEnabled = false fixed the problem!

I added Workspace.SignalBehavior = Enum.SignalBehavior.Immediate as well, but it did not work without the above setting set to false.

I read here that it’s not recommended to use immediate mode for new work. I’m not too familiar with this setting - are there any reasons you’d recommend using it now that I’ve determined it’s not the issue in this case?

1 Like

Some old signal modules and event code broke when with deferred events (they relied on events being responded to immediately).

If it works with deferred signals I would use deferred signals: at some point in the future they’re planning on making everything use deferred.

1 Like

Ah okay that makes sense. Thank you for explaining!

1 Like

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