Scripting HelpersARCHIVE
A little piece of Roblox history. Preserved for everyone, one question at a time.Read about the archive →
COMMUNITY QUESTION

[?] Custom Characters: Detecting the Ground

In order to compute various mechanical behaviours of a character, such as jumping and walking, it is necessary to know whether a Part is "on the ground".

Given an arbitrary physically simulated cuboid, how do you know when there is sufficient substance beneath it on which to rest itself?

If you don't understand what I mean, think of this related question "How do you know that a Part is or is not falling?". You might give a trivial answer, like "when the Y velocity is less than 0", and I would reply "I do not count sliding down a slope as falling".

I have experimented with two techniques, both of which are approximations host to various disadvantages:

Technique #1

Measure acceleration frame-by-frame and conclude that there is no ground beneath if the vertical acceleration is within N% of gravitational acceleration. Using a value of 15% was derived empirically and should represent a good upper bound.

Take "root" to be the physical Part in question:

prevy = root.Velocity.Y
t = tick()
coroutine.wrap(function()
while wait() do

local tempy = root.Velocity.Y

local deltat = tick() - t

local dydt = (tempy - prevy)/deltat --rate of change of velocity, i.e. acceleration

if math.abs(1+dydt/g) < 0.15 then
ground = false
else
ground = true
end)()

This technique is not always reliable with smaller upper bounds, and the greater the upper bound on the error %, the more unreliable other behaviour (such as walking down a steep slope) will be.

Technique #2

If any of a number of downwards rays parallel with the Y-axis comes into contact with something, conclude that there is ground beneath. I will not post the implementation of this technique, as it is computationally straightforward and intuitive.

The problem with this is that many rays are needed to be cast continually because only a fraction of the Part in question may be resting on a surface. Suppose that the smallest surface size of an ambient detail is 1 stud^2, how many rays need to be cast from a Part with a 4 st^2 bottom surface to guarantee that the 1 st^2 surface that it may be resting on is detected?


How do you suggest improving the above techniques, and what other approaches can you think of?

Thank you for your time!

3 answers

The easiest method I can think of would be to use a single raycast from the character's center of mass. Place an invisible part at this point, updating it whenever you update the positioning of the model (or less), and do a raycast straight down to see if it hits ground.

This isn't perfect and will result in some weird situations where the characters' feet could be off a cliffside, but it wouldn't fall, but it is a lot cheaper than multiple raycasts.

A modification of this uses two raycasts from the character's model's feet (one each, that is) to see if each is touching ground. This has an additional benefit of allowing you to slightly reposition the feet model so that the flats of the feet are always on the ground, rather than hovering inside or above it.

I'd give you some code, but I'm on mobile right now, sorry!

Good luck.

7 comments

0

Thanks for taking the time to respond, Adark. I'm afraid I don't understand your proposed solution, though. How does placing a Part at the end point of the first ray cast (from the center of mass) achieve anything? Surely the placement of that first part is subject to the exact same problems as using one ray for the whole detection process in the first place. I may just be misunderstanding what yo

0

..what you wrote. Perhaps you could elaborate on this? Thanks.

0

You place the part AT the center of mass, and then do a single raycast FROM it, straight down. If the CoM is balanced, so is the entire model, if you just want basic physics.

1

However, that will allow it to fall through thin cracks (which is symptomatic of not solving the problem he wants)

1

A solution I use (sort of) for my Portal game is to "expand" all parts by the size of the player. This is the behavior requested. Expanding by less would get a mix which might be optimal.

0

I should have clarified that the rotation of the Part is locked in all but the Y-axis, so if any part of the surface is on ground then the whole block is stable, so CoM tests are unnecessary. As a side note, I don't understand why you say to place a Part there and update its position, rather than just keeping/updating a Vector3 of the CoM?

0

@BlueTaslem I don't understand your solution, either, I'm afraid! What do you mean by "expand" and how does this affect detecting ground?

This may read a little strange, but I was watching a video by NVidia, about how their GPUs process MFAA (Multi-Framed sampled Anti-aliasing).

This AA method basically rendered the same object at different times, compared the results, and displayed good AA, without using as much processing power in a single frame.

If you're using Server-scripts, this may not work so good; But for LocalScripts, utilizing RunServices 1/60 update time - you could split down the impact of multiple ray-casts.

In other words, instead of casting all rays through a for loop in one update-interval - which can cause lag with multiple objects - cast one ray for each object in one frame, then the next offset ray-cast in the next frame.

You could also determine how detailed the ray casts be, based on how long it took to do this last ray cast.

This is my theory anyway, hopefully there's an idea in there somewhere you could use. ^^

1 comment

0

This would only be necessary if so many rays were required as to slow each update considerably, but I'd rather not have to process an excess of Rays anyway, still, thanks for the suggestion.

Technique #3

The solution that I've found is to exploit some special behaviour in how the ROBLOX engine positions Part Instances.

Given a Part that has CanCollide set to true, instructing part.Position = ... will cause ROBLOX to evaluate the geometry of the Part and environment for collisions and reposition the Part if an intersection is detected. Not only does it do this, but it also does so before the next instruction executes (rather than on a frame-by-frame basis). Hence, (ignoring other slight quirks that can be easily resolved) one can check for ground by creating a Part with the same bottom area of the 'Player', moving it to below the Player, and then checking if the position is what you set it to (i.e that it has not been changed by ROBLOX due to an intersection).

Test thus far suggest that this method is quick, reliable, and functions well with varied environmental structures.

2 comments

0

Hi, have you tried using Part:GetTouchingParts()? According to wiki parts to need to have CanCollide set to true and cannot be adjacent or flying (obviously), and so I did a quick test with a simple custom character despite it's legs being CanCollide on it still intersected the baseplate, but some exceptions may apply (HipHeight). For that case you can also try checking Humanoid's states & events

0

No, I haven't tried that, precisely because adjacent parts don't count as touching, so the ground underneath a character wouldn't count as touching. Also, using Humanoid state defies much of the value in custom physics - the idea is to be independent from the default ROBLOX implementation.

This discussion is preserved as it was. New answers, comments, and votes are closed.