Game Dev Diary

Day -366: Nannou, helping L, and lots of noise #

Before I can explain this backstory I need to give a bit more context; some backstory for the backstory if you wish.

My friend, L, in her free time a while ago was interested in following Daniel Shiffman from The Coding Train through the Nature of Code 2 playlist. I introduced her to Shiffman a while back and we both love him very much. She showed interest in trying to do that in Rust, the language I have been selling to everyone for a few years now and let me tell you I am an amazing saleswoman, since she already had one successful project in it. So without hesitation I encouraged her to do so and I quickly linked her to Nannou, an open-source creative-coding framework for Rust, in other words Rust's Processing!

She started her journey and at the time I was quite swarmed with work and so although I was able to help here and there, some stuff I really wanted to help with were out of reach. Namely she was following Graphing 1D Perlin Noise and Nannou didn't have a built-in 1D Perlin Noise generator but looking at its code I felt like I could make one by inferring from, or interpolating I guess, the 3D and 2D implementation and just removing things from them. But I sadly didn't have the time. So I told her I will add this to my To-Do list and will get back to it as soon as possible.

Fast forward many many months, the day is 5th of November, a Friday, also Bonfire day or Guy Fawkes day as L explained to me, I have already quit work a couple of weeks prior and feel ready to get back to some fun side projects and so I tell L,

So wanna work on the noise stuff this weekend? [Followed by the peek emoji below]
Peek Emoji

I got an I guess?

6th of November, or as some might say, the birthday of Memory itself. I dived alongside L deep into the depths of Nannou, right into noise-rs, the library that Nannou uses for its noise primitives, I went ahead and implemented 1D Perlin Noise. After lots of trouble that relates to the lack of clarity in majority of libraries about what algorithm is actually running behind the scenes we realized we also probably need to implement 1D BasicMulti. Long story short for the curious, Perlin noise is not actually THAT noisy...? Pure Perlin noise doesn't look very noisy...

Perlin noise having a rather sine-y wave look

We are still not sure if we missed something up or if this is just how things are, but I am decently certain this is just how things are. What libraries like Processing and P5.js do to generate their much more interesting noise is generate the above multiple times at different frequencies and octaves and add them together, hence the need for BasicMulti which does just that.

A more interesting noise generation using BasicMulti<Perlin>

We messed around some more with noise and then called it a day.

After that I decided I should start digging into some of the Nature of Code materials, I went through Coding Challenge #24: Perlin Noise Flow Field and found it fairly exciting but I missed the times when I used to watch Dan live, so after finishing it I hopped on Coding Train Live: Shaders which was one of the latest live streams available to watch and my game dev journey began for first time properly exploring shaders alongside Dan.

My previous exposure to shaders so far have been hearing their name when people discuss games and game development and that one time I was really curious and asked a bunch of MUCH more knowledgable friends and after they explained to me I reached the definition "A shader is any bunch of code that runs on the GPU" and thought their name was bad and I prefer the name "Gunctions" (GPU Functions). So seeing Dan stumble through them as we both tried to understand them together was a really nice enlightening experience.

Sleep eventually got the best of me and I decided I will finish the rest of the live stream tomorrow.

Day -365: Down the rabbit hole we go #

The day started with me finishing up the rest of the live stream from yesterday. I think coming out of that live stream I had an okayish idea of what a fragment shader was, though was still not truly sure why the name "fragment" instead of "pixel" and was EXTREMELY lost on vertex shaders and the whys of them. Nevertheless was very excited to be learning more

I contacted L good morning and quickly said from the excitement of finally understanding shaders a bit more

Do you wanna start a repo together where we translate every nature of code sketch to nannou?

Unknowingly to me at the time this was the trap I told L at . It was one of my usual wild ideas and I didn't realize it will send both of us down two separate quantum entangled Wonderland rabbit holes.

L showed hesitancy as she says she is not good enough for this, when in reality she is really awesome at it. Though she let me show her one of the ways we can do this in Rust by having a workspace with multiple projects.

With that idea in mind though I went back to Coding Train videos, quickly found myself watching Coding Challenge #145: 2D Raycasting, Bézier curves (Coding Challenge 163) and then thinking to myself, I should really be focusing on the Nature of Code 2 playlist not these fun distracting coding challenges... and so I opened it and read through the names, and got excited by the prospect of finally following along with Chapter 5: Autonomous Steering Agents, which I have seen a bunch before in Dan's older videos, a long time ago, but never sat down to actually try to implement any of it.

With that in mind I started on 5.1 Autonomous Steering Agents Introduction - The Nature of Code but it was already pretty late so I paused for the night and went to land of dreams.

Day -364: Enter Bevy & Shaders are hard #

Woke up super excited, I resumed yesterday's video while eating breakfast. And within a couple of hours I had already finished all the Chapter 5 materials that were available at that time (up to 5.7 Path Following (Steering) - Nature of Code). I had even paused in the middle to record a voice recording titled "On the hardcoded parameters of Steering Agents" where I talked about how one could remove some of the parameters Shiffman hardcodes in his videos (like the threshold for arrival behavior). I will probably work on demonstrating that when I get to actually implementing arrival behavior so stay tuned.

This all led me to be very excited about the prospect of being able to reimplement this in a way that I consider easier to follow and reason about, Shiffman typically follows a more OOP (Object Oriented Programming) methodology which I am not the biggest fan of as I think it tends to make things more complicated and harder to follow than other methods, which we will be exploring!

But breaking the ice is hard, so instead of jumping to my computer I procrastinated by looking up more creative coding things and stumbled upon this really amazing talk Exploring generative spaces: a quickstart to generative art - GitHub Universe 2020 and that's truly when my excitement exploded and I decided, it's time to DO THIS.

Bevy. #

I wanted to use Bevy. Bevy is a truly fascinating game engine that I have been following since its inception in 2020 about a year ago that I have been extremely in love with ever since. I never got a chance to make anything with it though, and thought this was it, my opportunity to try it and learn more about game development and use my favorite game engine. If you're thinking it's a bit weird to love a game engine without ever using it, just keep reading, friend. ❤️ I hope I can impress you!

So I created a new Rust project (went with the name flocking-bevy) and I added Bevy to the dependencies, I also went with depending on Bevy's main branch.

bevy = { git = "https://github.com/bevyengine/bevy", rev = "f4cfcc0e44b91446beb49a1dbe9965cb7bcde059" }

Well first things first, I need a triangle on screen! Let's look through Bevy docs for triangle! Uh-oh... that doesn't look like a great start.

Searching for triangle in Bevy's docs result in only two options render::pipeline::PrimitiveTopology::TriangleList and render::pipeline::PrimitiveTopology::TriangleStrip.

Neither of these look like a triangle shape... What even is a primitive topology? Triangle list? Heck is a render pipeline even? I just wanted a simple triangle...

So... not a great start, and yes if you’re already experienced with other game engines you might be feeling quite disappointed something so simple is not exactly obvious, but if you hang with me for a bit longer you will see what makes Bevy shine!

So when the docs don’t help, remember one of the golden rules of Rust:

The docs have your answer, and when they don't the examples do, and when they don't the community will help you and make it easier for the next person!

So docs exhausted, into next step; let's look for an example! Inside the examples folder we see even more folders, but at the very tippity top of them one can notice the 2d folder. That's probably where we will find whatever we are after.

We find inside multiple files with sprite in their name, although most game development terms are completely foreign to me, that one I am aware roughly means something like "Render an image as an object in your game". That's not what we want. I don't really know what a texture truly is but I am fairly certain it's also a bit image-y (raster-y if you will). We need something more vector-y. I don't know what contributors.rs example is about but the one last remaining option once all of those were eliminated felt more likely to guide me than it. So I went with mesh.rs

examples/
└── 2d/
    ├── contributors.rs
    ├── many_sprites.rs
    ├── mesh.rs
    ├── sprite.rs
    ├── sprite_flipping.rs
    ├── sprite_sheet.rs
    ├── text2d.rs
    └── texture_atlas.rs

I just copy pasted the whole example into my main.rs and ran it to see what happens.

Running the mesh.rs example results in a yellow star with a black center and a soft gradient from the edges to the yellow sides to the black center

Nice! This rendered a star, I knew that roughly a lot of things in rendering are just triangles, so I bet this star will be a group of triangles because you can kind of just look at it and notice each triangle. Time to look at the code and see if we are right. Scrolling through the code quickly I notice a few hints that I was probably correct about this star being a set of triangles, in fact I notice this particular line with its documentation

// Let's define the mesh for the object we want to draw: a nice star.
// We will specify here what kind of topology is used to define the mesh,
// that is, how triangles are built from the vertices. We will use a
// triangle list, meaning that each vertex of the triangle has to be
// specified.
let mut star = Mesh::new(bevy::render::pipeline::PrimitiveTopology::TriangleList);

Oh so that’s what this topology thing was for! That makes sense, heck we are already answering some of the mysteries presented earlier! I am not really sure what’s a “mesh” however. But since I knew the whole “a lot of things are just triangles” I am fairly certain I understand what PrimitiveTopology and in particular TriangleList is for at least! It tells the engine that we want to render something that’s a set of triangles.

With a bit more scrolling I quickly stumbled upon this part though

const VERTEX_SHADER: &str = r"
#version 450
layout(location = 0) in vec3 Vertex_Position;
layout(location = 1) in vec3 Vertex_Color;
layout(location = 1) out vec3 v_Color;
layout(set = 0, binding = 0) uniform CameraViewProj {
    mat4 ViewProj;
};
layout(set = 1, binding = 0) uniform Transform {
    mat4 Model;
};
void main() {
    v_Color = Vertex_Color;
    gl_Position = ViewProj * Model * vec4(Vertex_Position, 1.0);
}
";

const FRAGMENT_SHADER: &str = r"
#version 450
layout(location = 1) in vec3 v_Color;
layout(location = 0) out vec4 o_Target;
void main() {
    o_Target = vec4(v_Color, 1.0);
}
";

Heck. And I still don’t understand shaders well enough and this uses all those weird new things that I didn’t even see with Shiffman and so have no idea about like layout? location?

And thus I went on a tangent for the rest of the day to try to understand shaders more because I want to be able to write code that I fully understand! I went over a couple of chapters from the book of shaders but quickly put it down because it almost fully ignores the existence of vertex shaders which as you can see above are the bulk of these two shaders. I watched half of Freya Holmer’s Shader Basics, Blending & Textures • Shaders for Game Devs [Part 1] but the fact it was using HLSL made me shy away from it a bit since I didn’t wanna be confused by all the differences between GLSL and HLSL (they are different shader languages, the above examples are written in GLSL; I still prefer the name gunctions by the way). However, she did give me a better understanding of what vertex shaders are, they are gunctions that operate on the geometry, you can use them to create effects like shaking for example. Then found myself watching another video series that I think was also heavily focused on fragment shaders with very little vertex focus so ended up skipping it as well.

Tried to look up the specific syntax used in these two shaders, like layout, but the official documentation is not exactly well suited for learning, at least not in the chaotic way I typically learn by jumping to the middle of a topic...

As the night grew older I realized it’s going to be a very long journey until I can fully grasp shaders, and I tried to look for examples of other shaders being used throughout Bevy and its examples but it felt like all shaders were just these two copy pasted? Thinking about it I realized these are sort of the default-bevy shaders and that they basically almost do nothing. They are boilerplate, boilerplate I can try to understand more in depth later! So as closed my eyes I decided tomorrow I will try to actually understand the rest of the mesh.rs example instead of banging my head against a shaded wall.

Good night twinkling star, I shall see you tomorrow 💛