How to stick an entity to a "floor" surface?

Hi everyone!
I’m a bit of a noob here, please bear with me.

I currently i have my “player” object moving forward on z axis and im trying to make it “stick” to the surface below in this case a cylinder, but the shape might change to have curves so its not a straight cylinder and i would like to create a gravity force for it to stick to the cylindric surface? Im also using a raycast downwards from my vec3 position, but it doesnt seem to respect the rotation of the entity.

Hello, the gravity force works once you set a rigidbody and a collision component to the player (also on ground or player will go trough) and you use the right commands to move the player. You have also to have a weight set for your player and a gravity in your game settings.

1 Like

Hi, thanks for the answer.
but i think i must have asked it the wrong way.
I have set the Game setting’s gravity to 0,0,0.
All my physic objects have rigid bodies and colliders.
I want to create a similar gravity effect of Mario Galaxy, but instead of a planet i want to use cylinder, because my character will be running towards the length of the cylinder and probably jump on the cylinder.

Im also raycasting down from my “character” to detect normals on raycast hit and rotate the “character” to match the surface angle without success :C anybody has any insights on this?

Thanks,
Fch

Without an image, it’s a bit hard to visualise what you have.

In this case, I would apply a force every frame on all of the objects that are moving (the player, enemies etc) towards the centre axis of the cylinder.

Looking from the side, it be something like this where the axis of the cylinder is along world Z:

As for keeping the player ‘upright’ on the cylinder, you have the right idea but this is pretty tricky. You effectively have to construct a transformation matrix with the correct rotation and use the rigidBody.teleport to rotate the rigidBody to the desired rotation.

Getting the correct rotation can be done via calculating the new ‘up’, ‘forward’ and ‘right’ vectors based on the position of the player on the cylinder.

Get the normal of the cylinder either by raycast or by simply getting the difference between the player position and the cylinder axis and normalising it. That will give you the ‘up’ vector.

Cross the player’s ‘forward’ vector new ‘up’ vector, that will give you the new ‘right’ vector.

Cross the new ‘right’ vector with the new ‘up’ vector, that will give the inverse of the ‘forward’ vector which is needed due to a left handed coordinate system.

Then you can construct the transformation mat4 like so:

var transform = new pc.Mat4(
    x.x,
    x.y,
    x.z,
    0,
    y.x,
    y.y,
    y.z,
    0,
    z.x,
    z.y,
    z.z,
    0,
    0,
    0,
    0,
    1
);

Where x = right vector, y = up vector, z = inverse forward vector.

Now you can get the euler angles from the mat4 transform and pass that to the rigidBody teleport function.

(This is untested and off the top of my head so there may be some errors in my math or some glitches).

If you still have a problem after trying to implementing this, post the project link to the forum with your next issue.

2 Likes

Thank you! i will try this out and post my results.

@yaustar @ayrin
hey there,
I wanted to ask about applying gravity to Static object
can I somehow use gravity to change its orientation or do I have I to change its angles?

Because it is “static”, forces that are applied to it (including gravity) don’t affect it. If you just want to change it’s direction immediately, use rigidBody.teleport.

If you want something that is more gradual (e.g a rotating door), consider setting the rigidBody as kinematic and use applyTorque.

hey, thanks @yaustar
in more details:
I like to change the normal of the object (if he had one) to align with the normal of the surface at evey point
so I want the Static entity to stick to the surface.
I have the normalized normal of the point using raycast… but I dont know how to apply it to the entity

@yaustar
I was trying to use lookAt method but I couldnt make the rotation correct…
any tips?

That’s a little tricky. You would have to recreate a matrix with the new orientation. In a nutshell:

Edit: I noticed I have mentioned the same thing above in more detail: How to stick an entity to a "floor" surface?)

  • The normal of the raycast intersection now represents the the entity’s new y axis
  • Cross the normal of the raycast intersection with the entity’s z axis to get the new x axis for entity
  • Normalise the new x axis
  • Cross the normal of the raycast intersection with the new normalised x axis to get the new z axis for the entity
  • Set this data into a 4x4 matrix and create a quaternion from the matrix to represent the rotation
  • Use the quaternion to set the rotation of the rigidBody of the entity

The order of the crosses for the vectors may need to change but that’s the gist. This becomes a lot trickier if you are using a dynamic rigidBody as depending on your needs and may have to dig deeper into the physics engine.

great, I am trying it (meanwhile no success)
in the explanation above you mentioned that the new z axis is the inverse vector, does it need to be inverse again?

Playcanvas is using a right handed coordinate system so positive Z is actually the ‘back’ vector of the entity.

The order of the crosses matter so it be worth doing this on a simple flat horizontal plane to start as you know exactly what the values should be when you are debugging.

hey, so I think it is working when I use the negative new z in the matrix
(another issue is that now it is working on a tilted plane or other simple things, but for some reason not in my scene… still checking why)

code:

            new_y.copy(result.normal).normalize();
            this.entity.rigidbody.teleport(result.point); 
            helpz.copy(new pc.Vec3(result.point.z,result.point.z,result.point.z));
            //var back = new pc.Vec3().cross(pc.Vec3.RIGHT, pc.Vec3.UP);
            new_x.copy(new pc.Vec3().cross(result.normal, helpz));
            new_x.normalize();
            new_z.copy(new pc.Vec3().cross(result.normal, new_x));
            new_z.normalize();

             
            var transform = new pc.Mat4(
            new_x.x,
            new_x.y,
            new_x.z,
            0,
            new_y.x,
            new_y.y,
            new_y.z,
            0,
            -new_z.x,
           - new_z.y,
           - new_z.z,
            0,
            0,
            0,
            0,
            1
        );
            angle = transform.getEulerAngles();
            this.entity.rigidbody.teleport(result.point,angle);

This line is odd, why do you do this?

dirty code, I dont rememeber why but for some reason
vec3.One * z didnt work so I wrote it like that (where z is the entity’s z axis)
I can also clear it a little bit because I now I double teleport for no reason really…

thank you so Much! I am suppose to know linear algebra quite well but… I guess I dont remember anything :disappointed:
so really… thanks !

What were you trying here? It liked like you where trying to scale a vector by 1. Were you trying to invert it?

If so, look at vec3.scale() https://developer.playcanvas.com/en/api/pc.Vec3.html#scale