Physics System GET?
#7
Ran into several random roadblocks over the last week that were filtering me for a while, but we're getting somewhere.
Apologies in advance, this post is going to be entirely nerdly, slightly whiny, otacon shit.
¥animation system, GET!
As sketched out in >>4 the animation system is based on a animation subclass of timer which holds a table of animation states and modifies the active sprite slot based on those states, then an angled subclass which just hotswaps the entire animation table based on the character's angle relative to the camera. The system is fairly spartan, with each animation frame being a separately loaded image file, but whatever, it was super easy to implement, and should be flexible for expanding later.
¥physics system, GET! *Kind of.
Basic entities just have a x/z coordinate and are drawn at the height of their tile, to get a y-coordinate that means anything I had to implement a physics/collision system. Rather than simulate constant forces, I chose to abstract "impulses" as decaying bursts of velocity on tangible entities.
In theory this could allow for more "physical" feeling movement since we could simulate individual footfalls rather than modeling characters as sliding boxes. In practice simulating long strides feel godawful, in part because I chose to have impulses decay according to a geometric series so either the decay is too fast and it feels like you're glitching into the air, or the decay is too slow and it's like sliding on ice. I could change the decay function but I think the problem's more fundamental: you can't have precise player movement if all your strides are a fixed, wide, length. You'd have to do a ton of analysis on the player's keystrokes to figure out exactly how long the stride should be. Which sounds fucking hard.
If I set the stride length to like 2 ticks or something short like that all of this is basically negligible and it just gives entities a bit of ramp-up when moving which feels fine, so I'll just leave it as is for now. I do like the idea of simulating footfalls though...
Anyways, none of this shit is what took me a week implement, that's the fucking collision detection's fault. Draw a line from an entity's current position to it's next position, see if it hits a wall/floor, and cut it's movement off there if so. "Easy".
I got upset enough that I don't even fucking remember what filtered me for so long. Looking sat my notes, I wasted a a some time because I somehow forgot the non-parametric equation of a line breaks down at/near vertical and floats aren't precise enough to test for degenerate values, I was calculating X/Z and Y collisions at the same time so the floor collision always blocked any sideways movement while grounded, wall clipping caused entities to get stuck, there were some some other degenerate floating point cases. The code I wrote looks fine in retrospect, clever, even, but this is after I had copied and modified the code function by function into another file.
I could show off that the "math" core of the grid hitscan code is only 30 lines, and fairly brief lines at that, if maybe superficially inscrutable. I found a lot of refernces to "DDA" and Breshnam's algorithm when I was looking around for this sort of thing but both are centered around the imperative for loop, which isn't at all idiomatic in Factor. My implementation just straight up solves the parametric form of a line q=tdq+q0 (where q represents {x,y,z} and t an arbitrary value along that line) for t for all integer intersections of x and z between two end points, then computes the xyz coordinates of each, and we get a bunch of other helpful words for dealing with 3D lines we can reuse elswhere.
*It's technically 34 counting my combinator that's just a modification of Factor's base spread with call replaced with call-effect to work on runtime quotations.
The rest is just interpreting the results into tilemap collisions, checking some edge cases, and actually applying the results to the world. There's still room for improvement, I'd rather entities slide along walls than simply brake into them as they do now, and there's probably some way to get the sprite on top of the front-facing side of a quad so you can't see it visibly clipping inside of a tile.
¥Shooting system...ah, eto, bleh.
This hitscan code should be reusable for a shooting system, so I wanted to try implementing that quickly just to see
it in action, took me a day to figure out how to translate window clicks into target points, then another to figure out why the crosshair location was all over the place until I remembered my fucking camera yaw is below my camera translations in the matrix stack, so the relatively straightforwards math I had in place doesn't work unless I rearrange my camera code, at which point it becomes obvious I need to get that straightened out first. Still, if I just swap the translation and yaw in the rendering code (and break movement in the process) it does, in fact, work exactly as expected.
Fuck, I really didn't want to do camera code.
Apologies in advance, this post is going to be entirely nerdly, slightly whiny, otacon shit.
¥animation system, GET!
As sketched out in >>4 the animation system is based on a animation subclass of timer which holds a table of animation states and modifies the active sprite slot based on those states, then an angled subclass which just hotswaps the entire animation table based on the character's angle relative to the camera. The system is fairly spartan, with each animation frame being a separately loaded image file, but whatever, it was super easy to implement, and should be flexible for expanding later.
¥physics system, GET! *Kind of.
Basic entities just have a x/z coordinate and are drawn at the height of their tile, to get a y-coordinate that means anything I had to implement a physics/collision system. Rather than simulate constant forces, I chose to abstract "impulses" as decaying bursts of velocity on tangible entities.
In theory this could allow for more "physical" feeling movement since we could simulate individual footfalls rather than modeling characters as sliding boxes. In practice simulating long strides feel godawful, in part because I chose to have impulses decay according to a geometric series so either the decay is too fast and it feels like you're glitching into the air, or the decay is too slow and it's like sliding on ice. I could change the decay function but I think the problem's more fundamental: you can't have precise player movement if all your strides are a fixed, wide, length. You'd have to do a ton of analysis on the player's keystrokes to figure out exactly how long the stride should be. Which sounds fucking hard.
If I set the stride length to like 2 ticks or something short like that all of this is basically negligible and it just gives entities a bit of ramp-up when moving which feels fine, so I'll just leave it as is for now. I do like the idea of simulating footfalls though...
Anyways, none of this shit is what took me a week implement, that's the fucking collision detection's fault. Draw a line from an entity's current position to it's next position, see if it hits a wall/floor, and cut it's movement off there if so. "Easy".
I got upset enough that I don't even fucking remember what filtered me for so long. Looking sat my notes, I wasted a a some time because I somehow forgot the non-parametric equation of a line breaks down at/near vertical and floats aren't precise enough to test for degenerate values, I was calculating X/Z and Y collisions at the same time so the floor collision always blocked any sideways movement while grounded, wall clipping caused entities to get stuck, there were some some other degenerate floating point cases. The code I wrote looks fine in retrospect, clever, even, but this is after I had copied and modified the code function by function into another file.
I could show off that the "math" core of the grid hitscan code is only 30 lines, and fairly brief lines at that, if maybe superficially inscrutable. I found a lot of refernces to "DDA" and Breshnam's algorithm when I was looking around for this sort of thing but both are centered around the imperative for loop, which isn't at all idiomatic in Factor. My implementation just straight up solves the parametric form of a line q=tdq+q0 (where q represents {x,y,z} and t an arbitrary value along that line) for t for all integer intersections of x and z between two end points, then computes the xyz coordinates of each, and we get a bunch of other helpful words for dealing with 3D lines we can reuse elswhere.
*It's technically 34 counting my combinator that's just a modification of Factor's base spread with call replaced with call-effect to work on runtime quotations.
The rest is just interpreting the results into tilemap collisions, checking some edge cases, and actually applying the results to the world. There's still room for improvement, I'd rather entities slide along walls than simply brake into them as they do now, and there's probably some way to get the sprite on top of the front-facing side of a quad so you can't see it visibly clipping inside of a tile.
¥Shooting system...ah, eto, bleh.
This hitscan code should be reusable for a shooting system, so I wanted to try implementing that quickly just to see
it in action, took me a day to figure out how to translate window clicks into target points, then another to figure out why the crosshair location was all over the place until I remembered my fucking camera yaw is below my camera translations in the matrix stack, so the relatively straightforwards math I had in place doesn't work unless I rearrange my camera code, at which point it becomes obvious I need to get that straightened out first. Still, if I just swap the translation and yaw in the rendering code (and break movement in the process) it does, in fact, work exactly as expected.
Fuck, I really didn't want to do camera code.