fox-flux:

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