I’ve seen very few mentions of a suggestion like this one before, but without much explanation, that I wanted to create post for it as well
I’ve been studying the Source Engine a bit, and have established tests and debugs that I will demonstrate below, where I explain and showcase the Combine Ball from Half-Life 2, in more detail.
While, writing this and testing, I came to a conclusion that VectorForce
can already be used to achieve custom Gravity. There are still some issues, hence why the title includes ‘add “override gravity” to VectorForce’.
As a Roblox developer, it is currently too hard to …
Create Physics Assemblies that do not use the Workspace’s Gravity, without the use of BodyForce
or VectorForce
with the assistance from Scripts.
By default everything uses the Workspace’s Gravity.
Below, you’ll see a script for demonstration, where I use VectorForce
to control “custom gravity” and point out the questionable issues.
I wanted to keep these two sections here just pure text, for the feedback format.
So, further down below, there’s more that expands upon this script, and shows a way where it updates the .Force
whenever the dependencies change their values.
If Roblox is able to address this issue, it would improve my development experience because …
It would be faster and easier to create zero gravity projectiles, that reliably move and stop based on the velocity you input, without having to worry about the additional steps VectorForce
may add to the physics process.
It doesn’t even have to be a projectile, you can have a scriptless object that moves with physics.
Things with no gravity don’t get pulled down anywhere, and can be controlled with things like .AssemblyLinearVelocity
. Without gravity, there’s no influence in the velocity from the environment. (Well, there’s Fluid forces and Drag, but you can disable it I think)
The gravity would be controlled on Roblox’s low-level C++ implementations.
Which sounds like a more ideal solution over BodyForce
.
You could also use it for visual effects, which would be a debate over that vs. PreRender
if using for pure visual effects, like particles, where you’d use mathematical expressions. Which you can still do. But Gravity gives you more possibilities.
Maybe, Gravity could be a CustomPhysicalProperties
.
Or, VectorForce
could have a property that overrides the Gravity, because it already has a property that takes an Enum which contains the word “Actuator”. (Is the name related to gravity?).
If you can override gravity, you wouldn’t need to listen for changes made to workspace.Gravity
and AssemblyMass
. And with VectorForce
you can already change where things should get pulled towards to.
You don’t need “Gravity Origin” just to have a Gravity Property. But I have never seen the Physics Source code, so maybe if someone would want to implement Gravity Property, maybe they’ll find sense to add logic for “Gravity Origin” or “behavior” along the way as well.
But that is sort of already there for VectorForce
?
What happened in Beyond the Dark?
I know that Beyond the Dark has some code where they’d use BodyForce
or VectorForce
. However, lately Beyond the Dark was doing the following.
Is this intended? What is happening? These objects, they’re normal physics, but they’re lagging (Maybe, because the server is lagging, which also happens in Studio). It feels like these things have a different gravity, even though they don’t… Unless they do?
But Beyond the Dark does use BodyForce
. BodyForce
is deprecated, newer one is VectorForce
. I am not sure which one released first, the game or VectorForce
.
Workaround, BodyForce and VectorForce
BodyForce
Beyond the Dark uses BodyForce
, which works, I think.
-- "p" is a Part
local f = Instance.new("BodyForce")
f.Name = "ZeroGForce"
f.Force = Vector3.new(0,1,0) * workspace.Gravity * p:GetMass()
f.Parent = p
I didn’t even notice, but there’s the addition of BodyVelocity
with Vector3.new(0,0,0)
for secret “Air Resistance”. I hope that LinearVelocity and that, doesn’t have that? Not sure if this relates to dampening
, a physics property from other engines.
What even does “Write Parallel” for “Get” mean?
But, BodyForce
is deprecated.
VectorForce
This works. But there are some questionable issues.
local p = script.Parent
local attach = script.Parent.Attachment -- Maybe it doesn't even this, I didn't check.
local f = Instance.new("VectorForce")
f.Name = "ZeroGForce"
f.Force = Vector3.new(0,1,0) * workspace.Gravity * p:GetMass()
f.Attachment0 = attach
f.ApplyAtCenterOfMass = true
f.RelativeTo = Enum.ActuatorRelativeTo.World
f.Parent = p
Now, the part that I used to question was, Vector3.new(0,1,0) * workspace.Gravity * p:GetMass()
. But then I started thinking.
First thing is, Roblox Engineers have the Roblox Source code. They can precisely know that “workspace Gravity” in Roblox pulls things down. And “Mass” is an important value in Physics Engines. So, what I believe the above code does, is to negate original gravity.
And it does that by adding VectorForce
.
Now, I am not sure how efficient that is. Whether the Roblox Physics Engine performs optimizations. Or if VectorForce
is being computed all the time. While Parts without VectorForce
, just have an one easy drive-through computation route with only the value from workspace.Gravity
.
They have more formulas, I still need to post Resources about a few useful ones I found.
Aside from that, there are many questionable LuaBridge
problems here.
- If your Mass changes, you need to update
Force
again. - Same for Workspace’s Gravity.
I think BodyForce’s behavior was a bit different.
Now, what if you wanted it to have a Gravity of 1 instead of 0?
local desiredGravity = 1
f.Force = Vector3.new(0,1,0) * (workspace.Gravity - desiredGravity ) * p:GetMass()
This seems to be it.
Now, the issue here is that you’re carrying 2 dependencies at all times.
workspace.Gravity
and :GetMass()
So, basically:
Code
local p = script.Parent
local attach = script.Parent.Attachment -- you need to create an attachment
local f = Instance.new("VectorForce")
local _desiredGravity = 1
local function updateForce()
f.Force = Vector3.new(0,1,0) * (workspace.Gravity - _desiredGravity) * p:GetMass()
end
f.Name = "ZeroGForce"
f.Attachment0 = attach
f.ApplyAtCenterOfMass = true
f.RelativeTo = Enum.ActuatorRelativeTo.World
updateForce() -- init
f.Parent = p
p:GetPropertyChangedSignal("AssemblyMass"):Connect(function()
print("AssemblyMass updated")
updateForce()
end)
workspace:GetPropertyChangedSignal("Gravity"):Connect(function()
print("Gravity updated")
updateForce()
end)
This would update the .Force
, whenever the dependent values change and you can use it with more VectorForce
.
Now, Roblox could simplify this, which would do that what you can do with VectorForce.Force
, but more compact.
There is no built-in autocomplete for the Explorer yet. The Explorer doesn’t tell you that you need to create a “NumberValue” named Gravity
for a custom things that you created. Hence why built-in Instances are usually interesting.
I think a way to describe this is would be the creation of a “Wrapper” over… Roblox.
Hence why I asked whether the Require Tracer for ObjectValue
could also have autocomplete support, for very specific linking. https://devforum.roblox.com/t/release-notes-for-662/3513171/28
Here we have encapsulated values in our interface, which is _desiredGravity
. Plus, I am not a fan of the above code. My route would be a single script controlled by Tags. Maybe, I’d avoid metatables for the sake of preserving a little bit of memory.
The connections shouldn’t impact LuaBridge
that much. I’d question RunService connections that access properties, a bit more. Unless they’re optimized to behave differently when within something like PreSimulation
.
And to interface it comfortable, probably have these “Value Instances”. Where one has to be careful about timing and delays for events.
If Roblox ever offers you to change the workspace’s Gravity direction, you’d have to update the above script, so that Vector3.new(0,0,0)
has the right XYZ for the new direction that things the pulled to.
The Combine Ball from Half-Life 2
The only reason I created this section is because the feedback format, also asks to show a good example… and idk. This gravity stuff is fun to play around with, I don’t know how good it will be, but it’s used in another engine
Note that, this Source Engine version is a bit different.
At the left you’ll see the Combine Ball with Gravity disabled. And at the right you’ll see the Combine Ball with Gravity enabled.
Backup Video links
No Gravity:
With Gravity:
Here is an extended version for with Gravity enabled:
Video
The Combine Ball in Half-Life 2 is an entity that acts as a projectile created through VPhysics. VPhysics is available through an interface that let’s you access methods and do things, e.g. ->EnableGravity(false)
which is the default way on how the Combine Ball spawns in.
The Combine Ball is a sphere. Sphere’s seem to work fine on Roblox as well, so it is actually possible to have a reliable bouncing ball on Roblox, compared to a cube.
Based on the looks, you purely see a ball that reflects towards the other direction whenever it bounces off a wall. But it’s actually a bit more different.
In Roblox you have CustomPhysicalProperties.
The Combine Ball’s default material is metal_bouncy
which is:
surfaceproperties.txt snippet
"solidmetal"
{
"density" "2700"
"elasticity" "0.1"
"audioreflectivity" "0.83"
"friction" "0.8"
"step" "SolidMetal"
"audioHardMinVelocity" "250"
// "impacthard" "SolidMetal.ImpactHard"
// "impactsoft" "SolidMetal.ImpactSoft"
// "scraperough" "SolidMetal.ScrapeRough"
// "scrapesmooth" "SolidMetal.ScrapeSmooth"
// "strain" "SolidMetal.Strain"
"gamematerial" "M"
}
"metal_bouncy"
{
"base" "solidmetal"
"elasticity" "1000"
"friction" "0"
"density" "10000"
}
Now, the values differ here a bit. In Roblox, I think 100 Elasticy with the Weight of 1, means that if something collides onto the floor, it will go back up to the same height from where it fell down from. That’s not the same on the Source Engine. The Source Engine is old. I am unsure about Source 2.
Physics flexibility on Roblox is better than in the Source Engine. I can’t change particular things in the Source Engine without crashing, without altering the Source Engine. But the Source Engine promises to be more reliable.
If I change these elasticity values on the Combine Ball, the Combine Ball will still bounce.
The Combine Ball is programmed to avoid slow down, there’s continuous velocity that is being applied onto it. With how it is set up, it appears a bit strange but maybe there’s sense behind it.
There’s a collision event method. Within that collision event the logic for not slowing down the ball is set up.
Now, Roblox… doesn’t have that. And there’s something about the direction as well and unit conversions.
Edit: There’s a way that I find very questionable, but you can keep track of PreSimulation
and PostSimulation
with the Touched
Event.
Vector vecFinalVelocity = pEvent->postVelocity[index];
VectorNormalize( vecFinalVelocity );
vecFinalVelocity *= GetSpeed();
PhysCallbackSetVelocity( pEvent->pObjects[index], vecFinalVelocity );
But that’s basically it. Where “VectorNormalize” could be going towards some DirectX methods. PhysCallbackSetVelocity
can probably be seen as something that would trigger at “PreSimulation” on Roblox.
I did have a similar question, whether changing color of a GUI is better done through PreRender:Once
or .Color = newColor
vecFinalVelocity
overwrites the postVelocity from the collision event. On Roblox, you’d have to do hacky things with Touch Emitters, and then keep track of the velocity before and after, and you’d have to study at what time in PreSimulation
the velocity will even change.
But I am not covering collisions just yet, that’s for another feature request.
Here is a video where the material is being changed with continuous velocity being completely disabled:
Video
Video 2
Here’s a video of showing disabled and enabled continuous velocity on the Combine Ball with the wrong material:
Video
So, that’s about the Combine Ball, in comparison to Roblox. But the point was to achieve the purpose of disabling gravity individiually.