Author Topic: Checking whether a player is airborne  (Read 5352 times)

I'm surprised the searches found nothing on this.

I've made myself a player with the capability to dodge (or, more modestly, increase speed for a fraction of a second) and I recently found that triggering the dodge in mid-air consumes no energy and gives a rapid, unfair boost.

Is there a way to detect whether a player is airborne so I can remove dodging ability in the air?

Here's what the agile player uses to check if they're on the ground.
Code: [Select]
function ag_bicheck(%obj)
{
if(!isObject(%obj))
{
return 0;
}
if(%obj.getClassName() $= "fxDTSbrick")
{
if(%obj.isColliding() && %obj.isRendering())
{
return 1;
}
return 0;
}
return 1;
}
function Player::IsOnGround(%player)
{
%pos = %player.getPosition();
%scale = %player.getScale();
%xs = getWord(%scale,0);
%ys = getWord(%scale,1);

%nw = vectorAdd(%pos,0 - (0.75 * %xs) SPC 0 - (0.75 * %ys) SPC 0);
%dnw = vectorAdd(%nw,"0 0 -0.5");
%ne = vectorAdd(%pos,(0.75 * %xs) SPC 0 - (0.75 * %ys) SPC 0);
%dne = vectorAdd(%ne,"0 0 -0.5");
%se = vectorAdd(%pos,(0.75 * %xs) SPC (0.75 * %ys) SPC 0);
%dse = vectorAdd(%se,"0 0 -0.5");
%sw = vectorAdd(%pos,0 - (0.75 * %xs) SPC (0.75 * %ys) SPC 0);
%dsw = vectorAdd(%sw,"0 0 -0.5");
%dpos = vectorAdd(%pos,"0 0 -0.5");

%raynw = ag_bicheck(getWord(containerRaycast(%nw,%dnw,$TypeMasks::FxBrickAlwaysObjectType|$TypeMasks::InteriorObjectType),0));
%rayne = ag_bicheck(getWord(containerRaycast(%ne,%dne,$TypeMasks::FxBrickAlwaysObjectType|$TypeMasks::InteriorObjectType),0));
%rayse = ag_bicheck(getWord(containerRaycast(%se,%dse,$TypeMasks::FxBrickAlwaysObjectType|$TypeMasks::InteriorObjectType),0));
%raysw = ag_bicheck(getWord(containerRaycast(%sw,%dsw,$TypeMasks::FxBrickAlwaysObjectType|$TypeMasks::InteriorObjectType),0));
%rayce = ag_bicheck(getWord(containerRaycast(%pos,%dpos,$TypeMasks::FxBrickAlwaysObjectType|$TypeMasks::InteriorObjectType),0));

if(%raynw || %rayne || %rayse || %raysw || %rayce)
{
return 1;
} else {
return 0;
}
}
Dunno how well it works.

Here's what the agile player uses to check if they're on the ground.

Dunno how well it works.
Looks like it'll work for most cases, but large players standing on small bricks would break it, and it'll probably fail with players using different models. Also it'll fail if the player is standing on something unusual like another player or a vehicle, or if the player's feet are passing through a non-colliding brick while they're in mid-air. Still, it uses 5 raycasts every time you do it. Could be optimized a bit if it did the checks and returns alongside the raycasts, so if there was ground directly under the player and the center raycast saw it, it would return 1 immediately rather than do 5 more raycasts that don't matter.

One cheap test to check for being airborne that has some room for failure is to test if their z velocity is 0. This is virtually never the case when they're not on solid ground. It can report false positives for being in the air if the player is running up and down a hillside, but if you do the raycast thing whenever it seems like they're in the air just to be sure, you'll cut down on unnecessary raycasts a ton. It could also theoretically report false negatives if a player could be frozen in midair effectively, but first of all, that's actually really difficult to do because loltorque, and second, cases where it does happen can usually be checked on a case-by-case basis rather than doing a search through the collision geometry of the world.

The only real issue I can see with doing a z velocity check is that the ability won't work if the player is going up or down a ramp, yet still grounded.

As for the first suggestion, I'd use that approach, but with one raycast. As for different models, I wouldn't worry, since this is a script for a specific playertype. Different sizes can be fixed by getting player z scale and some simple math.

The only real issue I can see with doing a z velocity check is that the ability won't work if the player is going up or down a ramp, yet still grounded.
It can report false positives for being in the air if the player is running up and down a hillside, but if you do the raycast thing whenever it seems like they're in the air just to be sure, you'll cut down on unnecessary raycasts a ton.

As for the first suggestion, I'd use that approach, but with one raycast. As for different models, I wouldn't worry, since this is a script for a specific playertype. Different sizes can be fixed by getting player z scale and some simple math.
This would cause it to fail while standing on a ledge, which is the point of the other raycasts. When I said scale would be an issue, what I meant was if you have a really big guy, he could stand on a thin platform that would support him, but this script would still register as in mid-air if the raycast misses it.

This would cause it to fail while standing on a ledge, which is the point of the other raycasts. When I said scale would be an issue, what I meant was if you have a really big guy, he could stand on a thin platform that would support him, but this script would still register as in mid-air if the raycast misses it.
Use a box search?

Use a box search?
Oh right. Forgot about those. I've actually considered using them for this exact purpose before, but never got around to looking into how to set one up for some reason. Yeah if you can get that dimensioned properly and it identifies the bricks correctly, it'd definitely be better than the raycasts.

containerBoxEmpty doesn't actually work though.

containerBoxEmpty doesn't actually work though.
What about initContainerBoxSearch and then return if it finds a brick?

Here is one i made that uses a box container. (from sonic like)

Code: [Select]
function Player::groundCheck(%this)
{
%mask = $TypeMasks::FxBrickObjectType | $TypeMasks::StaticObjectType | $TypeMasks::VehicleObjectType;

initContainerBoxSearch(vectorAdd(%this.getPosition(),"0 0 -0.3"),"1 1 0.5",%mask);
%object = containerSearchNext();

if(%object.getClassName() $= "FxDTSBrick")
if(!%object.isColliding)
return 0;

if(isObject(%object) && %object != SunLight.getId() && %object != %this.getId())
return 1;
return 0;
}

The reason the sun's ID is in there because for some verry odd bug, it would come up in the %mask variable. Remove if you need to verify.
« Last Edit: September 19, 2014, 06:21:48 AM by Wrapperup »

Here is one i made that uses a box container. (from sonic like)

Code: [Select]
function Player::groundCheck(%this)
{
%mask = $TypeMasks::FxBrickObjectType | $TypeMasks::StaticObjectType | $TypeMasks::VehicleObjectType;

initContainerBoxSearch(vectorAdd(%this.getPosition(),"0 0 -0.3"),"1 1 0.5",%mask);
%object = containerSearchNext();
if(isObject(%object) && %object != SunLight.getId() && %object != %this.getId())
return 1;
return 0;
}

The reason the sun's ID is in there because for some verry odd bug, it would come up in the %mask variable. Remove if you need to verify.
Ah useful.
The last ground check i did was very basic (one raycast from position to -0.1 on the z-axis) and it sometimes didn't detect it. :/
I will be sure to check this code out.
Though i do see that you do not check the bricks if they are collidable.

Ah useful.
The last ground check i did was very basic (one raycast from position to -0.1 on the z-axis) and it sometimes didn't detect it. :/
I will be sure to check this code out.
Though i do see that you do not check the bricks if they are collidable.

Just check if the class is a brick and check %brick.isColliding. I will add to the code.

containerBoxEmpty doesn't actually work though.

Should work just fine, it's just that the player container box is ludicrously much bigger than you'd expect. Try using a predefined box size only masking bricks or something similar.

Just check if the class is a brick and check %brick.isColliding. I will add to the code.
Yeah i know how,  just wanted to make you aware of it. :)