I was trying to type “bad” and I typed “baf” by accident and racilepiebird decided it meant “best and funny” so I’m gonna start using baf as a compliment now
you are all baf
baf is baf
The thing about programming is that sometimes problems just seem to go away by themselves, and when that happens you should be very, very scared.
Stop me if you’ve heard this one before: there’s this girl, see—a pretty little thing—and she’s supposed to be the hero of the story. But everything she does is by the men around her. She lacks agency in her own story. She’s not acting independently. She isn’t free to make her own choices. Like, say, a woman without a voice, and the sword who’s there to fill the silence? That’s the glaring red flag that comes with Transistor: it sure looks like a game where you’re not supposed to project yourself into the character of Red as much as want to protect her. If Red doesn’t have a voice, then it’s really the sword’s story, isn’t it? It’s so glaring that it seems like Supergiant Games wants you to think that going in, because the first thing they do is turn that impression on its head. Red doesn’t just have agency within the world of Transistor—the game itself is an exploration of what that means. We’re going to dive deep into spoilers (right through to the game’s end), so stop here if that’s a problem. See, Red may be silenced (literally, when silencing women is usually a figurative act), but that doesn’t keep her quiet. She doesn’t listen to Transistor, as we’ll call her sword-bound companion, who wants her to keep her voice down. Not out of any desire to crush her or keep her contained—he just wants to keep her safe. There’s a lot between Red and Transistor, and it’s important to him that she do the sensible thing in reaction to being targeted by dangerous enemies. He wants her to get out of town. Lay low. Be quiet, and mourn her voice (and everything that’s been taken from her) in safety and silence. So he guides her as best he can, leading her to the bike she can take out of town. Leading her to the exit. And that’s where it becomes clear that Red has all the agency she needs, because she leaves that exit in the dust. She’s heading into town, and the little voice in her sword can’t do anything to stop her. He’s still there to help her, though. To fight the Process that are slowly devouring the city of Cloudbank, to find the reasons why the mysterious Camerata killed him and took her voice. She wields him like a tool, and keeps him close for comfort. She’s the one doing the acting, though—Transistor can only react. She’s a woman without a voice, but she still has her body. He just has his voice. Even once it’s clear that Red holds the agency in this game, it’s not as obvious that the game is about agency. The game is restrained with its text. It’s slick as hell, beautiful, overwhelming, and more than a little aloof. There’s a lot of game to play, but sometimes it feels like you’re sightseeing in Cloudbank rather than plumbing its secrets. All that glitz settles atop the game’s emotional heart, the relationship between Red and Transistor, leaving little need for much deeper reflection. It’s so pretty. So romantic. You might think you shouldn’t ruin it by overthinking anything. Until the game’s final moments. Here’s the big, giant spoiler: when everything is said and done, Red doesn’t take the action anyone expects her to. She doesn’t try to fix the city, to make the best of what’s been done, even though the power is explicitly hers. She literally holds the key to remaking Cloudbank in whatever image she’d like. Instead of doing any of that, she repairs only what she needs to find the body Transistor was pulled from, the body of her lover. Then she sits down and ignores his pleas from within the weapon as she turns it on herself. That moment is a record scratch, even as the lovely strain of Paper Boats begins to play over the credits. It’s Transistor forcing you to pay attention to the meaning, the message, because it’s just far too awful otherwise, isn’t it? Romantic, in a Romeo and Juliet sort of way, but brutal. Red was supposed to fix Cloudbank. She was supposed to defeat the Camerata and save the day. Because… Because that’s what we wanted, and what the sword said she was doing. Transistor isn’t an external narrator, omniscient and infallible. He’s a character in the story, talking to fill the silence. He has no special insight into Red’s actions, so he frames them as best he can from his limited perspective. Red is hurt, she’s lost her lover, she’s scared—she should flee. But then she doesn’t, so maybe she isn’t that scared. Maybe she wants what any good woman would want: to fix things. And that’s how he explains everything she does from then on out. He believes that together, they’re trying to find the answers. They’re trying to solve the problems, to fix what’s been done to Cloudbank. And maybe (desperately) to fix what’s been done to him. But at some point along the way, Red decides that she isn’t out to fix things. Three men try to direct her: Transistor, and two members of the Camerata, Asher Kendrell and Royce Bracket. They all expect her to do the right thing, instructing her and assuming she’s not only listening but agreeing. Transistor tells her to leave, and when she doesn’t, he talks about all the ways they can still save the day. Asher wants her to understand, and tells her to fix his and his husband Grant’s mistakes. Royce tries to lure her in with the truth, and leaves her in a position with nothing to do but the right thing: give up the Transistor so they won’t all be utterly destroyed by the Process. And with no other choice, she does. The moment she’s given a choice between giving up or destroying Royce, on the other hand? She does what she has to do to protect herself and her lover. Red doesn’t get much of a voice in the game, but she isn’t as silent as she seems. Her actions speak volumes, of course. She doesn’t run—she goes for the Camerata, destroying the two she can get her hands on and walking over the corpses of the other two. She also speaks through the terminals scattered around town, leaving her own comments on the news and voting in polls. Window dressing most of the time, but at key moments she uses them to communicate just how dedicated she is to this fight. “I’m going to find the thing that’s doing this and I’m going break its heart,” she tells Transistor as he’s addled and weakened by the Process. And then there are the songs. Red’s a singer whose voice has been stolen. Poetic, yeah, but also important. We see next to nothing of her history, learn little about her character, but the songs on the Transistor soundtrack are a textual, canonical part of her. There’s no doubt they’re supposed to be taken this way. As her database entry says, “when asked about her past and influences, [she] would often say her work spoke for itself. The song that gets the most attention in-game is In Circles, and it’s telling. I hear you buzzing, a fly on the wall In through the window, and up through the hall Flying in circles, just trying to land I see you hurting, I do what I can But I won’t save you I won’t save you Not the words of a fixer or a savior. Red’s not going to solve everyone else’s problems. She makes that extremely clear. In We All Become, she tells the world she’s not sticking around for its ending: When you speak I hear silence Every word a defiance I can hear, oh I can hear Think I’ll go where it suits me Moving out to the country With everyone, oh everyone Before we all become one Again, Red’s having none of anyone else’s plans. She’s the type to act. Once she might have thought she’d run and leave everything behind if the world fell to pieces, until it happened and took everything from her before she could. And as the final credits roll, Red’s voice carries us through Paper Boats: I will always find you Like it’s written in the stars You can run but you can’t hide, try A romantic sentiment, couched in rather threatening terms. “You can run but you can’t hide” is a vengeful statement, particularly compared to the verse that echoes later: I will always find you Like it’s written in the stars We can run but we can’t hide, try The change from “you” to “we” puts Red on the side of the other. Her enemies can’t run and hide from her, and she and Transistor can’t run and hide from them. But she’ll find him, and she’ll find them. All of this slips by the characters in the game. Her lover tells a story of heroism, a tale where the two of them save Cloudbank, win the day, and somehow get each other back. Camerata member Sibyl dies too soon to get much say, but her database entry makes it clear that she wants to possess Red. Asher looks to Red for redemption. Royce just wants to cast her aside so he can have what he wants. But Red isn’t the sort to step in line for anyone but herself. When Sibyl arranges Red’s lover’s death, Red doesn’t fall into the other woman’s arms for comfort. She also doesn’t flee, like Transistor wants, or save the day, like he and Asher want. And she certainly doesn’t surrender to Royce. Instead, she stays to fight. She cuts her way through her enemies. And when there’s nothing left but to rebuild a city that someone else destroyed, she doesn’t care. She won’t save them. She wants her lover back—she’s promised to always find him. She’ll go where it suits her: into the world inside the sword, the world that contains her lover. She destroys everything that could threaten the two of them, and when there’s nothing left, she goes to him. Once we stitch together all the fragments of her voice, we realize this is consistent with everything we know about her. But as players, we’re fighting through Cloudbank, too. We want to save the day. And when Red takes her own life in the game’s final moments, she takes that away from us. Transistor isn’t about whether or not Red has agency. She makes clear that she does in the game’s first five minutes. It’s about what her agency really means in a game when everyone wants to control her—including the player. It’s about the freedom to make the unpopular choice.
I THOUGHT I WAS DONE GETTING MY MIND BLOWN BY TRANSISTOR
BUT HOLY SHIT
I’m in the middle of implementing some two-dimensional collision logic, and I thought I’d share how it works.
The solution is based on the separating axis theorem, which for this purpose can be stated as:
If two convex polygons are not colliding, there must be some separating axis such that when the polygons are projected onto the axis, the projections are not colliding.
A more friendly (though not entirely accurate) way of saying this is:
If two convex polygons are not colliding, there must be a way to draw a straight line between them without touching either one.
Therefore, if we want to find out if two convex polygons are colliding, all we need to do is prove that there is no separating axis; i.e., it is impossible to draw a straight line that separates the two.
There are two parts to solving this problem: finding out which lines we should test, and actually testing the lines.
Part 1: which lines should we test?
Obviously, it’s prohibitively expensive to try every possible line between the two polygons. What’s the smallest set of candidate lines?
If you think about it, a convex polygon is always going to start colliding when one of its faces touches the other polygon. That is, if you were to mash a rectangle into a hexagon, from any angle, the part of the rectangle that will always touch the hexagon first is one of its sides (two if it’s a corner). The same is true from the hexagon’s perspective, but might not be quite as easy to imagine.
Because the first things to collide between two polygons will always be some set of their faces, the lines we should try are the set of lines which run parallel to each face of each polygon.
Part 2: how do you test a line?
Again, it is obviously too difficult to test every single line that runs parallel to any face of either polygon.
At this point, the metaphor of “drawing a line between the polygons” starts to break down. What we really want is to ask, for each face of each polygon, "is there a gap between the polygons relative to this particular face?"
Recall that at this point all we’ve decided is that we need to test, for each face of each polygon, whether some line that runs parallel to the face can be drawn without cutting either polygon. There isn’t really a clear way to do this. What we need to do at this point is flip the problem ninety degrees and test gaps rather than lines.
To check whether there’s a gap between two polygons relative to a particular axis (hold on, we’ll get there), we need to use a tool called scalar projection.
Projection (we’ll get to the scalar bit in a moment) is basically taking a polygon and throwing it, splat, against a wall. The splat mark is the projection of the polygon.
But what’s the wall? That’s our axis, or more precisely our axis of separation. What did the separating axis theorem say again?
If two convex polygons are not colliding, there must be some separating axis such that when the polygons are projected onto the axis, the projections are not colliding.
Okay! So what this means is pretty much
If there is any wall at which you can throw two polygons, splat, and the splat marks don’t overlap, then the polygons aren’t colliding.
This interpretation means we can ask the following two questions, which are much easier to answer than our original query:
Part 3: How do you project a polygon?
I’ve tried to keep the descriptions of projection in three dimensions for ease of visualization, but at this point we have to drop down into two dimensions because I haven’t the faintest idea how all the math works in three.
If it helps, you can visualize the 2D version of the above by imagining that you’re looking top-down at the crazy man hurling polygons at walls.
So, scalar projection. How do you do it?
s = |a|cosθ = (a ⋅ b) / |b|
Isn’t mathematics notation great?
no it isn’t
Okay, so. You need to understand some things before being able to understand the reasoning behind this equation.
Part 4: Stuff about vectors
The first is the notion of a vector. A vector is an arrow. I don’t care what you may have heard in math or physics. For every single useful application of vectors I’ve seen in game development, thinking of a vector as an arrow is perfectly sufficient. The important properties of a vector are its length and its direction.
Another powerful thing to note about a vector is that you can decompose any vector into components. The components of a vector are basically a bunch of smaller vectors (arrows) that get you to the same place as the original vector. Usually, you keep track of components relative to each axis of your coordinate system, so you can generally expect a two-dimensional vector to be modeled as a structure that has an x component and a y component. This is how we’re going to represent our vectors.
So. Vectors have a bunch of operations you can do on them. The ones we need here are the concept of a normal, the magnitude, the unit vector, and the dot product.
The normal of a vector is pretty much another vector with the same magnitude but a perpendicular direction. Each vector has two normals: right-hand and left-hand. You can get the normals of the vector by swapping its components (y, x = x, y) and negating either the new x component (left-hand normal) or the new y component (right-hand normal). Usually it doesn’t matter which normal you use as long as you’re consistent.
The magnitude of a vector is how long the arrow is. There are two interesting things to say about magnitude. The first is that it can be computed like this: sqrt(x * x + y * y), where x and y are the components of the vector.
The second interesting thing about magnitude is that vectors with a magnitude of one, usually called unit vectors, are super useful because they represent pure direction. In a traditional 2D coordinate system, the vector will be pointed in a certain direction, meaning it has a certain angle. The x component of a unit vector is the cosine of that angle, and the y component is the sine. If you know any trigonometry at all, you know that these values are really really useful, especially when you don’t even need to know the angle to get the unit vector. You can compute a unit vector that points in the same direction as some random vector by taking that vector and dividing all of its components by its magnitude.
The dot product can be kind of difficult to understand. On the face of things, it’s pretty arbitrary: you take two vectors, a and b, and sum their element-wise products: a dot b is a.x * b.x + a.y * b.y. Indeed, the only thing that’s particularly useful about the dot product of two random vectors is that it’s positive when they point in roughly the same direction (the smallest angle between them is less than ninety degrees) and negative otherwise.
But something special happens when b is a unit vector. Somehow, magically, when you take the dot product of some random vector with a unit vector, that turns into throwing the first vector at the second. SPLAT. What you get out is a number that tells you where the splat mark from the first vector ends up on the wall containing the second vector.
This sorcery has pretty much everything to do with trigonometry. Feel free to try and visualize it. The top-down thing helps. But now, I figure it’s high time we get back to collision detection.
Part 5: How do you check if two projections overlap?
So, you have your two polygons. You take each vertex of each polygon, and splat it against a unit vector running parallel to some face of one of your two polygons. You end up with two sets of points. What do you do next?
Well, we don’t really need the points that are hanging out in the middle of the splat mark. We really only need the smallest and largest point in each set.
Having thrown all the deadwood out, we’re left with two line segments. How do you test if two line segments overlap?
BAM. COLLISION DETECTION.
Part 6: Putting it all together
I think they’re going to kick me out of this coffee shop soon, so I’ll be brief. We’ve seen how to describe the collision problem in terms of gaps between faces, and we’ve learned how to measure the presence of those gaps. What does the actual algorithm look like?
Here’s some pseudocode:
for each face in union(faces in polygon A, faces in polygon B): axis = unit vector running parallel to the face point set A = project polygon A onto axis point set B = project polygon B onto axis left point A = find minimum(point set A) right point A = find maximum(point set A) left point B = find minimum(point set B) right point B = find maximum(point set B) if left point A > right point B or right point A < left point B: return false return true
This describes the general algorithm. Here’s the helper functions:
to find a unit vector running parallel to a face: unit vector = new vector unit vector.x = face.x2 - face.x1 unit vector.y = face.y2 - face.y1 unit vector = unit vector / magnitude(unit vector) return unit vector to project a polygon onto an axis: point set = new set for each vertex in the polygon: projected vertex = dot(vertex, axis) append(projected vertex, point set) return point set
Okay, I have to go. I might edit this post later. Hope it was helpful! :D