lexyeevee

troublesome fox girl

hello i like to make video games and stuff and also have a good time on the computer. look @ my pinned for some of the video games and things. sometimes i am horny on @squishfox



lexyeevee
@lexyeevee

you have a player actor. great. heres one. it's lexy

a small pixel-art fox

she is standing on a slope. you are savvy at physics and understand that:

  1. gravity will attempt to pull her downwards

  2. she will immediately collide with the slope itself

  3. her remaining motion will be projected onto the slope (roughly equivalent to subtracting the normal force)

  4. she will then slide down the slope.

this is bad, because lexy is not trying to move. neither she nor the ground are made of ice, so surely she can hold herself in place.

but again, you are savvy at physics, so you know what holds her there: friction. so you say, ok, add a bunch of friction.

now you have a problem. the good news is that your physics engine is completely homegrown, so you can explain the problem in excruciating detail. the bad news is that it is your problem to solve for the same reasons. and the problem is that your movement code integrates everything upfront, then performs a motion.

that means you have your current velocity (presumably zero), gravity (pointing downwards), and friction (pointing up along the slope). you add them together in some fashion, and the result can never be zero, even with friction's natural capping behavior — this is just how vector addition works. so lexy will definitely try to accelerate in some direction, and the best you can hope for is that she will try to accelerate exactly towards the ground. but working out how to make that happen requires essentially working backwards from gravity (non-trivial because various effects can interact with gravity) and also just feels real dumb to be doing.

so where did you go wrong?

(an aside: the reason this doesn't happen in the demo is that i also have a cutoff on very small movement, so that little rounding-error levels of velocity don't make things shift a pixel at a time over the course of seconds. but that cutoff is too coarse, and in particular it makes some slow objects just not move on monitors with high refresh rates, so i've reduced it dramatically, and that has caused lexy to slide downhill. i thought i'd fixed this before but apparently i was just masking it by accident!)

i think the problem,

conceptually, is that friction happens at the wrong time. it's integrated with velocity and acceleration accumulated over the course of the last tic before movement is attempted, but friction isn't something that happens in a vacuum. i mean, literally, it won't happen if there's nothing else around. friction is an interaction with another object we're touching, and before we start moving, there aren't any of those!

so it seems like friction should only apply between steps 3 and 4 — after colliding with the ground, before actually moving along it. this is the point at which we know we are, in fact, sliding against the ground. and now we even have the ground available, so we don't need friction itself to be a vector at all, which is kinda convenient.

but that's in the guts of basic motion i... hesitate... to put something like friction in the middle of that. also, what happens if you collide and slide again? does friction apply again? probably not. so it only applies specifically the second time through a loop? that's rather weird. maybe it's okay that it's weird? it does add complexity for "out-of-turn" motion though, like being pushed or climbing a ladder, which probably won't have a big perf impact but does feel inappropriate.

other solutions

skip gravity

i could simply not apply gravity to a player on the ground. i've seen mention of that being done before. but that just feels goofy?? they are trying to move downwards, and on a slippery slope they even should move downwards.

also i have a bunch of objects that care about things landing on them, and this implies some weird special cases (in who knows how many places) for detecting that a player is walking on top of them without actually moving into them. and i have enough special cases as it is man. expressing a physics condition is so goddamn hard. i have the unsettling feeling that this would have other unforeseen consequences as well

a possible upside of this is that it could very well skip an entire movement iteration every tic — currently, a walking player tries to move sideways (for walking) + downwards (for gravity), immediately hits the ground, projects along the ground, and then tries to move a second time. without gravity in there, they'd skip the first collision and move exactly sideways from the beginning.

remember the ground

i do in fact remember the ground (tile or actor or whatever) for each actor, so i could use that to get the friction right upfront.

but what does that mean in practice? i guess it means that the motion vector would be projected along the ground before even attempting to move at all (and then that would be shortened by friction). so it's kind of like skipping gravity — if lexy is standing on flat ground, her attempted motion (straight down) will be projected along the ground (horizontal) and always come out zero.

i guess essentially this combines the other two options — it applies friction to the movement vector, but it skips ahead one attempt by making use of knowledge from the previous frame. it would get the friction stuff out of the movement core, which i like, but it would still make objects not push against the ground, which i don't like.


ah, all the options seem nebulously Not Great, which is always a sign of a fun problem. and don't worry, this gets even more complicated with physics problems part 3


lexyeevee
@lexyeevee

maybe the last option works after all

what i'm trying to model now isn't that infinite friction keeps the player at zero velocity, but that zero friction has the player moving directly into the ground

i imagined simply projecting the frame motion along the ground and applying friction to that, which would end up with zero motion into the ground. but i could instead separate the frame motion into normal and tangent parts, apply friction to the tangent part, and then add the normal part back in. now infinite friction makes the tangent part zero, but the normal part is still there and shouldn't actually cause any movement

it does feel a bit weird to talk about applying friction to a motion. it's an acceleration (well, force, whatever) being simulated over a unit of time, so it really applies to velocity. i'm not yet sure how i want to square that.

there's still the question of whether i do this before movement or as part of sliding. i think that will come down to what makes the components easier to juggle — moving, walking, and falling are all separate and i like them to rely on each other (beyond telling move what to do) as little as possible. that, plus the conceptual issue where friction should really apply to velocity and not motion, are strongly inclining me to do it before movement

anyway this is what i was trying to do all along — it's just that the friction vector is (due to code arrangement) calculated before gravity's effect on velocity, so it's like it's acting on old information. if i make it a scalar and make movement code responsible for turning that into a vector at the latest possible moment, i think everything Just Works? maybe


lexyeevee
@lexyeevee

this should work anyway. applying friction is capped, so, it shouldn't matter how strong gravity is; if i just set friction to a real damn big vector pointing uphill, it should work. and lexy's walk acceleration is pretty high so this should work correctly already. right? am i confusing the hell out of myself?

...

hmmmmmm

it looks like i tried to apply a ½ factor to friction in the name of integrating it "correctly", but friction has a hard cap so you can't really integrate it correctly. i knew this was Dubious when i tried it, but it seemed to work, so i left it in. turns out it only worked because of that small-movement cutoff kludge that i recently removed

and if i also remove that ½...

it's fine. lexy holds still and her attempted velocity points exactly into the hillside. lol. lmao

in my defense i conceived of the explanation for the problem a few days ago when i was so exhausted i couldn't do anything but stare into space and think about physics. once i was more awake i just thought about how to fix it, since that part seemed harder. very impressed that i managed to explain in detail how friction needs to exactly precisely cancel gravity without ever thinking "ah hm yes wait, no it fucking doesn't"


You must log in to comment.

in reply to @lexyeevee's post:

I think the answer for cases like that is that you don't project the player's velocity onto the slope, so that gravity causes the player to just collide & stop at the slope rather than sliding down it. You'd probably project the sideways acceleration instead, so the player still naturally walks up/down the slope on input.

in which cases? only when hitting the ground? at what angle, how hard?

currently if lexy falls a ways and hits the side of a hill, she'll slide a bit before getting her footing, and i think that's very cool! and it's a natural consequence of fairly regular physics

(i do project her walk acceleration along the ground already — and gravity naturally makes downhill movement a bit faster! only very slightly, though.)

Could track "midair" state, so when player goes from midair to landing on the ground, you then project player's velocity on that frame, which gives you the little sliding behavior when landing. Could also add a velocity threshold to overcome so in-place hops don't cause the player to subtly slide down the slope.

this feels like it subtly contains the same problem again: for her to slide down the slope but slowly come to a halt, i need friction, which means i need to point it in the right direction to ensure she actually stops!

I think it would likely be the exact same friction you'd use to slow the player to a halt on completely flat ground - which is usually a kind of "selective" friction (like in the original Sonic games, there's actually several kinds of friction & acceleration - there's accelerating up to speed when walking, deceleration when trying to move in the opposite direction to allow quickly turning around, and then friction to slow speed down to 0 when not moving)

this is one of the (very rare) times i'd just fudge it, honestly

naively, do up to 3 raycast checks - the first straight down from the actor's centre to find if they're touching the ground, and if so, the other two to get the (average) angle of the ground they're standing on. if it's relatively flat, great! no movement except player input. if the gradient's too harsh, impart the appropriate side velocity and then factor in gravity

if the first raycast doesn't connect, just factor in gravity as per usual

tl;dr fuck friction, it does not exist. we do not respect friction around these parts

then again it's 3am and i feel like i'm missing a comical amount of context here so like, disregard this entirely i guess

My knowledge here is from physics and like, separate physics engines, so it’s not necessarily directly applicable to a game where you want to put in ad-hoc physics effects (and since rigidbody players aren’t very good). But:

It seems like your collision resolution shouldn’t be separate from forces. You don’t just delete part of the force by projecting out the remainder when you collide with the ground, the ground applies a normal force back up. When you collide, you have a downward force vector into the collision, and the collision produces a normal force perpendicular to the contact and a friction force parallel to the contact. Then you can integrate those normally.

(looking back at your post again to see where this sits relative to your problems and solutions…)

I guess this really is just your first instinct of putting the friction into the loop, just a more principled justification for why that’s right? I also think it might be correct to perform friction for each collision if there are multiple—how many iterations of movement are you really having in one physics step?

the normal force is exactly the difference between the original gravitational force and what you get when projecting it along the surface. i'm just skipping calculating it directly because i have a convenient "project onto" api already, and i don't need the normal itself for anything.

and yeah arguably the friction for each collision should be different. i've been thinking about that, but i don't know how to make sense of it. if you're trying to move 20 units along a surface, and that surface says you should lose 10 units of that due to friction, but you would hit a new surface or maybe lose contact with this surface after 15 units, then... what the fuck happens there? i mean i'm already fudging this by arbitrarily picking one actor to be the "ground" when it's very easy to be straddling two. (i also glossed over the part where the player is not actually subject to friction while walking — the whole way feet work is to use friction to propel yourself, so having friction counteract walking would be total nonsense. but friction is what keeps you in place on a hill.)

also 2 iterations is pretty common (one for diagonal into the ground, one for along the ground) but 3 would be entirely expected for walking onto the bottom of a hill or something. and then there's another one for ground adherence which is a whole damn thing

Perhaps you shouldn't actually be modelling Lexy as having friction. She's not just a box being pushed around, she should be reacting to what's going on. Instead maybe as having a desired motion (depending on keys), and some ability to apply force to follow that velocity. If on ground the force that can be applied would be greater, so on a slope she's just able to apply exactly the force to null out velocity. You'd keep the slide down slopes if you fall further, if that's greater than the amount she can compensate for.

said so in another comment, but she isn't subject to friction while moving, only when trying to hold still. friction is, after all, why you don't slide down any slope you try to stand on.

and the whole reason i do this is that i'm already doing what you describe, but applying exactly the right force to cancel out the velocity she will have in the future after it's been projected on the slope is essentially the same problem i have here. i thought just throwing her max acceleration at it and calling it friction would help because friction specifically can't make her move backwards, but the vector math doesn't quite work out.

And of course, in real physics there are two kinds of friction: static friction (the force needed for a stationary object to start moving) and dynamic friction (the force needed for a moving object to keep moving). The coefficient for static friction is higher than dynamic friction (so if you're not already sliding it's easier to stay that way, but once you start sliding you tend to keep sliding for a while).

... I initially thought that this distinction might point to a posisble way forward, but now that I've written this much I'm not seeing it anymore.

Is friction the only thing that holds you in place as a humanoid standing on a slope? Aren't you positioning your feet on the ground and exerting force (with your muscles) to stay standing in place on the slope?

hmm my intuition is that you're using your muscles to angle yourself upright — the natural position is to stand perpendicular to the ground, in which case you'd topple over

but whether i model it as internal or external i still have the same problem. treating it like friction is only somewhat more convenient because it keeps walking code further away from motion code

If you mentally model the ground as a microfacet environment, then friction can act in almost any direction it needs to - it just resists motion/impulse - because for any specific impulse direction, there will be a bunch of microfacets that can resist that. The shape of the microfaceting means it will be able to act at a greater magnitude in some directions (along the surface) then others (perpendicular to the surface). But yes for your typical "standing there on a moderate slope" situation, friction has a very large envelope in all directions that it can act. So applying it very very late - to first resist impulse (static friction) and then to resist velocity (dynamic friction) is probably the right answer.

you add them together in some fashion, and the result can never be zero

I... think that might be wrong? I mean Floating Point Math etc./ maybe I'm wrong and you're in a constrained programming environment where you don't have the cycles necessary but I feel decently certain this is solvable. Can we see your code for this?

i mean, if you have friction going up along a slope, and gravity pointing straight down, then they're never going to cancel out. at best the sum can point perpendicular to the ground but rigging that to happen in advance, without simulating gravity a second time, is impractical

in reply to @lexyeevee's post:

It is morning and my brain is still starting up and I don't have enough juice to comprehend your posts completely, so please don't take offense if I'm giving you information that you already are integrating into your approach, but it seems like you might be falling into one of the physics naming pitfall traps.

In something of the inverse of how physicists have seen fit to give energy at least four of five different names depending on the situation, there are actually two different frictions with nearly identical equations, but subtly and critically different semantics: static and dynamic friction. there is also drag, which gets lumped in with friction but is also different.

static friction applies to objects that are not moving - well, surfaces that are not moving relative to each other, so that includes people walking and rolling wheels and such. the equation for static friction is Fsf = μs × n where Fsf is force of static friction, μs is the per-material-pair constant, and n is the normal force. However, Fsf can't be used directly - it's actually a threshold. Any force less than Fsf gets absorbed as if the two objects are one. The terrible secret of structural engineering is that bolts aren't supposed to hold shear forces - instead they supply the normal force so that Fsf can hold the building up. Any force greater than Fsf prods the objects into relative motion, at which point the objects are outside of the jurisdiction of static friction, and fall under the purview of:

dynamic friction is the classical box-sliding-down-a-slope friction: the equation is Fdf = μd × n and is basically the same as static friction except that where Fsf is a threshold, Fdf is a force - independent of speed (aside from direction), Fdf produces the gentle parabolic displacement graphs typical of gravity - right up until the object hits zero velocity and Fsf takes over.

In real life, μs is typically 1 to 1.5 times μd, but it's theoretically possible to be arbitrarily large, as long as it's greater than μd.

drag behaves like Fsf but is proportional to speed - it shows up less in real life than one might think, but computational physicists like it

All this to say that if your character is standing still, it is within your rights to look at the forces after they have been tallied and unilaterally zero them so long as they are less than Fsf