#rigid body gravity with area3d

235 messages · Page 1 of 1 (latest)

pine sleet
#

@uncut rain all the stuff should be here for you?

#

i am so very confused

uncut rain
#

You're manually setting the linear velocity. Do not recommend.

#

This will override gravity and causes a myriad of headaches.

#

Instead, apply forces over time to move the player

pine sleet
#

ah so it's my code then LOL

uncut rain
#

apply_central_force( _velocity )

#

RigidBody3D doesn't like to be moved directly, unlike CharacterBody3D which does nothing but

pine sleet
#

right

#

that's my fault I couldn't really find much about rigidbody player controllers

#

so do I even need the lerp for acceleration?

uncut rain
#

Yeah, documentation could use an update there

pine sleet
#

(this is also like my third day with godot, i am very at fault)

uncut rain
#

Lerp shouldn't be needed since it'll accelerate on its own

#

Might want to give it more mass though

pine sleet
#

well that makes my code easier

#

okay it doesn't mvoe now

#

oops

uncut rain
#

You might need to amp up the forces used. Right now they're small numbers for a RigidBody3D

#

Multiply the velocity by a scalar (float) until it's at a level you're happy with

pine sleet
#

ok you lost me again

#

i removed the lerp and now i'm applying force to target velolcity

uncut rain
#

By velocity I mean _velocity not linear_velocity

#

Then apply the central force to move the object

pine sleet
#

apply_central_force(_velocity)```
#

like that?

uncut rain
#

Basically yes. Move speed might need changing though since the method of movement is less direct

pine sleet
#

its

#

so slidey

uncut rain
#

Yep. That's why you might want more mass, and perhaps more friction

#

It's a physics body so it'll act like any object in a physical way

pine sleet
#

right hang on

#

is there an is_on_floor check for this or do i need a sphere raycast

#

oh my friction is zero

uncut rain
#

There's no is_on_floor for RigidBody3D unfortunately.

#

You'll have to do any checks manually.

pine sleet
#

god dammit

#

do I just need to add a bunch of mass so it doesn't keep sliding after i'm not on the input

uncut rain
#

A simple Raycast3D pointed towards the floor would usually suffice

#

Mass helps it accelerate slower. Friction helps it slow down faster

#

You probably don't want immediate acceleration, so a small increase to mass can help there. Friction helps you stop in a timely manner. Friction of 1.0 basically means you're an immovable object

pine sleet
#

so I want like 0.8 friction

#

what sa good move speed then

uncut rain
#

You probably want it reasonably high. I'd start with 0.7 and see how that goes

pine sleet
#

ok i can tweak that later, when i get into the area 3d how do i orient the character to match the gravity direction

uncut rain
#

You need the gravity direction first ... Here's where the fun begins

pine sleet
#

why am i going backwards

#

this is so frustrating haha

#

press w
goes backwards

#

crazy

#

why aren't there tutorials for this

uncut rain
#

func _integrate_forces(state: PhysicsDirectBodyState2D)  -> void:
    DownVector = state.total_gravity.normalized()```
#

This way you always know which way is down

pine sleet
#

where does total_gravity come from

uncut rain
#

From the state sent to Integrate forces

#

It's a built in function

pine sleet
#

oh

#
    # Get input direction
    var direction = Vector3.ZERO
    
    if Input.is_action_just_pressed("jump"):
        apply_impulse(Vector3.UP * JUMP_FORCE, Vector3.ZERO) 
    
    if Input.is_action_pressed("forward"):
        direction.z -= 1
    if Input.is_action_pressed("backward"):
        direction.z += 1
    if Input.is_action_pressed("left"):
        direction.x -= 1
    if Input.is_action_pressed("right"):
        direction.x += 1

    # Normalize the direction to ensure consistent speed when moving diagonally
    if direction.length() > 0:
        direction = direction.normalized()
        
    #_velocity = _velocity.rotated(Vector3.UP, head.rotation.y) 

    # Calculate the target velocity
    _velocity = direction * MOVE_SPEED

    # Apply the final velocity
    apply_central_force(_velocity)

i have this now which just doesnt work in the slighest for whatever reason

uncut rain
#

As in it doesn't move or...?

pine sleet
#

wait I have it moving again

#

ok

#

well there' gravity working in that i come back down from a jump

uncut rain
#

A good start

pine sleet
#

ok other than the fact i move in the wrong bloody direction it works

#

it moves in different directions every time i run the same thing what the hel

#

can i disable a node

uncut rain
#

Depends what you mean by disable.

#

You can pause nodes, but I'm guessing you want something more targeted and specific?

pine sleet
#

no that's alright i don't need to do that

#

ok os how does a down vector help me

#

i set the transform basis y to the down vector?

uncut rain
#

It's always pointing at where your 'feet' should be. You can use it to rotate the player appropriately

pine sleet
#

can you add a max speed to the forces

#

when you strafe is it getting stacked or somethng

#

its defo stacking speed or soemthing

#

sorry I really suck i appreciate all your help

#

okay jumping makes the movement speed so much quicker lol

uncut rain
#

To limit the speed you can either set a drag factor to be realistic. Or for a more arcade feel you can clamp the linear_velocity in the _integrate_forces function

#

Don't do this anywhere else though

#

Eg:

   linear_velocity = linear_velocity.normalized() * max_speed```
#

If you want to separate gravitational and horizontal movement that's a whole other ball game

pine sleet
#

what happened to not changing linear_velocity LOL

#

I just can't seem to get a good balance between mass friction and movement speed at the minute it all just feels weird

uncut rain
#

You don't usually want to do this. If you ever do, it should only be inside the _integrate_forces function. Nowhere else

#

Otherwise you get weird results

pine sleet
#

I see

#

I feel like I'm either moving too slow or moving too fast

uncut rain
#

It's a rollercoaster for sure

pine sleet
#

even with a friction of 0.9 it feels like I don't stop for ages

uncut rain
#

Does the target surface also have friction?

pine sleet
#

the target surface is a csg box

#

i have no clue

uncut rain
#

Check the physics material it's using

pine sleet
#

its not using any material

#

it doesnt have a rigidbody

uncut rain
#

Then add one and set the friction to at least 0.1

#

Static body also uses a physics material. All collision bodies do except for areas

pine sleet
#

yeah i'm not seeing a physics material area

#

i can only add standard or shader

#

am i stupid

#

it doesn't matter cus the box is for testing

#

the planet will have friction

#

ok so down vector rotation

uncut rain
#

There's the material slot

#

At the top

pine sleet
#

yeah thats my fault it wasnt in a static body

#
    DownVector = state.total_gravity.normalized()
    
    rotation_degrees.z = DownVector.z```

is tis not just how to do it
#

no it is not i just fly off

pine sleet
#

ok i am literally never going to figure this out i haet spheres

pine sleet
#

@uncut rain am i meant to rotate it to be perpendicular to down vector but then the movement forecs will eb jank

uncut rain
#

So, basically the mesh

#

And of course, any Raycast3D you're using to check the ground collisions on

pine sleet
#

uhhhh

#

doesnt the rigidbody also have to be rotated anyway since it's the parent node

uncut rain
#

Ideally you want to avoid that since it'll mess with the physics

pine sleet
#

uhhhhhhh

#

was setting the z to down vector z even correct

#

like i have to rotaet the head and the mesh and the collision anyway?

uncut rain
#

No. The default direction for an object is to face forward. Getting a downward vector and matching the rotation would only give make you face towards the floor. Probably not what you wanted

#

But, if you know the down direction you can calculate the correct rotation using quaternions

pine sleet
#

i need to face the negative down vector

#

what the heck is a quaternion

uncut rain
#

Quaternions are a form of abstraction for 3d rotation. You should ideally avoid using direct changes to rotation as it can lead to something called Gimbal lock

pine sleet
#

gimbal lock

#

i am in far too deep to walk around a stupid sphere

uncut rain
#

You'll need to cross this bridge eventually for working in 3D. These are fairly common issues.

pine sleet
#

do i have to rotate every child of the rigidbody then

#

just not the rigidbody itself

uncut rain
#

Only the visual elements. Personally, I'd put them all on a single node and just rotate that single node rather than iterate through them all

pine sleet
#

Yeah but if I rotate only the mesh then the camera won't be correct no?

uncut rain
#

You might also need to rotate the collision shape if it's not uniform

#

You can parent a camera to the mesh which will allow it to track it and rotate with it

#

Or, if you're using the add on, set the mesh as the object to follow instead of the rigidbody

pine sleet
#

if I only rotate the mesh the collisions will be wrong anyway?

#

i dont get why i can't just rotate the rigidbody

uncut rain
#

That's why I mentioned the collision shape.

#

That's not always needed, in the case of a sphere for example

pine sleet
#

Yeah so I have to rotate everything anyway, so why not the parent?

#

oh thta's what you meant by uniform

#

unless the collision shape can also be a child of the mesh

uncut rain
#

You can rotate the parent, but you'd need to be smart about it. The variable you want is angular velocity. But like with linear velocity you should avoid setting it directly

#

So you can add angular momentum in the direction of the turn instead

pine sleet
#

ok but can collision shapes be a child of the mesh

#

that's not gonna break anything

#

so i have to calculate the correct rotation with a quaternion given the down direction and then set the mesh rotation to thta

#

can this be done with a CharacterBody3D even

#

?

#

cant be as simple as that

uncut rain
#

Collision shapes need to be a direct child of the parent collider

pine sleet
#

so that's a no

uncut rain
#

CharacterBody3D is a different beast to a RigidBody3D. You'll need to calculate gravity forces manually if you switch

pine sleet
#

ok i won't do that then

pine sleet
#

can i just set transform basis y to rotation quaternion now

uncut rain
#

Not quite. You need to invert the y so add a minus symbol first

pine sleet
#
    var down_vector = state.total_gravity.normalized()

    var rotation_quaternion = Quaternion(-state.transform.basis.y, -down_vector)

    $PlayerMesh.transform.rotation = Basis(rotation_quaternion)
    $PlayerCollision.transform.rotation = Basis(rotation_quaternion)```
#

am i cooking

uncut rain
#

Oh wait, sorry I didn't realise you were already inverting the gravity. That's on me. Yeah, only one needs inverting not both

#

So just the gravity. Remove the one you added for the basis

pine sleet
#

I thought so haha

#

am I alright to just use the basis like that i've seen it before

#

nevermind thats an error

#

is transform.rotation not a thing

#

unity syntax oops

#

i guess i'm just changiong the transofmr basis.y on both then?

uncut rain
#

Try this:


func rotate_to_ground() -> void:
    var gravity = down_vector. normalized()

    var current_down = -global_transform.basis.y
    var target_down = gravity

    var rotation_axis = current_down.cross(target_down)
    var angle = acos(clamp(current_down.dot(target_down), -1.0, 1.0))

    if rotation_axis.length_squared() > 0.0001 and angle > 0.001:
        rotation_axis = rotation_axis.normalized()

        # Scale the torque by angle and align_speed
        var torque = rotation_axis * angle * align_speed
        apply_torque(torque)```
#

Should allow a smooth gradual rotation rather than snapping

pine sleet
#

should this be called under integrate forces

#

is apply torque a custom function

uncut rain
#

Call it in physics process

#

For the first frame, down vector should default to Vector3.DOWN

#

So be sure to add the default

#

apply_torque is a function of the RigidBody3D

pine sleet
#

right

#

well that doesn'#t seem to work 😭

uncut rain
#

What issues are you having?

pine sleet
#

i don't think its rotating anything

uncut rain
#

Try increasing the speed. It's set fairly low.

pine sleet
#

is there a way i can view the game scene from a different camera?

#

it might rotate the character itself, but the camera and stuff isnt reflecting that

#

which is why i dont think it rotates

#

yeah, if i go to the bottom of the planet my head is touching the bottom and not my feet

#

if i set the align speed to much higehr i start bouncing off the planet

#

which is quite interesting

uncut rain
#

I'd need access to my pc to run some tests to be honest. I think I've hit the limit of what I can help with without being able to test it directly

#

You can set the bounce to absorbent if you want to avoid that

pine sleet
#

yeah i have no clue

#

thanks for trying

uncut rain
#

I'll revisit this later once I'm back home rather than at work. Might have some more juicy stuff to play with

pine sleet
#

current_dwown doesnt seem to change at all with these print statements

#

is it meant to chagne lol

uncut rain
#

It's mostly in case it started out at a non zero value

#

So if you rotated it in the scene editor for example, it'll take the new rotation into account

pine sleet
#

i think its keeping me upside down? lol

#

me on the bottom of the planet with it above my head? lol

#

probably some negative needed

uncut rain
#

One more try. After that I'll tap out until I've had a chance to test it myself.

@export var align_speed: float = 5.0

func rotate_to_ground() -> void:
    get_gravity_area()

    var gravity_center = gravity_area.global_transform.origin
    var gravity_direction = (gravity_center - global_transform.origin).normalized()

    var current_down = -global_transform.basis.y
    var target_down = gravity_direction

    var rotation_axis = current_down.cross(target_down)
    var angle = acos(clamp(current_down.dot(target_down), -1.0, 1.0))

    if rotation_axis.length_squared() > 0.0001 and angle > 0.001:
        rotation_axis = rotation_axis.normalized()

        var torque = rotation_axis * angle * align_speed
        apply_torque(torque)

for area in get_overlapping_areas():
    var gravity_center = Vector3.ZERO
    var nearest_area: Area3D = null
    var min_distance: float = INF

    if area.is_in_group("GravityZone"): # Put your gravity Areas in a "GravityZone" group
        var center = area.global_transform.origin
        var distance = global_transform.origin.distance_squared_to(center)
        if distance < min_distance:
            min_distance = distance
            gravity_center = center
            nearest_area = area

    gravity_area = nearest_area```
pine sleet
#

hang on i may have fixed it myself

#

nvm i didnt

pine sleet
#

ok so - the code you sent doesn't work, or i'm doing something wrong

pine sleet
#

this is what i'm trying now:

#
extends CharacterBody3D

func _ready():
    Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)

func _process(delta):
    gravity_direction = (planet.global_transform.origin - global_transform.origin).normalized()   
    
    set_up_direction(-gravity_direction) 
    
    print("Gravity Direction:", -gravity_direction)
    print("Is on floor:", is_on_floor())


func _unhandled_input(event):
    if event is InputEventMouseMotion:
        head.rotate_y(-event.relative.x * SENSITIVITY)
        camera.rotate_x(-event.relative.y * SENSITIVITY)
        camera.rotation.x = clamp(camera.rotation.x, deg_to_rad(-40), deg_to_rad(60))


func _physics_process(delta):    
    if not is_on_floor():
        velocity += gravity_direction * gravity * delta

    if Input.is_action_just_pressed("jump") and is_on_floor():
        velocity += -gravity_direction * JUMP_VELOCITY
    
    var input_dir = Input.get_vector("left", "right", "forward", "backward")
    var direction = (head.transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()    
    
    if is_on_floor():
        if direction:
            velocity.x = direction.x * speed
            velocity.z = direction.z * speed
        else:
            velocity.x = lerp(velocity.x, direction.x * speed, delta * 7.0)
            velocity.z = lerp(velocity.z, direction.z * speed, delta * 7.0)
            
    else:
        velocity.x = lerp(velocity.x, direction.x * speed, delta * 3.0)
        velocity.z = lerp(velocity.z, direction.z * speed, delta * 3.0)
        
    var target_transform = transform.looking_at(global_transform.origin + gravity_direction, Vector3.UP)
    var current_quat = transform.basis.get_rotation_quaternion()
    var target_quat = target_transform.basis.get_rotation_quaternion()
    transform.basis = Basis(current_quat.slerp(target_quat, delta * ROTATION_SPEED))
    
    move_and_slide()```
#

it works but the rotation is a bit wrong so idk why

#

i am so so lost

#

its defo something like this ```f rotation_axis.length_squared() > 0.0001 and angle > 0.001:
rotation_axis = rotation_axis.normalized()

    # Scale the torque by angle and align_speed
    var torque = rotation_axis * angle * align_speed
    apply_torque(torque)```
#

but for the character controller

#

i'll probably need you for thta

uncut rain
#

Working on a demo you can pull apart later. Might need to adapt it to your needs though

#

I'll post it when it's ready

pine sleet
#

thank u !!! i feel really close with this rn but so far

#

hopefully I can get the bare bones just working

pine sleet
#

any update on this?

uncut rain
#

Had a play around and I've got something that mostly works. Had an emergency I had to take care of, so wasn't able to complete it, so I hope to post the whole thing later today assuming no more surprises.

pine sleet
#

Oh please don't worry too much I totally understand! I'll kep hacking away at it for now anyway and see if I can't come up with a solution

#

I feel like I am so close

pine sleet
#
    var gravity_direction = (planet.global_position - global_position).normalized()

    var target_rotation_quaternion = Quaternion(global_transform.basis.z, -gravity_direction)
    var current_rotation_quaternion = global_transform.basis.get_rotation_quaternion()

    transform.basis = Basis(current_rotation_quaternion.slerp(target_rotation_quaternion, delta * ROTATION_SPEED))

    set_up_direction(-gravity_direction)```

I tried this and it almost works but the movement and gravity stuff is a bti buggy. I guess I'll just wait for your demo atp
uncut rain
#

Are you using CharacterBody3D or RigidBody3D. I know you were making some changes recently

pine sleet
#

This is me trying with a CharacterBody3D

pine sleet
#

I'm going to have one last try using self.rotation to see if that works, if not I'm truly stumped and doing something catastrophically wron

#

const speed = 20.0
const JUMP_VELOCITY = 10
const SENSITIVITY = 0.004
const ROTATION_SPEED = 15 

const gravity = 9.8

var gravity_direction = Vector3.DOWN

@export var planet: Marker3D

@onready var head = $PlayerHead
@onready var camera = $PlayerHead/Camera3D

# Hide cursor when game loads
func _ready():
    Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)

func _process(delta):
    var gravity_direction = (planet.global_position - global_position).normalized()
    
    var relative_up = -gravity_direction
    
    set_up_direction(relative_up)
    
    
# Handles camera movement
func _unhandled_input(event):
    if event is InputEventMouseMotion:
        head.rotate_y(-event.relative.x * SENSITIVITY)
        camera.rotate_x(-event.relative.y * SENSITIVITY)
        camera.rotation.x = clamp(camera.rotation.x, deg_to_rad(-40), deg_to_rad(60))

func _physics_process(delta):    
    if not is_on_floor():
        # Add gravity while in the air
        velocity += gravity_direction * gravity * delta

    # Add jump velocity in the opposite direction of gravity
    if Input.is_action_just_pressed("jump") and is_on_floor():
        velocity += -gravity_direction * JUMP_VELOCITY
    
    var input_dir = Input.get_vector("left", "right", "forward", "backward")
    var direction = (head.transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()    
    
    if is_on_floor():
        if direction:
            velocity.x = direction.x * speed
            velocity.z = direction.z * speed
        else:
            velocity.x = lerp(velocity.x, direction.x * speed, delta * 7.0)
            velocity.z = lerp(velocity.z, direction.z * speed, delta * 7.0)
            
    else:
        velocity.x = lerp(velocity.x, direction.x * speed, delta * 3.0)
        velocity.z = lerp(velocity.z, direction.z * speed, delta * 3.0)
    
    move_and_slide()

Yeah at this point I can't get beyond this at all. I'll just wait for the demo

uncut rain
#

Since you're now using CharacterBody3D you might want to try the following. Mesh in this instance refers to the character mesh. Collider refers to the collisionshape you're using (so that collisions match the character).

    var down_vector: Vector3 = -get_gravity().normalized()
    mesh.transform.basis.y = down_vector
    mesh.transform.basis.x = -mesh.transform.basis.z.cross(down_vector)
    mesh.transform.basis = mesh.transform.basis.orthonormalized()
    collider.transform.basis.y = -down_vector
    collider.transform.basis.x = -collider.transform.basis.z.cross(down_vector)
    collider.transform.basis = collider.transform.basis.orthonormalized()```
#

Just call it after your move_and_slide() and it should snap to the gravity.

pine sleet
#

Yeah that's not working either, I found a reddit post that had this code:

        var left_axis = -current_gravity_dir.cross(transform.basis.z) # left_axis is left hand side for player
        var my_z = global_transform.basis.z # Don't change z
        transform.basis = Basis(left_axis, current_gravity_dir, my_z).orthonormalized() # Current player rotation
        up_direction = transform.basis.y # Change up direction```

but I can't get this to work either, I don't know anymore
pine sleet
#

Okay I think I have it almost working now

#
    var target_transform = Transform3D()
    target_transform.origin = global_position
        
    # Find the left hand side of the player
    var left_axis = -gravity_direction.cross(transform.basis.z)
    # Keep the z axis the same
    var my_z = global_transform.basis.z
    # Set target rotation
    target_transform.basis = Basis(left_axis, -gravity_direction, my_z).orthonormalized()
        
    var current_rotation = global_transform.basis.get_rotation_quaternion()
    var target_rotation = target_transform.basis.get_rotation_quaternion()
    
    # Lerp between the two quaternions
    var interpolated_rotation = current_rotation.slerp(target_rotation, delta * 10.0) 
    
    global_transform.basis = Basis(interpolated_rotation)```
#

I have this

#

Just trying to figure out how to get te oriented input direction for mvoement

pine sleet
#

okaaaaaaaaay

#
    var input_dir = Input.get_vector("left", "right", "forward", "backward")

    # Get the camera's global transform basis
    var camera_basis = camera.global_transform.basis

    # The horizontal plane's basis vectors are perpendicular to the gravity direction
    # We can construct them by taking the cross product of the gravity direction with
    # the camera's right and forward vectors. This guarantees they lie on the plane.
    var horizontal_right = gravity_direction.cross(camera_basis.y).normalized() 
    var horizontal_forward = gravity_direction.cross(-camera_basis.x).normalized()

    # Combine the directions
    var direction = (horizontal_right * input_dir.x + horizontal_forward * input_dir.y).normalized()

    return direction
#

i don't know what i;m doing anymore

#

sometimes i press forward and i go upwards so this isnt quite right

pine sleet
#

ok i think this get oriented input is all I ned help with now - im trying to simplify donw:

    var input_dir = Input.get_vector("left", "right", "backward", "forward")

    # Get the camera's global transform basis
    var camera_basis = camera.global_transform.basis
    

    # Orient the input direction using the camera's basis
    var direction = camera_basis.x * input_dir.x + camera_basis.z * (-input_dir.y)

    return direction.normalized() # Important to normalize the direction```
pine sleet
#

@uncut rain is there anyway i can just. rotate the sphere?