Chara controller & one way platforms / walls.

Discuss any questions about BEPUphysics or problems encountered.
Post Reply
6thVenom
Posts: 12
Joined: Mon Sep 03, 2012 6:31 pm

Chara controller & one way platforms / walls.

Post by 6thVenom »

Hi,

You know, those little floating platforms you can jump through and stand on it, or those walls you can pass only in one direction, generally seen in all platformers.

I first thought it would be easy as collision triangles are one side only, but the character controller detect the penetration and correct it, resulting a big out of control jump at best.
I'm sure i could do something with collision rules and triangles normals, or something like one of the demos (volume penetration by a colored box), but i can't figure where to start implementing it in the character controller.

So how would you go about this?

Thanks,
Greg.
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Chara controller & one way platforms / walls.

Post by Norbo »

The character (and the physics in general) usually works completely 'locally.' All it knows is the current state, not how it got there. Working within the restrictions of locality (especially working only with contact points) makes it difficult to create behaviors like this one. Without extra information, the character can't easily determine whether it has smashed deep into penetration from above or jumped up from below. It has to pick one or the other. Consistent with the rest of the physics, the character just always treats it as penetration to resolve.

The solution, on a high conceptual level, is to collect more information.

One possible implementation of this additional information would be a supplementary ray/convex cast shot downwards from the feet of the character. All such platforms would default to a collision rule of NoBroadPhase, but when that ray/convex cast from the character's feet hits an object, the collision rules are modified to allow the character to collide with it.

This ensures that the character can always jump up through the bottom of a platform, but when it comes down from above, it is solid.

A few details to watch out for:
-Using a ray cast as opposed to a convex cast will result in the character not landing on platforms which do not extend at least halfway into the character (assuming the ray was positioned centrally). Using a convex cast with a flat cylinder shape that spans the character's width will address this.
-The cast's origin should be at the bottom of the character, offset upwards ever so slightly. Only 'solidify' platforms if the cast has a nonzero time of impact. A cast with a T of 0 means that the cast started within the object (or extremely close to it); for platforms with volume, this could mean that the character is not out of the platform yet and 'solidifying' it would cause penetration resolution.
-Avoiding unnecessary ray casts would be good for performance. If you're already standing on a solid object (as determined by the character's support finder), then you don't need to perform more casts. This may end up complicated collision rules management logic, though.
-You may find that the default collision rules system is a little unwieldy for this purpose since it's so specialized. You can override the CollisionRules.CollisionRuleCalculator with a custom alternative if you find it convenient to do so.
6thVenom
Posts: 12
Joined: Mon Sep 03, 2012 6:31 pm

Re: Chara controller & one way platforms / walls.

Post by 6thVenom »

Hey thanks for the fast reply! (as always)

But i think i can't go for a 'turn collisions on/off for the platform' method, as my game is supposed to be multiplayer enabled, so one standing on the platform don't want to fall through it every time another one jump into the platform from bottom.

Because the game already use and abuse collision groups for projectiles by team (friendly / enemy), i think i must go for a convex cast that somehow turn off collisions responses between platform collision group and player(s), by changing the player's collision group while he is into the volume.

What if i cast from the supportFinder's origin (bottom of the body) to direction Vector3(0f, bodyHeight + 1f, 0f), then if a platform's collision group entry is found, switch player's collision group to another one?

The character controller support height should be enough to prevent any bottom platform to disable "solidity", right?
And of course disable the cast if has support.

Now, if you approve that way should work, in your opinion, where in the char controller should i put the check/cast please?
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Chara controller & one way platforms / walls.

Post by Norbo »

But i think i can't go for a 'turn collisions on/off for the platform' method, as my game is supposed to be multiplayer enabled, so one standing on the platform don't want to fall through it every time another one jump into the platform from bottom.
Using 'personal' collision rules would indeed be a bad idea, but that was not what I had in mind. Collision rules are defined on a pairwise basis. One character and the platform can have one collision rule while another character has a different rule. There are many ways to approach this correctly.

Abusing collision groups for the purpose would produce headaches. I would either just use the 'specific' object-to-object collision rule relationships, or make your own collision rule calculator that takes into account whatever data is convenient.
What if i cast from the supportFinder's origin (bottom of the body) to direction Vector3(0f, bodyHeight + 1f, 0f)
While going bottom-up to desolidify an object can work, it doesn't seem as intuitive (to me, at least) as casting down to solidify an object. You also can't disable the cast once you have support in general when doing bottom-up unless your environment geometry obeys certain rules. Casting down relaxes those requirements.
Now, if you approve that way should work, in your opinion, where in the char controller should i put the check/cast please?
This should probably not be managed as a part of the character itself. Characters run in parallel. Modifying collision rules from asynchronous contexts is dangerous.

Instead, just hook an event handler to the appropriate stage (the Finishing event on the Space.BeforeNarrowPhaseUpdateables would be a good spot) or create your own IBeforeNarrowPhaseUpdateable. That way, it runs on each time step in line with the execution without having to worry about threading issues.
6thVenom
Posts: 12
Joined: Mon Sep 03, 2012 6:31 pm

Re: Chara controller & one way platforms / walls.

Post by 6thVenom »

Yes i think i got your point.

You're idea is to have a general rule of no solid platforms for players, except if the top-to-bottom cast (or would body.Bottom to down suffice?) detect a platform underfeet.
that's the point?
Norbo wrote:I would either just use the 'specific' object-to-object collision rule relationships, or make your own collision rule calculator that takes into account whatever data is convenient.
Could you post me pseudo code about that, or point me some place in the demos source where i can find an example to understand how that works, and how to set it up?
Norbo wrote:This should probably not be managed as a part of the character itself. Characters run in parallel. Modifying collision rules from asynchronous contexts is dangerous.

Instead, just hook an event handler to the appropriate stage (the Finishing event on the Space.BeforeNarrowPhaseUpdateables would be a good spot) or create your own IBeforeNarrowPhaseUpdateable. That way, it runs on each time step in line with the execution without having to worry about threading issues.
My understanding of this subject is very light, but is that also applies to "the 'specific' object-to-object collision rule relationships"?

I'll give a look in the demos source to find something similar (if any), cause it's definitively something obscure for me atm :D


PS: I've found that the Finishing event is what you use in the chara controller to Expand bounding box before the narrow phase right?
so i should create the casting method and hooking it there, thus preventing all threading issues?
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Chara controller & one way platforms / walls.

Post by Norbo »

You're idea is to have a general rule of no solid platforms for players, except if the top-to-bottom cast (or would body.Bottom to down suffice?) detect a platform underfeet.
that's the point?
That's pretty much correct.

Doing a top-down cast will result in a jumping character 'solidifying' platforms as soon as the head comes up through the surface. That's why the cast should start near the bottom and go down. That way, the feet have to come up through the floor (mostly) before the floor solidifies.
Could you post me pseudo code about that, or point me some place in the demos source where i can find an example to understand how that works, and how to set it up?
The collision rules instances associated with a collidable have a 'specific' dictionary which stores other objects and the rules associated with them. The default collision rules calculator considers these relationships to be high priority; they will override any other relationships (personal or group). The CollisionRules.AddRule convenience function makes defining these rules simpler:

Code: Select all

CollisionRules.AddRule(a, b, rule);
The Collision rules documentation explains the collision rules system on a conceptual level.

The CollisionFilteringDemo in the BEPUphysicsDemos shows some collision rules management. Some other demos use it too, though they have other primary foci.
My understanding of this subject is very light, but is that also applies to "the 'specific' object-to-object collision rule relationships"?
The 'specific' object relationships are just another form of collision rules management, so yes, they're unsafe from an asynchronous context too. I should clarify though- you should assume everything is unsafe from an asynchronous context unless you have proof that it is, in fact, safe :)
PS: I've found that the Finishing event is what you use in the chara controller to Expand bounding box before the narrow phase right?
so i should create the casting method and hooking it there, thus preventing all threading issues?
That could work. Hooking it there will execute it before the broad phase runs which allows you to stop pairs from ever being created by assigning the appropriate collision rules. On the other hand, the latest pairs have not yet been detected because the broad phase has not run, so you might encounter occasional subtle frame-off errors. Chances are these errors won't matter, though.
6thVenom
Posts: 12
Joined: Mon Sep 03, 2012 6:31 pm

Re: Chara controller & one way platforms / walls.

Post by 6thVenom »

Ok, i think i'm ready to rumble with code... tomorow! as it's 1am here :)
I overlooked the collision rule (specific/personal/group) property, it's exactly what i'm looking for.
Norbo wrote:That could work. Hooking it there will execute it before the broad phase runs which allows you to stop pairs from ever being created by assigning the appropriate collision rules. On the other hand, the latest pairs have not yet been detected because the broad phase has not run, so you might encounter occasional subtle frame-off errors. Chances are these errors won't matter, though.
I'll go for that for the moment, but if you got a better idea about where and how to do what i'm looking for, feel free to tell me about. :D

You gave a great help as always.

Thanks you for your time,
Greg.
6thVenom
Posts: 12
Joined: Mon Sep 03, 2012 6:31 pm

Re: Chara controller & one way platforms / walls.

Post by 6thVenom »

Hi Norbo, i'm back with some code, i had to finish some textures before going further with this.
First let me explain how i naively added it to my game:

So i got this Entity_player class that hold a BroadPhaseEntry for the current platform he is standing on if any, during the class update (fixed time step), it call a physicManager's method (with a ref) that ConvexCast a cylinder down to detect any collisionGroupPlatform entry's collision rules.

If something is found, i apply a specific Normal collision rule to the player and ref the result.HitObject.
If nothing is found, and the ref still have a value, i remove the player's specific collision rule and ref null.
(or do nothing, as i can't add/remove the same rule twice.)

Strangely, this works perfectly, except very unnoticeable glitches sometimes on landing, due to threading unsafe, or the fact the "test platform" is actually a cube with an axis constraint dropped on a static mesh?


So here is the method (maybe usefull for futur forum searchs):

Code: Select all

        public bool CheckPlatformsForPlayer(int id, ref BroadPhaseEntry platform)
        {
            //Create the shape to cast.
            ConvexShape shape = new CylinderShape(0.1f, PlayerControllers[id].Body.Radius);

            //Where is the does the shape start?
            var startingTransform = new RigidTransform(PlayerControllers[id].Body.Position, Quaternion.Identity);

            //Which way is the cast going?
            var sweep = new Vector3(0, -6.5f, 0);

            //Compute bounding box.
            BoundingBox BBox;
            Toolbox.GetExpandedBoundingBox(
                ref shape, 
                ref startingTransform, 
                ref sweep, 
                out BBox);

            RayHit rayHit;
            var overlappedElements = Resources.GetCollisionEntryList();
            space.BroadPhase.QueryAccelerator.GetEntries(BBox, overlappedElements);

            RayCastResult result = new RayCastResult();
            result.HitData.T = float.MaxValue;

            for (int i = 0; i < overlappedElements.Count; ++i)
            {
                var candidate = overlappedElements.Elements[i];

                if (!convexCastPlatformFilter(candidate))
                    continue;

                //Found one!  Does our convex cast hit it?
                if (candidate.ConvexCast(shape, ref startingTransform, ref sweep, out rayHit))
                {
                    if (rayHit.T < result.HitData.T)
                    {
                        result.HitData = rayHit;
                        result.HitObject = overlappedElements.Elements[i];
                    }
                }
            }

            //We hit a platform
            if (result.HitData.T < float.MaxValue && result.HitData.T > 0f)
            {
                if (platform != result.HitObject)
                {
                    // Add a specific collision rule.
                    PlayerControllers[id].Body.CollisionInformation.CollisionRules.Specific.Add(
                        result.HitObject.CollisionRules,
                        CollisionRule.Normal);

                    platform = result.HitObject;
                }

                return true;
            }
            else
            {
                if (platform != null)
                {
                    // Remove a specific collision rule.
                    PlayerControllers[id].Body.CollisionInformation.CollisionRules.Specific.Remove(platform.CollisionRules);
                    platform = null;
                }
                
                return false;
            }
        }

        bool convexCastPlatformFilter(BroadPhaseEntry entry)
        {
            if (entry.CollisionRules.Group == collisionGroupPlatform)
                // We got a platform.
                return true;
            else
                return false;
        }
Feel free to comment on my code, as it's my first C sharp project :D

So many thanks for your help, and the so awesome lib that is BEPU,
Greg.

PS: Why did i had to replace Resources.GetBroadPhaseEntryList() (not found anywhere) by Resources.GetCollisionEntryList()?
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Chara controller & one way platforms / walls.

Post by Norbo »

Code: Select all

var startingTransform = new RigidTransform(PlayerControllers[id].Body.Position, Quaternion.Identity);
The entity's position is at its center of mass. Starting the sweep from the center of mass will make the ground solidify as soon as a character jumps halfway up through the platform. Collision response will force it up the rest of the way. If that's what you want, then it's fine; but if you'd rather it not push so soon, start the cast closer to the feet.
Strangely, this works perfectly, except very unnoticeable glitches sometimes on landing, due to threading unsafe, or the fact the "test platform" is actually a cube with an axis constraint dropped on a static mesh?
I'd guess it was related to some subtle update ordering issue. Performing certain operations at the wrong point in an update can end up using one-frame-old data. Another common issue is managing some physics-related logic outside of a Space.Update(dt) call. Space.Update(dt) could simulate more than one time step in one update call, and unless the external logic is aware of that fact, it won't work quite right.

Unsafe asynchronous access violations are usually invisible until they cause a horrific catastrophe (things disappear, program crashes, etc.).

It might be some side effect of constraints, but I can't be sure. To know more, I'd have to see the behavior in question.
PS: Why did i had to replace Resources.GetBroadPhaseEntryList() (not found anywhere) by Resources.GetCollisionEntryList()?
Resources.GetCollisionEntryList() is from an older version; it was renamed to Resources.GetBroadPhaseEntryList() in newer versions of the engine.
6thVenom
Posts: 12
Joined: Mon Sep 03, 2012 6:31 pm

Re: Chara controller & one way platforms / walls.

Post by 6thVenom »

Oh well, yes i forgot to use the standingHeight/2 offset in the rigid transform constructor, thanks. :oops:
Unsafe asynchronous access violations are usually invisible until they cause a horrific catastrophe (things disappear, program crashes, etc.).
Scary words there!
My physicManager is just a wrapper from my game project to the BEPU lib, doing some ToolBox things for game's entities, but nothing is thread safe here, as every modifications on space.entities are performed on the fly... and entities use little lerps for position anyway (avoiding BEPU jitterings, and netplay false predictions artifacts).
Resources.GetCollisionEntryList() is from an older version; it was renamed to Resources.GetBroadPhaseEntryList() in newer versions of the engine.
Ok, that makes perfect sense now.

Thanks.
Post Reply