Game Dev Diary

Day 1: Isn’t Mesh a 3D only shape, though? #

Weird start but today I took my friend A (mysterious names all around, also I hope nobody reading this ever watched Pretty Little Liars) through the core Bevy concepts in an interactive tour/lesson hybrid. Also I realized midway this might not be the best way to start someone who is very new to programming but she really gets bored of anything that isn’t immediately practical so I thought Bevy might be a good place to start. And I actually still think that but I think that Bevy needs some training wheels. And we will explore what those wheels look like today.

So where did we start from? Well my friend got to this example herself independently and written it in her editor and ran it successfully. So the first thing I wanted to do was make sure she fully got what was going on in it.

She started by pointing out that change_clear_color was where the Window was created, but that’s not true so I decided to take that angle in the explanation. So I told her she can comment out that system and see what happens. Once commented out the window still appeared with its color but spacebar was no longer working. We went over the other parts and then I told her the secret, everything happens in the magical DefaultPlugins where the Window is created and the “game loop” is started. Commenting that out results in an app that just immediately close.

I also briefly re-explained Commands as “instructions for Bevy to do at the end of the frame.” I am not even sure of the accuracy of this definition, hope it’s accurate enough… Look we are learning together, like good friends should be…

Okay next up we wanted to draw a circle! This is where things get a bit trippy. I wanted to show her a bit of the beauty of docs.rs so I guided her to Bevy’s page there and we searched for Circle and found it! And then we scrolled down, I wanted to show her how Circle is a component (or more likely a bundle), like any other and that we can create a circle and spawn it! I was surprised Circle wasn’t a component, in fact Circle had almost no useful functionality besides… turning into a Mesh. “Ah well, I guess Mesh is what implements Component then,” I thought to myself. Oh how wrong I was.

We went to Mesh’s page next and to my dismay it also didn’t implement Component. which after researching more a bit now I realize might be some kind of intentional limitation? Let’s talk about this at the bottom of this diary entry.

I was honestly quite perplexed. So I decided, it’s example time! We went to 2d and found a fitting example that was called 2d_shapes.rs. Neat. And they even draw the Circle, and oh heck what is this:

// Circle
commands.spawn_bundle(MaterialMesh2dBundle {
    mesh: meshes.add(shape::Circle::new(50.).into()).into(),
    material: materials.add(ColorMaterial::from(Color::PURPLE)),
    transform: Transform::from_translation(Vec3::new(-100., 0., 0.)),
    ..default()
});

Well now I get it… but this is not super beginner friendly… but I guess we have to roll with it? (or do we?) I can’t just tell my friend this mess is a circle without explaining why it’s like that. So now this diary entry will turn into an initial writing for “ecs2” the article that is going to one day follow up the other one I wrote a year ago at this point.

BUT BEFORE THAT. My friend looked at this in disgust, and not for the reason I was ever going to guess. No, not because it looked overly complex or confusing. Not at all. She said in disgust, “okay I don’t like this” and I asked “why?”

“Because Meshes are only for 3D.”

oh.

Then I started saying “Well but see” and she was “Heck no” and we started laughing and opening Wikipedia and reading the first few lines and laughing some more… Not where I expected that to go at all. After the laughing subsided I explained to the best of my ability that yes well meshes are 3D but that every 2D shape is also a valid 3D shape. She knows meshes are a bunch of triangles and so I pointed out that one triangle alone is still a valid mesh and it’s clearly 2D and that one could make a mesh of a bunch of triangles layered in a flat surface and it would be 2D.

She thought “meshes are 3D and sprites are 2D.” And I don’t really understand sprites well enough to explain why that’s not exactly the case… Real question for future Mathy though, are sprites just a texture rendered on a 2D mesh lol?

We opened Wikipedia page for sprite and read some more and I explained that if you wanna draw a circle you really have two main ways of going about it. You can either draw a lot of triangle slices and make a mesh that looks like a circle or you can draw a circle image and render it as a sprite. We were both happy enough to move on for now.

Okay so I explain to my friend briefly how this code is meant to be read, which is not an ideal reading experience let me tell you:

// you first read this
shape::Circle::new(50.)

// then this
.into()

// then this
meshes.add(..)

// and finally another one of these...?
.into()

Luckily I know my way around Rust and docs.rs so I will be able to figure out what the heck all of this is for. Let’s go!

// we create a circle and give it some radius
shape::Circle::new(50.)

// then we call .into which I described as a magical Rust incantation 
// for triggering `From` implementations which I also explain briefly
.into()

// and then I point out that meshes is defined earlier as a ResMut<Assets<Mesh>>
// and we are adding this mesh to them... but what does that mean?
meshes.add(..)

So here is that ecs2 tangent. I really like explaining components and entities as a table and systems as things that operate on this table. And yes I know that resources are factually part of this table in most ECS implementations but I personally think it’s easy to think of them as something separate. So I explain that resources are like a bucket next to the table that we put stuff in… and that there are actually many buckets each for a different “type of thing”… and assets are stuff we will put in this bucket. And that to find something from the bucket we will wrap it in yarn and pull on the yarn when we need it again…

See, I actually really like the yarn and bucket metaphor but this is the second time this happens to me; I remember this actually happened to me personally a year ago when I was learning Bevy I tripped on Res<Asset<T>> hard. My brain merges the resource concept with the asset concept. Still I made sure to explain that Asset is mainly a performance concern to not load things multiple times and have multiple copies around of them.

More explanation and tripping on my own misunderstanding later, she asked something like “why not just use like different drawers for all the different stuff instead of wrap them in yarn?” Which is quite a fantastic question. “How would you find the drawer that contains the thing you’re searching for?” I responded. She said, “well you could sort them alphabetically.” I replied, “but they are just unnamed thingies,” excited for the direction where this is going. “You could name the drawers.” perfect. “Well but then you would need to look through all the drawers for the drawer with the name you have on a piece of paper to remember which drawer has the thing right?”

Yeah okay I have an unconventional way of teaching, sue me.

Now I am going to interrupt this story briefly to mention how much drawer thing was exactly what I needed and I am so happy she got me there. Resources are named cabinet of drawers. Assets are buckets you usually put inside the drawers and wrap stuff in them in yarn. Yep. I got there finally. Perfect metaphor, no take backsies now.

Eventually we got through the rabbit hole I took us down through abut resources and assets and we came out with a Handle. And then I looked at that last .into() and asked myself “Wonder why that one is there.”

We got to the docs for it and… wat. Mesh2dHandle is a new type around Handle<Mesh>? And Handle<Mesh> is public? I actually have absolutely no idea why this is like that. I understand it’s trying to do some kind of distinction or specialization between 2D and 3D but why 3D doesn’t get its own also if it’s pub and uses .into() instead of .try_into() then it’s clearly possible for you to just stick a 3D Mesh in there and be like 🙃. Docs kinda lacking for it too. I wonder if this is some kind of temporary limitation or something permanent? I’d have to look into it more. Anyway I try to briefly explain this but give up rather quickly because I didn’t really get it myself too.

Next we go briefly over the material which is basically exactly the same as the mesh except with less confusing .into()s and last but not least we go over the transform which she is fairly familiar with already. Finally the default is easy to explain. Okay great! We have a circle and we get how it works!

We revise things and I let her poke at different parts of the circle and we both get to see what happens. Impressively the error for not passing a Material2d to MaterialMesh2dBundle is a work of art… at least if you understand Rust. (I am not sarcastic, it’s genuinely amazing that the lack of inference basically forces you to provide a Material to the MaterialMesh2dBundle or otherwise you really should be looking for another bundle. Was genuinely impressed.)

She cracks few jokes here and there about Bevy misusing 3D meshes and we go on a little tangent explaining why rotating a circle could be relevant in 2D if we wanted to, where I try to rotate the circle around the Z for a few seconds before facepalming about my absurdity and remembering that’s the only axis I wanted to avoid not use for demoing what a rotated 2D circle would look like lol.

And that’s all for today! We watch an episode of The Owl House which you should be watching right now instead of reading this rambling madness.


Okay this is the part where I talk to myself a bit more because I am not already insane enough so.

Mesh is not a component #

Right. So I guess Mesh is not a component because… Mesh2Handle is? And for 3D… Handle<Mesh> is a component because Handle<T> where T: Asset and Mesh is Asset. Well okay then. Whacky and a bit overly complex and I am not sure if it’s some kind of temporary limitation or not but I will take it. Honestly my gut tells me it’s specialization problem and specialization is probably never happening so it might not be very temporary even if it is. Either ways always exciting digging through Bevy’s traits and their implementations. It genuinely feels like a treasure hunt and I love it.

Bevy needs training wheels #

I have this dream of teaching programming via game dev powered by the helpful Ferris and the adoring Bevy. I got to have a bit of a taste of what that would be like today. It’s a bit too hard to swallow. BUT I THINK IT’S FIXABLE:

// Circle
commands.spawn_bundle(training_wheels::Circle1 {
    color: Color::PURPLE,
    translation: Vec3::new(-100., 0., 0.),
});

What if we could start here? What if we could then take it here:

// Circle
commands.spawn_bundle(training_wheels::Circle2 {
    color: Color::PURPLE,
    transform: Transform::from_translation(Vec3::new(-100., 0., 0.)),
    ..default()
});

YOU SEE WHERE I AM GOING WITH THIS? And it’s really not even that hard to make at all. And it’s such a natural expansion. Maybe tomorrow I can make these training wheels and my friend can take them for a ride! Maybe that’s what tomorrow’s entry is going to be about?