Avatar

fox flux DELUXE

@fox-flux / fox-flux.tumblr.com

a puzzle-platformer about turning into things, by @lexyeevee
Avatar

after many bumps in the road there is at long last a

which has been described as "longer than i expected" and "how the fuck do i get that heart??" and "am i supposed to be able to do this"

this is my little puzzle-platformer about turning into things and collecting candy. i've been working on it for a long time and it's still not done but i hope you enjoy what there is so far! especially because one day it will be done and it would be cool if you bought it

until then you can get intermittent builds and early screenshots and stuff via patreon. if you're especially daring you can get in my dev discord and see me complain about how pixel rounding breaks pushing things in some cases or whatever! exciting

Avatar
reblogged
Avatar
fox-flux

this already worked, but i just rewrote it and now it works a second time, and that's nice

(the goal is to make grumblebees able to follow a path too)

previously the platform just figured out what its velocity should be and moved at that speed in the direction of the next point. now, the platform figures out exactly what position it should be in and moves exactly there. the difference is absolutely nothing but it'll be easier to add circular motion now

wahoo

i didn't realize before but i didn't even have support for a looping path! the only moving platforms i'd ever made just moved back and forth along a polyline and reversed at the endpoints

and the demo doesn't even have moving platforms so. watch out. because now they are so easy for me to make it's great

Avatar

this already worked, but i just rewrote it and now it works a second time, and that's nice

(the goal is to make grumblebees able to follow a path too)

previously the platform just figured out what its velocity should be and moved at that speed in the direction of the next point. now, the platform figures out exactly what position it should be in and moves exactly there. the difference is absolutely nothing but it'll be easier to add circular motion now

Avatar
reblogged
Avatar
fox-flux

physics problems 2: still player on a slope

(crossposting from cohost)

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

[lexy sprite omitted because tumblr completely fucks it up]

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

lmao

alright great news

i thought about this some more and... you know, i'm already making a friction vector based on the ground i was standing on last frame. and friction is capped, so just throwing a real big tangent friction vector at it should work no matter how strong gravity is. and lexy's walk acceleration is pretty high so the friction vector is pretty big. so...

so this should... already work... right?

anyway it turns out that when i applied friction, i was including a factor of ½ in an attempt to also integrate friction correctly. i remember i suspected it was a fool's errand when i first tried this, since friction is not differentiable, but it seemed to work so i left it in. alas! it only worked because of some other kludge that i recently removed.

if i also remove that ½, lexy stands perfectly still on a hillside, and her attempted velocity every frame is perpendicular to it, exactly as intended

amazing. turns out i already did everything right. very smart of me

in my defense, i did the diagnosis part of this problem a couple days ago when i was too exhausted to do anything but stare into space and think about physics, and since then i only thought about fixing it since that seemed like the harder part. never questioned the vague notion in my head that gravity needed to be exactly cancelled by friction, and even described it in detail! whoops

Avatar
reblogged
Avatar
fox-flux

physics problems 2: still player on a slope

(crossposting from cohost)

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

[lexy sprite omitted because tumblr completely fucks it up]

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

Avatar
ashovel
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

I'm getting the impression that you're trying to add velocities and forces from this post. If you are, please don't do that.

Anyways, in the real world, you would have the force of gravity (g) pointing down, the normal force (N) pointing perpendicularly outward from the surface, and the force of friction (f) (where applicable) pointing parallel to the surface. Note that velocity is not considered here.

On a slope (going up to the right) at a standstill, the normal force points up and to the left and is a bit smaller than on flat ground because cosine, and N + g points down parallel to the surface of the slope, because cosine. So friction would point up along the slope. If the maximum friction force for that surface and angle is greater than |N + g|, then friction will cancel out the other forces and keep Lexy in place.

As another note, it makes sense to treat friction while moving differently from friction while standing still, as one is opposite the direction of movement and one is opposite the direction of the other forces.

As for the solution, I'm not a game dev, but something like the 'remembering the ground' solution might work. You mentioned on cohost that you don't calculate the normal force, but you might want that, because the normal force is both how hard the ground pushes against Lexy and how hard Lexy pushes against the ground.

i promise i'm not trying to add velocities to forces :) some dt is involved. i'm actually doing

Δv = a Δt v₁ = v₀ + Δv Δs = (v₀ + ½ Δv) Δt

which is exactly correct at any framerate for constant acceleration. plus there's some messy stuff for making friction counteract but not overshoot. but the details seemed slightly beyond the scope of the post

i do know generally how the real physics work; this is more a question of when exactly to do it during the individual steps of the simulation that doesn't make a big mess of things. real physicists have the luxury of saying "friction is simply parallel to the surface", but i also have the problem of finding the surface, and i don't know what it is yet at the point where i would want to integrate friction! hence the kind of conceptual problem i'm having — it seems to make sense to do all the physics math and then simulate the motion and collision, but some of the physics math depends on the motion and collision. which is why i'm considering "remembering the ground", yeah.

the normal force doesn't seem useful on its own; i do remember the normal vector with the ground and use that, but actually my physics engine contains almost no mention of forces at all. even friction is secretly just a form of acceleration. don't tell any physicists though

Avatar
reblogged
Avatar
fox-flux

physics problems 2: still player on a slope

(crossposting from cohost)

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

[lexy sprite omitted because tumblr completely fucks it up]

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

Avatar
canmom

“so where did you go wrong?”

physicist turned gamedev here o/

so, assuming you want a physics based character controller, you forgot the normal force from the slope. here’s a free body diagram for a character on a slope:

You can see that the sum of normal, weight, and friction can come to zero without issue.

The most straightforward integration method is what it sounds like you’re already doing - compute all the forces to get acceleration, integrate the velocity with that acceleration, and integrate the position based on that velocity. I think that’s something like implicit Euler integration.

In games, it tends to be a little complicated because collision detection and computation of the normal force usually depends on a certain amount of intersection, and if the timestep is too high, transient contact forces can be integrated incorrectly and lead to all sorts of weird jiggling.

You might be interested in extended position-based dynamics, which is an integration method widely used for things like cloth, that gets around a lot of that kind of problem:

Anyway, for a character controller, physics-based movement where the player directly applies a force to the character can often feel awkwardly floaty. The way human bodies walk around is not really like rigid body motion. Since you’re (probably) not really simulating all the limbs in full detail, you probably want to write custom logic for grounded movement. That said, if you take that road, you get all sorts of edge cases such as walking off cliffs and up/down slopes and handling steps and things like that.

Anyway good luck! Writing character controllers is really interesting.

calculating the normal force has exactly the same problem — i only have a normal vector in the first place after i collide with something, and at the point where i'm integrating velocity, i haven't yet collided with anything this frame! if i had an obvious place to compute the normal force, i could compute friction in the same place, and then i wouldn't need the normal force anyway.

i always forget to mention this, but i'm doing continuous collision detection, with full sweeping rather than teleporting. there's no overlap rejection, because there aren't supposed to be overlaps in the first place, so jiggling won't be an issue. not for that reason, at least.

and don't worry, the player isn't modelled as something an invisible hand is pushing around. she isn't subject to friction at all while moving, only while trying to stand still. even then, mostly because friction conveniently already has the behavior of never overshooting and moving her uphill instead.

i'm some years into this and i've shipped little games using this code and it's mostly pretty solid. i think i accidentally caused the downhill slide to recur when i split up all the physics code into discrete chunks a while back, and i never noticed because it was masked by something else. so this is just a needly little problem where the most correct solution feels awkward and that is annoying.

thank you though!

Avatar

physics problems 3: potpourri

(crossposting from cohost)

here's just some stuff, though it ended up more related than i thought it would

collision responses

to zoom in on how motion is resolved a bit, the steps are basically like this

① compute the actor's instantaneous velocity. multiply by dt to get this frame's movement

② attempt to move. if you hit something, inform it of the collision (call its on-collide), project the remaining movement along the surface of the thing you hit, and repeat. (essentially, if you hit something at a slant, you slide along it.)

③ after movement is done, repeat the projection, but on the actor's velocity. (if you hit something at a slant, you don't keep trying to move in the same direction; you're now moving along the slant. also, if you hit something dead-on, you simply stop moving.) this technically cheats a bit because it only slides against the last thing you hit, but that's very unlikely to matter unless you're trying to go through a sonic loop-de-loop at 2 fps.

the questionable part is in step 2, where the collidee is informed of the collision. because this is in the middle of motion. there are several consequences of this. this post is going to consist of a lot of lists

• because of the sliding behavior, you might collide with something twice in a single motion. this means that a lot of objects that do stuff on passive collision, like candy, need to be a teeny bit careful that they're not double-collected. (candy sets itself "pending destruction" on collide which isn't actually removed from the map until the end of the frame, and arguably collision should be ignoring pending-destruction actors anyway, but candy is just a simple example of a more general problem)

• a handful of objects want to respond to collision by doing something to the collider's velocity, e.g. the bounce shroom, which launches things into the air. this is troublesome because the collider's velocity is essentially undefined during step 2, because it's still waiting to be slid in step 3. if you collide with something that alters your velocity, that change should probably not... also be subject to the collision? like at that point the collision has already happened, right. currently i slap a zero-second delay on a closure to do the velocity change but that seems uhhh questionable

• i think loading zones try to initiate a map transition in response to collision. it's not like this happens immediately since there's an animation but still, this is clearly insane to be doing in the middle of physics behavior, and i think also potentially subject to double trigger if you hit it just right. (but arguably scene changes shouldn't happen mid-frame anyway so that's a whole other thing—)

• starcow lexy can break stone blocks, but in the process she gets knocked backwards and the collision stops. i would like her to be able to break at least some things without being stopped. that's rather difficult to do without conditional collision. currently it is possible to change whether a collision happens from within a collision handler, but i virtually never do it, and i'd been thinking about removing the ability altogether. i guess this isn't a real problem but it's something that will factor in below

these are varying levels of Actual Problems so i have mixed feelings about how hard i want to try solving them. but i have the growing sense of something being just a bit messier that it needs to be, causing just a few too many minor headaches, and forcing me to keep just a few too many things in mind. it's just hard to pinpoint exactly what it is.

it would be nice if velocity changes were just plain safe to do from a collision handler. this suggests that collision handlers should happen after collision is completely done, when physics are back in a consistent state. that seems reasonable. like i also have a "collided-with" handler that i used to use a lot, but it's been almost entirely replaced by "after-collisions" which happens... after collisions. so i could just do that, but the other way around.

and here is where i feel like i'm in the middle of several decisions i was only ever 90% confident about.

see, "after-collisions" only receives the list of collisions from the last iteration of step 2. so it's possible, though relatively unlikely, to hit something that "after-collisions" doesn't know about. but it mostly cares about having either hit anything or ended up touching a particular thing, so glancing blows aren't very interesting.

i guess you could say the api mostly cares about where you end up? even though it might also be told about a collision with something that you aren't still touching by the time you stop moving. alas.

anyway the obvious thing is to similarly defer on-collide until later. that means remembering the actors i collided with and only telling them about it later. i don't love the added gc churn in this already fairly hot and heavy code, but whatever i guess. the bigger question is uhh which collision do i report? first? last? all, thus reinventing an annoyance i already have?

i'll also still need the immediate version for conditional collision, so that means i'll have four collision callbacks: immediate collider, immediate collidee, deferred collider, and deferred collidee. i guess the symmetry is satisfying, at least.

one more annoying wrinkle: after-collisions is actually called before velocity is updated, so it doesn't even solve this problem! the reason is that velocity is updated as part of the generic movement update, but an individual motion might happen outside of that (e.g. being pushed), and after-collisions is still called for those. but velocity can't be updated until the motion has finished, and after-collisions is called as the last step of the motions. wow! doing everything in the right order is a pain in the ass

an object i do not wish to disclose

there's a thing i have in mind that's activated when something lands on it from above. as in, specifically lands on it, not just walks onto it from the side.

i thought this was a pretty simple concept. alas

i found out by complete accident that it will trigger by accident if you create the following setup: position a crate so it's hanging over a ledge, near a mushroom. now just bounce the thing on the mushroom so that it hits the underside of the crate.

the problem is that the thing hits the crate, and stops. now it's the crate's turn to move. it's subject to gravity, so it tries to move downwards. in trying to move downwards, it hits two objects simultaneously — the thing, and the ground it's sitting on. the crate does not yet know that it's blocked, because on-collide is part of determining that, so it simply calls on-collide on both objects. the thing sees it's been hit, sees it was from above, and sees the velocity was downwards — remember, velocity hasn't been slid yet, and can't have been, because at this point we don't even know whether the crate is allowed to continue moving — so it triggers.

it's easy enough to look at this and go "well slap a threshold on that bad boy", and i maybe should anyway, but in this case it feels like papering over a problem that shouldn't exist in the first place. the thing just plain isn't being hit by an object moving downwards, and it's a clear api bug that it can even think that that's happening.

so changing on-collide as described above should fix it. more incentive i guess

only do it once

a recurring wrinkle is how to make a zone apply effects to any actor that touches it, without doubling the effect when crossing between two zones. anywhere this actually works is a goddamn miracle, though i don't know offhand if it comes up in the demo levels. but like i tried to make conveyor belt tiles once and that... that was... something.

or for example, the springs from the jam demo still exist, but they don't really handle being placed next to each other very well. springs depress based on what they're "carrying", and an actor can only be carried by one thing at a time. so if you straddle two springs, you're only carried by one of them arbitrarily, and it starts to depress, but you're still held up by the other spring, so you're no longer touching the first one, so it's no longer carrying you, so the second one is now carrying you, so the second one depresses, but in the meantime the first one returns to its original position, ad infinitum.

this one isn't a concrete problem it's just a category of problem that crops up every so often

expressing the situation correctly

ability spoiler i guess!! look away now if you don't want that

if you enter the farm level as starcow lexy and simply charge directly right or left, you will stick under the ceiling and... stay there. you won't move.

the reason is that the charge determines when to stop by looking for a specifically horizontal collision. otherwise, charging up a hill doesn't work correctly. but the geometry here prevents a horizontal collision — the ceiling is sloped and touches the corner of your hitbox, so the collision is diagonally downwards.

but if you hit the ceiling and the floor weren't there, you'd slide down along it, and that would be fine too.

i think the issue here is that what i really wanted was to say "if you end up in a position where you can no longer move, end the charge". that's kinda hard to express directly with the information you get back from collision. i thought a head-on collision would cover it, but i was wrong. luckily, there's a very easy way to make this work: simply wait one frame, and it becomes "if you don't move, end the charge".

anyway

i was aware of a lot of this stuff when i put the demo out, but i opted not to try to fix it then. i hope you can see why: a bunch of this touches some deep plumbing, and fixing it is the sort of thing that might unexpectedly break some obscure behavior somewhere else. but i'll keep sticking fingers in the dam holes until i'm pretty sure i've got them all

Avatar

physics problems 2: still player on a slope

(crossposting from cohost)

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

[lexy sprite omitted because tumblr completely fucks it up]

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

Avatar

incredibly stupid physics problems

(crossposting from cohost)

i posted this in the secret fox flux $4 discord channel last night and it reveals A Problem

i mean, a problem besides the barrel's layering being kind of dubious

the pistons stop trying to extend as soon as they're blocked. and here, you may notice, the one extending left pushes the slime a little bit before giving up. this turns out to be caused by a combination of:

  • the piston is "moving", but it's not using the normal motion machinery (obviously), so it doesn't actually have any velocity.
  • when object A tries to push object B, if it determines that object B is moving away from it faster than A is moving, it doesn't bother. i don't remember what specific problem this was meant to solve, but it does make intuitive sense — if i'm pushing a boulder down a hill then it's just going to roll away from me and i am going to accomplish nothing. if i let the push happen, i'd make B move even faster (however briefly) which is silly.

as long as the slime is walking towards the piston, everything is fine. but the slime immediately notices it's blocked on one side, turns around, and starts walking the other way. now the slime has a leftwards velocity, but the piston has zero velocity, so the piston thinks the slime is speeding away from it. but that isn't happening, and the slime is still there, so the piston thinks it's now blocked and stops extending.

love it. chef kiss. pushing objects is the hardest thing in the world. anyway i guess i can think of two potential solutions here

① delete the special case for a pushee moving away. checking velocity in the middle of the (hairy) push code is already kind of weird, and it's only done for this specific case. it also shouldn't matter; even if we push something slightly this frame, if it really is moving away from us, then it should be beyond our reach next frame anyway.

i suspect this is a leftover from an older and vastly more complicated iteration of the push code that tried to conserve momentum and whatnot — in which case, doing this even for a single frame would have made A and B look like a single unit, combined their momentum, and given both the same velocity, which means B slows down. so that's bad. but that was an endless nightmare of increasingly obscure interactions so i eventually gave up and scrapped it all. the current implementation won't let you simulate newton's cradle, i guess, but that's not the kind of game this is so whatever?

on the other hand, if i'm wrong and this is still important in some exceptionally obscure case, it would be bad to remove it! exciting.

(if you're curious: what i do now is give lexy — or other push-capable actors — a maximum push mass, and then when she tries to push something, i scale her movement down proportional to how much she's pushing. for example, rubber lexy's push capacity is 8, and a wooden crate weighs 4, so she moves at half speed when pushing one crate and at zero speed when pushing two. i think if a sliding crate hits another one now, they'll both just move at the first one's original speed, but friction skids everything to a halt fast enough that it mostly doesn't come up.)

② give the piston a fake velocity to fool the check. i might want to do this anyway for stuff like climbing, where the player is moving but is considered to have zero velocity because it's being done manually. but lying like this feels like it'll come back to bite me somewhere down the line.

anyway this is just the sort of thing i bumble into occasionally. hope it's interesting. i should probably go try to fix it now