lexyeevee
@lexyeevee

there's a fun thing wrong with my brain where i just cannot bear to leave uncovered cases or undefined behavior or other kinds of potential future potholes for myself. i can spot a lot of these a mile away, too: the unspoken assumptions made in what i'm saying to myself, or what someone is saying to me. (for example, every time i hear someone say they simply pause their game by setting the timescale to zero, seventeen klaxons go off around my desk.)

sometimes i describe a problem, where some convoluted (but entirely plausible) level geometry will cause A Problem, and inevitably an onlooker will suggest "i would simply not put that geometry in a level". but to me that is like saying "i would simply not have too much traffic on the bridge"

this is very abstract. i should make it concrete.

i am adding moving platforms to fox flux.

...well, okay, the jam game already had moving platforms. i'm overhauling the implementation, because i'm about to introduce them in the full game.

moving platforms have two parts: the platform itself, and the track that it moves along. the track is a polyline object drawn in Tiled. it does exist as a separate actor in-game, but mainly so the platform can refer to it; it doesn't actually do anything. (although i suppose it would be cool if it could draw itself sometimes?)

the implementation is fairly simple (except for some annoying physics calculus at the end of the track, where the platform slows smoothly to a stop). the platform remembers which segment of the track it's on, and which direction it's moving it, and it moves towards the next point on the track. when it reaches a point, it either continues along to the next one, or turns around.

now, see, in the jam game, i couldn't actually control where a platform started. it would always start at the first point on its track. i'd even place it there in the map editor as a reminder to myself, but it sort of gave the impression i could move it somewhere else.

and this is kind of inflexible. like if i want to start from the other end, that's too bad, because i don't think you can reverse a polyline. and in the case of a loop (i.e. a polygon), i certainly don't think you can change which point is "first". like why would you ever want to do that

so, now there's a new initialization step for moving platforms: they have to work out where on the track they are, so they know what the heck they're doing when they start moving.

my first crack at this just checked for the nearest point. because why would i start a platform out not at a point? if the platform wasn't on a point then, i dunno, something would happen. the platform would find a point and continue along to the next one on the track section it thought it was on, so, maybe its first movement would be kind of diagonal. i guess that's weird. but i could put a platform halfway between two points and it would do the right thing, probably.

but then i went to write a little docstring about how this worked, and i found bad track examples popping into my head, and i found myself apologizing for how they wouldn't work correctly. for example, imagine a flattened triangle, and the platform is placed so it starts in the middle like this:

(platforms are anchored at their top center.)

well, ah, whoops. it's pretty obvious what i meant, but the nearest point is the one at the top, so now... the platform will start out moving towards one of the side points, and then turn right around and go back along the bottom edge before finally starting to trace out a triangle? what the fuck.

and, dearest reader, this is the whole thing. i just cannot fucking bear when i can immediately think of an entirely reasonable thing i might do that will blow up in my face. it is like i am burying land mines in my own engine for no reason at all. when i inevitably try this in a few months, it's not like i'm going to see the goofy behavior and think "ho ho, well, i guess i can't do that, better do something else!". no i'm going to swear at my past self (who is currently me) for making me have to go back into this code and fix it again now that i've forgotten what dumbass thing it's doing.

so why not just fix it now while i'm thinking about it anyway?

thus begins round two: figure out which track section i'm on. this seems straightforward enough, except for the annoying part of "is this point on a segment". it's easy enough to work out if a point is on a line, but segments have endpoints, and those make it annoying.

and now the little gears in my little fox brain start turning, and a fascinating question emerges. because i know that what i'm doing here, fundamentally, is converting a point to a track section. like this is a function, in the mathematical sense, that takes one and produces the other.

but there are no real constraints on that point, right? it could, in principle, be anything. and so i wonder:

what happens if the platform isn't actually on the track to start with?

and here's where the easy answers are "simply don't do that" or "i dunno, something bad probably". but that feels like taking a hacksaw to the level design before i've even designed anything — suddenly this parameter has been reduced from "anywhere" to "only along this line", a reduction of infinity. and if "something bad" happens, then, what does that mean exactly? if i place a platform a pixel away from its track by accident, the game fucking explodes? what kind of failure case is that?

what's especially fascinating is that about half the time, sensible behavior isn't even that difficult. consider how you might place a platform on a track in the first place:

  • iterate over track sections
    • if the platform is on this track section, attach here and return; we're done
  • we fell through, so blow up i guess

ah, but we skipped a step there. remember that whole thing about segment endpoints? yeah that's annoying. i think you'll end up doing something with vector projection to take care of it, at which point you are essentially at:

  • iterate over track sections
    • compute the platform's distance to the track section
    • if that distance is zero, attach here and return; we're done
  • we fell through, so blow up i guess

but now we're not very far away from:

  • iterate over track sections
    • compute the platform's distance to the track section
    • if that distance is zero, attach here and return; we're done
    • otherwise, remember the minimum distance seen so far
  • we fell through, so attach to the nearest section...
  • ...and use the closest point on the section as a temporary goalpoint

i mean, that's using stuff i was gonna compute anyway. i'm just not throwing it away any more.

and now something interesting has happened. what is a "temporary goalpoint"? well here it must be a point the platform moves towards first, before bothering with the track. in this case the temporary point is on the track — that's the whole idea, after all — but once again, nothing says it has to be. i've just invented a tiny new feature. almost for free! and i know it works because i'm already using it for something.

what else could i possibly use this for? i have no idea. it's just tucked in the back of my head as a little bookmark. but i'm already thinking about a platform that takes a brief detour off of its track for some reason, or using multiple tracks to fake track switches and moving between them, or a platform that has no track at all and is moved by some other means.

and part of this effort has been extracting the track code out of platforms entirely, so it can be placed on other objects. hmm. what else might want to wander off its track temporarily? creatures perhaps?

maybe i'll never use this. but i like feeling possibilities open up. i guess when i'm doing the dev work and the level design, it can feel like the code constrains what i even allow myself to consider for the levels, so the last thing i want to do is add to those constraints. on the other hand, doing something sensible in an edge case tends to let me imagine more broadly, which i think is worth it on its own


something else i did with platforms is— okay so i said they slow smoothly to a stop at the end, right? and, just so you know, they also accelerate smoothly up to top speed when they get going. (the twiddle for this "ramping" is a distance, not an acceleration, just because it was easier for me to reason about "how far along the track will this thing be before it's at full speed" if i gave a distance. the math isn't too much worse though.)

a lot of platforms follow a single-segment track: a straight line, horizontal or vertical. so it's very possible that the accel and decel parts happen on the same track section, and i have to make sure that works too. okay, not really a problem.

but then something immediately nags at me. i'm curious if you've spotted it. it's the kind of thing that sets off klaxons for me, but that most other people seem to overlook. i don't know if it's because i think about things mathematically or because i had an ear in infosec circles for a while or what. but i spell this out and right away i think...

what happens if the track is too short?

as in, what happens if the track is shorter than twice the ramp distance? then there's not enough room to get up to full speed and slow back down again!

so here's what i did, are you ready

i handled that too

i said, ok, we can't get up to full speed, but we do have enough room to get up to some speed and then slow down again, at the usual rate. so let's work out what that speed is, and only accelerate until we hit it, and then stop. at that point the deceleration will kick in because we're guaranteed to be within the deceleration zone. problem solved!

is this crazy? i don't know. at the moment, i have no reason to make teeny tiny track sections like that. but it would bug the hell out of me to know that i wrote something that already doesn't work sometimes. and maybe i would make a short track for something else that isn't a platform, like a grumblebee.


incidentally, part of why it's fine to leave the ramp acceleration implicit: when decelerating, i don't use it anyway. i calculate how fast i'd have to decelerate, given my current speed and distance from the end of the track, to come to a halt exactly on time. and then i decelerate that fast.


doty
@doty

I always think it’s so nice when I come back to something like this and discover that it just kinda… works? Does the thing I need it to do? All because I was diligent when I built it the first time.

I think to myself “good job me!” A little present to myself.


You must log in to comment.

in reply to @lexyeevee's post:

I can't tell if there's something I'm missing that makes it obvious, but from what you're saying I assume that you don't have much control over the behaviour of your level editor. (Because if you did, you could ensure that, e.g., platforms always start on a defined point—by having the level editor snap platforms to a point when you place them.)

the level editor is Tiled, a general-purpose map editor, yes

the funny thing is that "snap platforms to a point" is essentially what i did anyway, just, in the sim rather than a design constraint

Except for the "find the way to the path" it sounds like you're building a motion planner like would be in a CNC controller, especially if you're planning to limit the speed that can instantly change direction in path corners. Some of them even use higher-order acceleration/decel curves instead of constant-acceleration. (TinyG vs Smoothie)

I think those cases where you can either solve the limitations or just "simply not do that" are like the difference between solving a problem from an "engineering perspective", or solving it from an "artistic perspective".

Saying "just don't do that" is accepting the limitation and then designing within that constraint, which isn't a bad thing, because there's always some constraint, and the "artistic problem" is thinking about interesting things within those constraints, like the "limitations breed creativity" kind of thinking. I'm also thinking of very limited design environments like mega man maker, where the point is you're designing within those specific limits.

Contrariwise trying to lift the limitation is like trying to design a tool for maximum flexibility so that it's the maximally useful tool, but that isn't necessarily useful to you if you're just trying to make a very specific game within a very specific design space, which is why it seems more like "engineering" to me.

If you have a game in mind, probably you have a sense of the kinds of things you want to do with it, so it's not necessary to have your tools be able to do things which you don't need for your design? Granted that since you're in the process of making the game, rather than having the game's design spill out of your mind fully formed, you're probably also experimenting with things that you don't know if you're interested in or not, for which more flexible tools are more useful, but also you can't know if you actually will need that flexibility, so it seems kind of like "premature optimization" in that sense? Like it seems more optimal to make the tool for the specific ideas you're currently interested in, and only make them more flexible if you need it, rather than spending a lot of time making features you may never use.

limitations breed creativity, but i don't think that means you should (necessarily) go out of your way to give yourself more of them. i already have enough artistic limitations — the screen is only so big, the tiles are fairly chunky, the player character has only a few ways to interact with the environment, etc. and there are already technical limitations as well, like, i'm working with retro physics so things just can't rotate which means i'd probably better not place a wooden crate anywhere you can get it onto a sloped surface.

on the contrary i think "just don't do that" is more the engineering approach, because there is always some limit beyond which most things just don't work — if i put a platform outside the level then it would still function, but the behavior would be kind of absurd. making more things possible feels more artistic to me because now i can get weird with the levels (and even have ideas for ways i could get weird) without getting bogged down in boring gritty stuff like "ah yes i must be sure to place this platform exactly on a point".

and that's sort of a problem i have creatively in general, is i imagine overly tight constraints that don't actually exist, and my work ends up confined within a box of what i assume is possible or reasonable or feasible. so this is a nice counter to that, and almost a hack of my own brain, to engage the "anything could be possible if i simply defined it to be" mathematician part and hope it bleeds into the artistic part

also this kind of geometry stuff can be fiddly and tedious and kind of a slog even if i take shortcuts, and i really don't enjoy when i have to revisit it and end up throwing away half of what i had because i did take shortcuts, so it really does end up easier overall to just minimize the obvious failure cases upfront.

this particular game is also very much about goofing around with its own mechanics, which might factor in. like if it were more about combat or story or something then i would care a lot less, but when i say i'm adding platforms, i mean i'm going to make platforms a major fucking component

yeah that makes sense! I just thought you were describing edge cases to account for things that were more like user error (which wouldn't matter if the only user is you), but it makes sense if your idea looks like it benefits from more possibilities and covering every possible case.

i do also bear in mind the remote possibility that other people might want to make levels someday, somehow... but tbh i find that treating my future self as a bumbling third party makes things easier anyway :)

I can totally think of a use case for the "find the nearest point on a segment and move towards it." Speedrun challenges where you have to make your way to a platform before it goes out of reach.

The whole problem with this really complex thing is it's not very deterministic. You can't tell from a casual glance what it's going to do, because YOU now have to do lots of nearest-point determinations.

You need it to be as "unsurprising" as possible. So here's my recommendation:

  1. Always start from the first point.
  2. If the first point is more than a tile away, SCREAM MURDER into the debug log.
  3. The end.
  4. Next problem. Let's ship this bastard.

what? it's 100% deterministic. "find the nearest track section" is something you can eyeball and the result will always be the same. and in the common case of the platform being placed already on the track, it will now start from where it's visibly placed in the level editor.

"always start from the first point" was already implemented for some time, and it consistently fucked me up. because it meant the level editor lied to me! and that is pretty damn surprising. and it was in ways that were hard to notice and mentally correct for. it was guaranteed to generate a continuous stream of little surprise problems that either had stupid tedious solutions or no solution at all. having stderr go "No, idiot, you can't do that because it's too surprising" would not be very helpful

now i can design whatever i want and the game will Just Do It! and even if (somehow) i still get problems from too-distant platforms, then i can easily alter it to snap to the track instead; 98% of that code is already there, because i wrote it today

"find the nearest track section" is something you can eyeball

I think it's sometimes not necessarily eyeball-able if the track is shaped like a bunch of Z's and the platform is right halfway between two of them. Maybe you could get into fun behaviour with the platform snapping to either a line just above itself or another just below it depending on exact rounding errors?

(although i suppose it would be cool if it could draw itself sometimes?)

oh fuck you know what i think fez has a couple levels that do this. like during lighting flashes or something? it's a really fun mechanic. loved fez what a delightful little game. anyway

:eggbug:💭 moving platform system that doesn't know about waypoints at all, you actually map them out as just a painted line on an invisible layer that the platform follows with like a classic line-following algorithm like a little robot. if it can't find the line it just woobles around until it finds one

The platform remembers which segment of the track it's on, and which direction it's moving it at all times. It knows this because it knows where it isn’t. By subtracting where it is from where it isn’t (whichever is greater), it obtains a difference or deviation . The subssystem moves towards the next point on the track. when it reaches a point, it either continues along to the next one, or turns around, arriving at a position where it wasn’t, but now is. Consequently, the position where it is, is now the position that it wasn't, and it follows that the position that it was, is now the position that it isn't.

hm, still some big chunks missing — the platforms would need to know when they touch the rails, which implies having collision for both the platform anchor and all the rail pieces. and it would be a bit of a pain in the ass to have a second hitbox for the same actor with different behavior

Philosophically I much prefer keeping simple constraints that solve the actual design problems I have, because I have no way of knowing what my all my future design problems are going to be. The more logic I add to any gameplay system, the more dependencies I have introduced. If I decide later that actually I want something that breaks that logic, I risk breaking many more gameplay dependencies. Undoing work is universally more complicated than doing new work.