Implementing Snakey

Snakey is a complicated beast, just standing there... menacingly. This one’s gonna be a doozie, so join me and we’ll embark on the most perilous implementation yet.


Okay, so, Snakey stands still and doesn’t harm Lolo.


And we’re done! Stay tuned for the next post.

















Oof, that’s not even an original joke. Also it doesn’t even scratch the surface. Let’s start with Snakey’s habit of looking towards the player.


Eventually, Snakey will get dizzy and then you can strike it with your sword! Or was that Legend of Zelda? Oop.


There are four angles Snakey can face in most games (except Windows 95). Here is a diagram of where Lolo must stand to cause Snakey to face any given direction:


Lolo standing in the red area means Snakey will look “very left”. Very left, really? Extreme left? Left-left? Words are hard.


Snakey checks two things: whether Lolo is to the left or to the right, and whether Lolo is further away horizontally or vertically. That first statement is pretty easy to understand, but that second one has some surprise maths. Actually, both require a bit of math, so let’s math up.


If Snakey takes Lolo’s X coordinate and subtracts its own X coordinate, we get a number that represents the horizontal difference between their positions. If Lolo’s X coordinate is larger, then that number is positive, meaning Lolo is somewhere to Snakey’s right. So Snakey should use one of its right-facing graphics. If the number is negative, then Lolo is on the left and the sprite should look left. For the corner case of 0, Snakey defaults to facing left.


There’s integer rounding happening here, I can smell it



Now that we know which direction Snakey is facing (for the sake of a simple example going forward, let’s say Snakey is facing right at the moment), we have to determine if Snakey should use the “slightly right-facing” sprite or the “very right-facing” sprite. Snakey faces very right when Lolo occupies the green zone in the aforementioned zones diagram, and faces slightly right when Lolo occupies the yellow zone. We can determine which zone Lolo occupies mathematically similar to how we computed left or right.


Let’s say hypothetically that Snakey lives at coordinates (0, 0). If Lolo stands at coordinates (5, 4) then Lolo is 5 spaces to the right of Snakey and only 4 spaces above Snakey. This means Lolo is further to the right than he is above, and that math tells us that Snakey should use the “look far right” sprite.


If you’re not convinced, then take a look at that diagram again (I’ve added it again conveniently below this paragraph!) and pick any grid point touching the green zone, not just on the border. Treat Snakey’s point as (0, 0) and you’ll see that the x value of the point you pick always has an x value at least as large as the y value (whether positive or negative, the magnitude is what matters). The moment the y value becomes equal in magnitude or greater, you start crossing into the orange zone.


You will appreciate this diagram I spent so much time making! Now with added number line labels for your viewing pleasure.


Snakey isn’t always at point (0, 0) though, so we need to make this equation work in a more general way. Really, what we care about is the X-distance and Y-distance between Snakey and Lolo regardless of where they are, and we can get those by subtracting Lolo’s X-coordinate from Snakey’s X-coordinate to get horizontal distance, as well as subtracting Y-coordinates to get vertical distance. Negative numbers are possible if Lolo is to Snakey’s left or is below Snakey, so we should take the absolute value of the distance to ensure we’re always working with positive numbers. Then, if the Y-distance is greater than or equal to the X-distance, Snakey uses its far-facing sprite, otherwise Snakey uses its slight-facing sprite.


There are some notable corner case behaviors to point out. One of them occurs when the player moves left and crosses into a different zone; Snakey switches its facing sprite the instant the player begins moving into that new zone even if Lolo ends up standing perfectly on the border of those two zones. But when the player moves right and crosses into a new zone, Snakey waits until the end of the full movement, when the player’s entire body has definitely crossed over before switching to a new sprite. This can be easily explained by fixed-point arithmetic and integer rounding of the coordinate while Lolo is sliding between grid points. I will probably not preserve this nuance of the behavior, instead preferring a more consistent behavior.


private enum SnakeyDirection {VERY_LEFT, LEFT, RIGHT, VERY_RIGHT}


private SnakeyDirection getSpriteBasedOnPlayer(Point playerPoint) {

    Point centerPoint = this.region.getCentermostPoint();


    // Special case where Lolo is directly on top of Snakey,

    // which happens when Lolo stands on Snakey as it respawns

    if (centerPoint.equals(playerPoint)) {

        return SnakeyDirection.VERY_LEFT;

    }


    int horizontalDistance = playerPoint.x - centerPoint.x;


    // Special case where Lolo is in the same column as Snakey

    if (horizontalDistance == 0) {

        return SnakeyDirection.LEFT;

    }


    int verticalDistance = Math.abs(playerPoint.y - centerPoint.y);

    // Horizontal distance being greater than 0 means Lolo is to the right

    if (horizontalDistance > 0) {

        if (verticalDistance > Math.abs(horizontalDistance)) {

            return SnakeyDirection.RIGHT;

        } else {

            return SnakeyDirection.VERY_RIGHT;

        }

    }

    // Otherwise, Lolo is to the left

    else {

        if (verticalDistance > Math.abs(horizontalDistance)) {

            return SnakeyDirection.LEFT;

        } else {

            return SnakeyDirection.VERY_LEFT;

        }

    }

}



One last thing: when the last heart is collected and the chest is open, Snakey will ignore all that code we just wrote and will do a dance instead. No math here, they just face very left, then left, then right, then very right, then very right, then right, then left, then very left, ad infinitum. I guess they get excited? Or maybe scared that you’re going to voip them from existence? Eugh, who’d have thought these games were so existential?


♫ We like being ♫ oh we like being ♫ oh we like being alive ♫


Okay, this math is done, and it wasn’t even that hard. This isn’t even a gameplay mechanic, it’s totally cosmetic, so what makes Snakey harder to implement than Medusa?


Oh right, the behavior I’ve already mentioned several times in the context of implementing Snakey


Ah. Yes, egg-riding is complicated behavior beyond anything we’ve done yet, especially since it requires two entities coordinating their movement in sync. It’s not Snakey-specific behavior, because any creature except the Medusas can be egged and then pushed, but I’ve been saving it for Snakey because being egged and pushed is, like… Snakey’s thing. It’s almost the only gameplay mechanic that separates Snakey from being a rock with eyes happy.


Seriously though, egg-pushing and egg-riding is such a mammoth task that I’ll have to cover it in a post all on its own. I also won’t consider Snakey truly “done” until Snakey is eggable and eggs are rideable, so another post will follow this one shortly to discuss pushing and riding. Stay tuned, same dood time, same dood channel.


Oh, and a parting thought; any guesses as to why Lolo does the walking animation while riding an egg? Leave your funniest answer in the comments.

Comments

Popular posts from this blog

Demo is here!

Who Is This For?