#How do I allow turning during my Slide state?

195 messages · Page 1 of 1 (latest)

compact osprey
#

I have a Slide move that currently doesn't allow me to turn, instead it forces me to move in the direction I was moving in before the slide was initiated.

I have made several attempts to fix this, but I haven't been able to find a way to do so without breaking the special velocity calculations that take place during the Slide.

I honestly don't know if this is an issue with my Slide state or my other functions that handle changing the movement direction and accelerating. I'd really appreciate some help from any Vector math wizards that may be able to assist.

#

Here is the Slide state:

func _on_sliding_state_physics_processing(delta: float) -> void:
    _enable_gravity(delta)
    _allow_movement2(delta)

    floor_direction = get_floor_normal()
    var ray_normal: Vector3 = ((feet_front.get_collision_normal() + feet_back.get_collision_normal()) / 2.0)
    var slope_direction: float = Vector3.DOWN.dot(horizontal_velocity.slide(floor_direction).normalized())

var floor_perpendicular: Vector3 = Vector3.UP.cross(floor_direction).normalized()
    var floor_downslope: Vector3 = floor_perpendicular.cross(floor_direction) # A vector that always faces down slope.

    var velocity_normalized: Vector3 = floor_downslope

    if horizontal_velocity.length_squared() > 0.0:
        velocity_normalized = horizontal_velocity.normalized()

    var acceleration_downslope: float = floor_downslope.dot(Vector3.DOWN)
    var acceleration_velocity: float = (acceleration_downslope * velocity_normalized.dot(floor_downslope))
    var acceleration_sign: float = signf(acceleration_velocity) # Returns +1.0 when sliding down, -1.0 when sliding up, and 0.0 when sliding on flat ground.
    var acceleration_max: float

    if acceleration_sign == 1.0:
        acceleration_max = slide_acceleration
    elif acceleration_sign == -1.0:
        acceleration_max = slide_friction

    var accel: Vector3 = ((velocity_normalized * acceleration_velocity) * acceleration_max)

    var new_velocity: Vector3 = (horizontal_velocity + (accel * delta))

    if not is_zero_approx(slope_direction):
        horizontal_velocity = new_velocity
    else:
        accelerate(0.0, delta)
ocean wagon
#

Still didn't find any solution.....😢. Well I can't blame others too. Your code is already too complex....... At this point. I'd try to completely understand my whole code and try to debug it myself. It's not easy for others too to just go through your whole code. Only you can. I'd actually try to redesign my code structure as well.

#

Try distributing your code in smaller and absolute modules. so that it is certain that they work perfect.

#

Then start combining them. As long as you're certain that certain part is absolute, debugging will become easier.

heavy pelican
#

You need to figure out how to apply this logic into your code

  1. Read input
  2. Use input value to rotate velocity of global Y axis
compact osprey
#

Thank you, I'll see if I can figure it out when I get home later. I think what I'll do is stop trying to tie in my movement function and just hardcode the logic directly into the slide state

#

I can work out the rest later

compact osprey
#

Aight I'm gonna see if I can figure this out

#

I'm gonna remove all of the complicated calculations and try something super basic like

horizontal_velocity = movement_direction * slide_speed
heavy pelican
#

well, it's not exactly hard, but you need to know what you are doing

compact osprey
#

lmao ain't that the truth

heavy pelican
#

for now, let's say pressing right is rotating it right

compact osprey
#

That's the real trick isn't it?

heavy pelican
#
if right:
  velocity = velocity.rotated(Vector3.UP, deg_to_rad(1.0))```
#

the real problem is, how do you want control to work
after all, you need to translate player input to "rotate left or right" command
while the rotating logic is just like above

compact osprey
#
if Input.is_action_pressed("move_right_digital"):
        horizontal_velocity = horizontal_velocity.rotated(Vector3.UP, deg_to_rad(1.0))
    elif Input.is_action_pressed("move_left_digital"):
        horizontal_velocity = horizontal_velocity.rotated(Vector3.UP, deg_to_rad(1.0))
heavy pelican
#

if right is 1.0, then left is -1.0 😄

compact osprey
#

Let me take a quick video clip

heavy pelican
#

also, you can do that with final velocity

#

no need to do it with horizontal velocity only

compact osprey
#

So basically you get a brief burst of speed, and then you can steer a little bit by holding left or right

#
horizontal_velocity += (movement_direction * slide_impulse)

This seems to accomplish the first part

heavy pelican
#

yep, above code should allow that kind of rotation

#

I'm not sure what is movement_direction value there, but something tells me that will mess up with the rotation logic
I would prefer to use -model.global_basis.z for slide direction

compact osprey
#

movement_direction is a vector3 that determines which direction I am moving in

#

But I don't need to use it, I just thought that it would be easy since that's what I've done for most of my other movement states

#

I have this:

# Handles directional input.
func _process_input() -> void:
    input_direction = (Vector2(Input.get_axis("move_left_digital","move_right_digital"), Input.get_axis("move_forward_digital", "move_backward_digital")))
    #input_direction = Input.get_vector("move_left_digital", "move_right_digital", "move_forward_digital", "move_backward_digital").normalized() # This causes drifting that can't be fixed with deadzone. This is due to a bug with get_vector().

    if input_direction.is_zero_approx(): # Due to a bug in the get_vector() method, this conditional branch is necessary to handle inputs seperately for digital and analog.
        input_direction = Input.get_vector("move_left_analog", "move_right_analog", "move_forward_analog", "move_backward_analog", stick_deadzone)

Can we reuse this?

heavy pelican
#

sure

#

with that you get input_direction

#

then you need to "translate" that into "rotate left or right"

#

how, is depend on what kind of control you want player to execute

compact osprey
#

The way it works in Pseudoregalia is that if you hold left, right, or back, it will just steer you in that direction

#

Apparently the whole game was made in Unreal Blueprints so I have no idea what the code would look like

#

So let me make sure I understand this: global_basis.z is a Vector3 that represents moving backward, and the opposite would be -global_basis.z

#

And global_basis.x is the same but for left and right?

heavy pelican
#

in that game, can player model face camera, or camera will always be behind player ?

compact osprey
#

The player can face the camera, but only when not moving

compact osprey
#

So basically the same as my project

heavy pelican
#

what happen when you face camera then press dash ?

compact osprey
#

Let's find out

#

She do be sliding towards the camera, which means I was wrong

heavy pelican
#

then while she's sliding, when you press right, where did she rotates to ? her left ?

compact osprey
#

Still to the right, actually

#

The controls appear to be relative to the transform basis and not the camera

#

If my understanding is correct

heavy pelican
#

then how do you want yours to work ?

heavy pelican
compact osprey
#

Well the way it's handled in this game feels very natural and intuitive

#

So honestly I'd be cool with an exact copy, or a close approximation

heavy pelican
#

if you want to follow that then, just use global_basis.z as movement direction, and above code to rotate based on input. then you should be fine

compact osprey
#

This doesn't appear to be working

if Input.is_action_pressed("move_right_digital"):
        horizontal_velocity = horizontal_velocity.rotated(Vector3.UP, deg_to_rad(-1.0)) * -global_basis.z
    elif Input.is_action_pressed("move_left_digital"):
        horizontal_velocity = horizontal_velocity.rotated(Vector3.UP, deg_to_rad(1.0)) * global_basis.z
heavy pelican
#
horizontal_velocity = -global_basis.z * speed
if Input.is_action_pressed("move_right"):
  horizontal_velocity = horizontal_velocity.rotated(Vector3.UP, deg_to_rad(slide_rotation)
elif Input.is_action_pressed("move_left"):
  horizontal_velocity = horizontal_velocity.rotated(Vector3.UP, deg_to_rad(-slide_rotation)```
compact osprey
#

That also doesn't work. Hmm...

#

Here is the whole thing

func _on_sliding_state_physics_processing(delta: float) -> void:
    _enable_gravity(delta)
    #_allow_movement2(delta, slide_impulse, true)

    if velocity.length() < 0.5:
        _state_chart.send_event("Idle")

    var slide_rotation = 1.0
    horizontal_velocity = -global_basis.z * slide_impulse
    if Input.is_action_pressed("move_right"):
        horizontal_velocity = horizontal_velocity.rotated(Vector3.UP, deg_to_rad(slide_rotation))
    elif Input.is_action_pressed("move_left"):
        horizontal_velocity = horizontal_velocity.rotated(Vector3.UP, deg_to_rad(-slide_rotation))

    accelerate(0.0, delta)
heavy pelican
#

then something else is messing with your horizontal velocity

compact osprey
#

I've commented out everything else in the state except for the relevant code. It just moves me in one direction forever and won't let me turn. There must be something else outside that is causing this

#

Could it be this?

func update_velocity() -> void:
    horizontal_velocity = horizontal_velocity.slide(Vector3.UP)
    horizontal_velocity = horizontal_velocity.limit_length(MAXIMUM_HORIZONTAL_VELOCITY) # Sets the maximum velocity limit for horizontal movement.
    velocity = (horizontal_velocity + (Vector3.UP * vertical_velocity))

It's the only thing that I can think of that might be interfering

#

That's the only function being called outside the sliding state that changes the velocity

#

Let me try something real quick

#

Okay no I'm fairly confident that function isn't the cause. If I call it only in my Idle and Run states I still can't turn or decelerate. The slide state must be the issue

compact osprey
#

I removed it and now I am getting behavior more similar to what I want, but it's still not quite right. Holding left or right makes me actually rotate rather than turn, if that makes any sense

heavy pelican
#

try to change rotation code
instead of rotating velocity, rotate the body instead

compact osprey
#

I like moments like these where unexpected bugs do amusing things

#
if Input.is_action_pressed("move_right"):
        global_basis.z = global_basis.z.rotated(Vector3.UP, deg_to_rad(-slide_rotation))
    elif Input.is_action_pressed("move_left"):
        global_basis.z = global_basis.z.rotated(Vector3.UP, deg_to_rad(slide_rotation))

This is what caused that

#

I'm assuming that's what you meant by body, right?

heavy pelican
#

this is where using rotation is actually easier than basis
rotate_y(deg_to_rad(slide_rotation))

#

if you want to use basis then

var bas = global_basis
bas.z = bas.z.rotated(Vector3.UP, deg_ro_rad(slide_rotation)
bas.x = bas.y.cross(bas.z)
global_basis = bas```
or
```var z = global_basis.z.rotated(Vector3.UP, deg_ro_rad(slide_rotation)
global_basis = Basis.looking_at(z)```
compact osprey
#

Okay so it's not actually changing my movement direction, it's just roating my character

#

I can try the basis one real quick

#

Nope that does the same thing

heavy pelican
#

well, try both
rotating the velocity and body

compact osprey
heavy pelican
#

👍🏻

compact osprey
#

And hey look at that, it even works with my fancy acceleration calculations

#

Thank you once again, you've been a tremendous help

heavy pelican
#

no problem

compact osprey
#

I guess the only question now is if I can tie in my input_direction to simplify this further, and allow for holding back to turn as well as in Pseudoregalia

heavy pelican
#

read your input X value

compact osprey
#

What about for backward? That would be a y value right?

heavy pelican
#

I'm talking about rotating left and right

#

what is it with backward ?

compact osprey
#

Oh I was just wondering how to make it like that game I showed you earlier. If you hold back in that game it will also turn you while sliding

#

I believe it turns you in the direction you are always turning in

#

It's not required, I was just curious

heavy pelican
#

is the turn the same when pressing back as left / right? not sharper or anything ?

compact osprey
#

Correct, the turn rate is the same

#

I don't know if being able to turn by holding backwards is an oversight or intentional, but it feels intuitive. Maybe it's just a side effect of how they handle their velocity

heavy pelican
#

then you just need to figure out "back direction" is closer to left or right

#

then rotate that way

compact osprey
#

I believe lerp_angle() and rotate_toward() do that automatically IIRC

#

Oh that's a form of acceleration. Hmm maybe that won't work

compact osprey
#

Hmm, so if I slide to the left or right, it will still turn. That's not desired. I'll have to figure that out another time

compact osprey
#

@heavy pelican
It turns out that this solution isn't correct after all. It continues to rotate me even if I'm already moving in the direction I'm holding in.

I think what I need to do is refactor my basic movement so that accelerating and turning are split apart, so I'm trying to figure that out now.

I'm sorry that your efforts were wasted 😔

#

I'll keep at it though, I'll get it eventually

heavy pelican
#

Yes of course
That's exactly what previous code works
If you want to make slide rotates to point to where joystick is pointing then the math is a little bit different

Simply put

  1. You find the "movement direction " intended by joystick (you should have this already in your walking logic)
  2. You find the signed angle between velocity and the intended movement direction
  3. You use the result to rotate left or right
compact osprey
#

Oh nice, it works!

#

Hmm this still isn't quite right though. The turn speed is nearly instantaneous and it seems to always fully turn me towards the new direction even if I let go of the key before it reaches that point. I feel like this is a step in the right direction though

#

I created a new function for testing purposes, so that it wouldn't interfere with the movement I already have.

func slide(delta: float) -> void:
    movement_direction = Vector3(input_direction.x, 0.0, input_direction.y).normalized()
    movement_direction = movement_direction.rotated(Vector3.UP, camera.rotation.y)

    var signed_angle = horizontal_velocity.signed_angle_to(movement_direction, Vector3.UP)
    horizontal_velocity = horizontal_velocity.rotated(Vector3.UP, signed_angle)
heavy pelican
#

You are almost there
What's lacking is, you don't rotate the amount of signed angle. You use signed angle to do rotation like previous logic

#

To determine rotation direction

compact osprey
#

Hey sorry haven't tried this yet. I've been so exhausted from work that I haven't found the time to work on it

#

I did want to ask something related though. For basic movement in general does it make sense to split acceleration and turning apart? Is that a common or recommended pattern?

heavy pelican
#

Usually those two are combined in one logic

#

BTW for above question
Clamping signed_angle to maximum rotation speed can also work

compact osprey
compact osprey
#

I'm sorry I'm a bit confused. You're saying I need to use the signed_angle to rotate something?

#

I'm not understanding your instruction

heavy pelican
#

Your code is correct but rotates too fast, right ?

#

In that case, just clamp signed angle to rotation speed

compact osprey
#
signed_angle = clampf(signed_angle, (-rotation_speed * delta), (1.0 * delta))
heavy pelican
#

No

#

Try using
-deg_to_rad(1.0), deg_to_rad(1.0) first

compact osprey
#
var signed_angle: float = horizontal_velocity.signed_angle_to(movement_direction, Vector3.UP)
    var turn_rate: float = deg_to_rad(rotation_speed)
    signed_angle = clampf(signed_angle, -turn_rate, turn_rate)
    horizontal_velocity = horizontal_velocity.rotated(Vector3.UP, signed_angle)
#

Oh yeah yours is better

#
signed_angle = clampf(signed_angle, -deg_to_rad(1.0), deg_to_rad(1.0))
#

Yeaaaah there we go, now it's working

#

Well alright, I think I may finally have it

heavy pelican
#

👍🏻

compact osprey
heavy pelican
#

Nice

compact osprey
#

Now my next question is: Should I try and refactor my basic movement to use this as well, or should I only use this for the sliding?

#

Technically my movement is working fine otherwise, but it does seem a little extra to create a function just for this one edge case

heavy pelican
#

The golden rule is
If it works, leave it be
Player doesn't care about your code
Player only knows if the game works good or not

#

Unless, you have good reason to redo the code

compact osprey
#

That sounds like a good rule

#

Well... you already told me that acceleration and steering are usually coupled together so I guess I shouldn't split them apart My thought process was that it made sense to do that since it would give me a finer degree of control over my movement

#

I mean, cars work that way so it makes sense to me at least

heavy pelican
#

Note that my code also has some parts where it uses special rule
And in most games, there will be something like that

compact osprey
#

Oh for sure, I expected as much

#

In my wall run state, I only call accelerate() without allowing for turning. In my slide state it's the opposite, I only wanted steering without acceleration. That's what lead to this whole ordeal.

I had thought the fix for that was to split up my movement into two functions instead of one.

heavy pelican
#

In my code
Enemy and player uses same class
Player uses control states to receive control and move, enemy uses direction state to receive direction from fsm to move
Then there is some cases. Player can change to fly state where gravity doesn't apply
Or both can enter root motion state where now movement is determined by played animation

And maybe some more I don't remember

There are always edge cases that will be better to have separated logic

compact osprey
#

Yeah that makes sense to me. I have a Player3D class that my Player script inherits from, and my Player has a State chart which handles all the movement states

#

I also have states where gravity doesn't apply, like Air Dashing

#

Thus far things have actually been going pretty well, all things considered. But my lack of experience in Vector math has definitely made things a struggle. I have to claw for every inch it seems like

heavy pelican
#

Don't worry
I was like that too few years ago

#

Struggle is part of learning

compact osprey
#

I started roughly a year ago. I can't shake the feeling that I should be farther along than I am currently. Is that normal?

heavy pelican
#

Now vector math might be easier for me
I still struggle at making good combat , level design and so on

compact osprey
#

To be fair though, I had zero applicable skills

#

Well except for level design, I'm already experienced at that thanks to my childhood

heavy pelican
#

Try doing small 2d platformer as side project
No need for animation. Just use godot icon for everything

It can teach you lots of things including simpler vector math

compact osprey
#

Hmm I could go back to my Zelda-clone. It's a top down game with simple movement

heavy pelican
#

Skipping 2d was my worst mistake in learning godot
I can save so much time if I started in 2d

But no, I had to make the next Deus Ex 😂

#

If you do 2d, I suggest Mario
You have to deal with horizontal and vertical movement which is similar to your 3d game

compact osprey
#

Oh I do have a Castlevania-like project I was working on a few months ago

#

Using what I've learned I could probably improve on it quite a bit

#

I've made more progress on my Mega Man Legends project than all of my others combined though. I'm a lot more invested in it even though it's way harder

heavy pelican
#

Yep
Try doing like super jump, dash, slide, wall jump, double triple jump and so on

#

If you can do that in 2d, converting the logic to 3d is simple

#

You just add one more dimension 😂

compact osprey
#

Well I already have Air Dashing and Air Jumping. I can simply increment a variable and can jump or dash as much as I want

#

That was surprisingly easy, I didn't even need help for those

#

State machines help so much

#

Wall Jump is so much easier in 2D. I'm still working on getting it working right in 3D

heavy pelican
#

Try rewriting that game with your current knowledge and you'll see how much your wisdom grows

#

Or if you don't have time,
Open that project and read your code
You'll soon find how much you think can be written better

#

That's the sign of growth
We make mistakes and learn some wisdom. Then with new wisdom we rewrite the code to better state

That's the cycle of doomed game devrlipment, fated to never reach completion and getting released 🤣

compact osprey
#

Is it normal to have to ask for help so much? There have definitely been some things I've done on my own, but I feel like nearly every mechanic I've implemented has required someone to teach me how to do it

#

But tutorials are useless, so I don't see any other way to learn

heavy pelican
#

It's fine as long as you end up understanding what makes it work

#

Like code above. You understand right ? Then you are OK

compact osprey
#

Absolutely, I've been studying at work too. Vector math in particular. I have been having trouble finding good info on certain things though, like sign() for instance

#

I know what it does, but why does it exist? What do we use it for?

heavy pelican
#

Don't worry about things existing without you knowing what it's for
No one uses everything

#

I've been using godot since 3.0 and I probably only uses 20% of whole godot feature

compact osprey
#

That's a fair point, I don't think I could ever fully utilize all the engine has to offer

#

I do feel like my grasp on the basics is still lacking though. I'd like to improve my "tool kit"

heavy pelican
#

You are only 1 year right?

#

I don't get "good" until I'm on 3rd year

compact osprey
#

Yeah I'm just a baby

heavy pelican
#

Before it I'm the same as you
Asking everything

compact osprey
#

lmao

#

That is reassuring though, and I do appreciate all the help you given me

heavy pelican
#

Heck, back in the day we don't have move and slide
I got someone to teach me how to do something similar with script

compact osprey
#

The constant struggles and failures are... very disheartening. I would definitely have given up by now were it not for this community helping me learn

#

I knew game dev was hard going into it, but I greatly underestimated the level of difficulty

#

This is actually insane, I don't know how people do this

heavy pelican
#

No one is ready to make a game first time around
We just make what we can make
My original vision is barely recognizable in current game

compact osprey
#

I guess we just have to keep chipping away at it

#

That's the only way right?

heavy pelican
#

Yes

#

There will be time when then only way forward is to accept the difficulty and do what we can

#

You might also need to learn 3d modelling, at least basic / low poly
Having that skill will broaden your options by a lot

#

That's another 2 years to learn 😄

compact osprey
#

Yeesh, yeah my plan was to hire an artist. I've got some money saved up for that