👉 Get Rust training from Let’s Get Rusty: https://letsgetrusty.com/start-with-cherno
Patreon ► https://patreon.com/thecherno
Instagram ► https://instagram.com/thecherno
Twitter ► https://twitter.com/thecherno
Discord ► https://discord.gg/thecherno
Code ► https://github.com/TheCherno/Hazel
Hazel ► https://hazelengine.com
🕹️ Play our latest game FREE (made in Hazel!) ► https://studiocherno.itch.io/dichotomy
🌏 Need web hosting? ► https://hostinger.com/cherno
💰 Links to stuff I use:
⌨ Keyboard ► https://geni.us/T2J7
🐭 Mouse ► https://geni.us/BuY7
💻 Monitors ► https://geni.us/wZFSwSK
This video is sponsored by Let's Get Rusty.
Оглавление (9 сегментов)
Segment 1 (00:00 - 05:00)
Hey, what's up guys? My name is Ao. Welcome to something a little bit different. So, now that I'm not a full-time YouTuber anymore, I thought I would take some liberties with my content and make I don't know, it sounds silly, but more of the kind of content that I want to make and hopefully stuff that you will enjoy more as well. So, some of you may remember the game engine series. Welcome to this announcement video because we are about to begin the game engine series. Yes, the game engine series is something that I started in 2018, about 8 years ago, which is wild to say. I'm 31 now, so I was 23 when I started that series, and you know, I was working at EA on game engines. I had some knowledge obviously of what I was doing, but nothing compared to what I have today. I think the other critical difference is just how much software engineering has actually changed. And of course, I'm talking about AI. That's probably the biggest change, but there are also, you know, lots of little changes in both the way engineers write and architect code these days, but also the kinds of technologies that we have available to us. So, if I was to sit down and write a game engine today, it would obviously be very different from well, what I did back then. And again, both because the world is different, but also I am different. I stopped doing the game engine series for YouTube because, well, that's probably a video in and of itself. I won't bore you with that. The point is that I wanted to take a look at this. github. com/thecherno/hazel, which is the public version of Hazel, Hazel 2D as it became, and do a bit of a code review over it, like see what I like about it and what I absolutely hate. And whilst this, of course, is a much more dumbed down version of Big Hazel, Hazel 3D, the proper version of Hazel as I usually call it, which is accessible on patreon. com/thecherno. This is a much more dumbed down 2D version of that engine, but it does share a lot of similar design and architecture. And honestly, of course, it's a bit nostalgic to me. I just wanted to go through it and see what I think of it for all time sake. But also, I kind of want to decide what to do with this. And I posted a tweet last night where I was like, should I just do the game engine series again, but use AI? And that definitely stirred the pot. I think that's probably quite a controversial statement to make, but to be clear, it's not that I would just let AI write the whole engine and do nothing. I would just use it to enhance my own programming while still controlling the architecture, the design. I'm just getting it to do all the implementation details whilst also looking over it to make sure that it's doing what I wanted it to do. Because what that would effectively do is completely supercharge the development of a game engine, right? Like one of the reasons why the game engine series didn't really make it was just because it was too much work for one person to sort of write every line of code on camera and make it into a video that also explained, you know, every line of code that went into making a game engine. It's just there's just too much information there. And so if AI is helping generate a lot of the boiler plate and just the magnitudes of code that has to go into an engine, but with someone still obviously looking over it and making sure that it's doing the right thing, you know, it still takes someone who knows what they're doing to drive this, we could probably achieve a lot. And so that's kind of exciting to me. I don't know if that's exciting to you. And so the question and the decision that I have here is do we keep building on this version of Hazel and we just keep developing it or do we just start a new engine from scratch? Because in all honesty it wouldn't really take too long to catch up to this public 2D version of Hazel because again every line of code for this version was written as part of the game engine series playlist on YouTube. So whilst there are over a 100 videos it's not you know a massive amount of code or anything. But also, I guess the downsides of this is, you know, you can even tell by the getting started section of the readme that you need Visual Studio 2017 or 2019 because this is quite old, right? This project was started a long time ago. And so, not only is this an old base to sort of work from, it also would be really difficult for someone who hasn't watched the 100 plus existing game engine series to actually fully understand what's going on. So starting fresh might actually be cooler, but we can still use this as a guide, I guess, and take some of the good architecture that I like from this and use that to develop something new, something fresh. So that anyway, that's my idea for this new like game engine series. Please let me know in the comments below if that is something you'd be interested in. And again, what is the value of this if AI is writing most of the code? Well, the value shifts a lot more towards the design, the architecture, the actual engineering that's going on. Because of course, we're not just prompting the AI to oneshot a game engine. We're doing this very fine grain and we're going through piece by piece and just being like, hey, you know, use this subm module like GLFW to generate a window for me that will work in this way and here's some architectural guidelines and this is how I want you to lay this out. It's more of that than just make me a 3D game engine because of course that would be both a bad result and a bad product, but also not really useful to us trying to learn.
Segment 2 (05:00 - 10:00)
So hopefully that makes sense. Um and then today what we're going to do is just go through this and see if we like it. And yeah, it'll just be hopefully a good time going through this and seeing h seeing what this is like. Now before we get into it, even 5 years ago, yeah, people were rewriting Hazel in Rust, which is quite funny. So if Rust is a language that you're interested in, you should definitely take a look at Let's Get Rusty, the sponsor of this video. Rust is a low-level programming language that's kind of like C and C++, but safer. It's got strong safety guarantees, particularly around memory management and ownership, and that makes multi-threading and concurrency a lot safer. And honestly, it does build a lot on CSA++ and makes it better in many regards. That's why companies like Google, Microsoft, and Amazon have been adopting it more and more in production. So, what I'm basically saying is you should probably learn Rust. And Let's Get Rusty focuses specifically on Rust education and job ready training. And it's made by a fellow YouTuber with a YouTube channel all about Rust. Did I mention that Rust also keeps topping Stack Overflow's most admired language year after year? Yeah, a lot of people seem to really like it. Let's Get Rusty runs structured programs for individuals and teams. And they've helped thousands of developers over the years actually get productive with Rust, not just watch video tutorials and forget everything a week later. And here's the good news. They're running a new cohort next month. Spots are limited, so now is a really good time to check it out. Just go to let's getrusty. com/sartwithshon. Link will be in description below to find out more about their training. Huge thank you to Let's Get Rusty for sponsoring this video. Okay, so what I've done is cloned this and I actually haven't done anything beyond that. So this is a fresh clone of github. com/thechern/hazel which again is the public 2D version of Hazel, the simple version. And there is a script here called wingen projects and setup. bat. I don't really remember. I'm actually going to treat this like someone sent me a project to code review. So there's going to be part of the fun of this is like you know I can complain a lot about the kinds of projects that I receive. So let's go through this and see if I'm able to stick to my own high standards of how a project should go. Okay, so we start by cloning recursively. I've done that. And then we configure the dependencies. So run setup. bat found in script. So okay, so setup. bat is what we run. If not installed, then it will I don't know what that even means. We'll execute the Did I write this readme? Okay, it's time to get blame on the readme. No, I'm just kidding. Okay, so let's just run setup. bat and see what happens. So premake not found. Would you like to download pre-make? Yes. I didn't even know that I did that. And then Vulcan SDK. You don't have the correct Vulcan SDK. Engine requires 1. 3. Would you like to install 1. 3? Okay, sure. I've got 1. 4 installed. So, we're going to download this. We're going to run the installer. I think it says something about once it installs, you have to rerun setup. bat. So, is it actually running the installer? Oh, okay. Look at what happened here. Unsupported 16 bit application. The what now? What? This is a 16bit Vulcan installer. Excuse me. That can't be right. Where did it download that to? That's hilarious. How did that even happen? That's got to be like a corrupted download or something, right? Okay. So, where is it? It's in uh Hazel Vendor Vulcan SDK. Yeah. And it's done the same thing again. Let's have a look at this installer. So, what? Hazel Vendor Vulcan SDK 1 kilobyte. Yeah. So, the download just hasn't successfully completed. Okay, that's strike one cho. Let's go over here and download the latest Vulcan SDK. It's a bit confusing that it requires Vulcan because this is an OpenGL engine. It must be for spurvy shaders. That's got to be the reason. In which case, it really doesn't matter. Like, I don't think it's worth us going through and downloading and installing like 1. 3. Also, as if I don't have I definitely have 1. 3 on this system. I think my Vulcan SDK environment variables just set to 1. 4. So, we have two options here. We can either bypass the validation that it does inside the setup script, which would be setup. py right over here. So, there's a setup Vulcan Python script. And then you can see it's requiring 1. 3. So we could just be like actually 1. 4 is fine. Or the other alternative would be to change my Vulcan SDK environment variable which you can see over here in check Vulcan SDK. It's trying to get Vulcan SDK and then basically read this. So it's trying to see if this string which used to be 1. 3 is contained within the path of the Vulcan SDK environment variable. And just to show you that so that's what the Vulcan SDK environment variable looks like. So within that string it's trying to find 1. 3. can't find 1. 3. It's not a very good way to validate that, but anyway, it can't find it, so it errors us. So, yeah. So, we could change that to 1. 4, or we could just change our Vulcan SDK to point to one of the other installed SDKs that does have 1. 3. So, what I'll do is I'll just set this to 1. 4 and rerun the setup script cuz that should just work, I think. There we go. Goes past that and generates all of our pre-make stuff so that we get project files for Visual Studio and we have a Hazel solution. Oh, 2026. I haven't actually used 2026 yet. I didn't even know I installed 2026. No, I don't know. Do we try this with
Segment 3 (10:00 - 15:00)
2026 or should I go back to I have actually never in my life launched Visual Studio 2026? As you can see, that's probably why it's showing me all this welcome stuff. This is great, but I don't know. Do we risk it? I guess we risk it. Why not? This is a fun video. It's supposed to be a fun video. Gh. What's with all the new UI? This looks weird. Everything's like round. Okay. And you can see it's still kind of using I guess it's still using Okay. Let's do a release build of hazelnut. Sure. Um probably should have built the whole solution, but why is everything round? Is this docked? See, this is annoying cuz this is this looks like it's not docked, right? Cuz it's round. Yeah. And but then you dock it and it sort of looks like it's not docked. So Visual Studio 2026, I hate it. Okay, so that actually built really quickly. Um, I don't know how long that took, but it was like, you know, way quicker than modern Big Hazel. All right, let's run this. And okay, we immediately get this like super weird open window. So, I'm guessing it's trying to open a project, but why is it like a very thin and tall window, which is a bit weird. So, sandbox project sandbox. Let's just open that. And yes, we have Hazel. Wow, it looks terrible. Okay, so yeah, this is Hazel. Um, Hazel 2D. We've got some settings which just show like some kind of this is just our MSDF uh fonts. I guess that was the last thing I added. If we actually go back to the git repo, I think we can see over these commits in the master branch that we uh expose text component to C text components rendering text successfully. So clearly we were doing like text stuff. So let's try and uh add some text for fun. Text component. Hello everyone. So, is this like um how do I move the camera? Oh, okay. Yep. Yeah. So, this is like you can see this is crisp because it'll be it's this library called MSDF gen which uses multi- channelannel sign distance fields to actually generate these glyphs in a way that you can actually see the font outlets over here. So that we actually have a shader that can sort of see how the geometry of the glyph of your font glyph works. And therefore we can render it not just like via rasterization which is what we would do with just using free type rasterizing to a texture at a certain font size and then reading and sampling a texture. And we get to do some math and some more exciting things to actually figure out, you know, the distance from a point to the edge. And anyway, it just means that our text is like way uh sharper and smoother. But also, crucially, it doesn't matter what the font size is. You don't really care about the font size anymore. And you know, I can just take, for example, this empty entity that I just made and if we go to the resize thing, I can resize this to whatever size. And obviously, it's not like I have to regenerate that font atlas or anything. It's just all it's actually got all of the data that describes that glyph. Kind of like a vector graphics thing within the actual font atlas already. And we can just sort of work that out when rendering the pixels that make up each of these characters. Anyway, um, so that's cool. That's like a new feature, I guess, that I added before I just abandoned this whole thing. Uh, why do we have like why does every entity have a sub entity with the same name? That makes me think that this scene hierarchy panel is not rendering correctly. And also, you can't really click on it. So, that's not nice. Um, we got script. Oh, yeah, cuz we've got scripts. So, we got like C support via mono, I think. Oh man. So, we can hit um Oh, this is just physics. So, let's go over here. So, we have this weird little scene and we can hit this and you can see it's got box 2D physics that we see here. And then if I hit play then uh I think can I move this around? No. So, can I reload assembly? How do I Let's try and work out how we build stuff. So, I think sandbox this hasn't even run. has it because I'm supposed to Oh, no, there it is. Sandbox VCX prod. But where's the solution like for sandbox cuz there's just Hazel. So there it made a VCX prod but not a solution file. So that's a little bit weird. Um if I go back here though, I don't think Let's stop this engine for a minute. Um yeah, it wouldn't be in here. We've got Hazel script core, which is fine, which we didn't actually build. Oh, here we go. Could not find script instance for entity. But how does it build? Let's try running again. Maybe it all went wrong because I didn't build that thing. Reload assembly. Okay, that just straight up crashes because I guess we have no assembly to reload. I mean, crashing seems a bit harsh, but I guess yeah, S data app assembly image is probably null and we're just passing that in willy-nilly. This is the wrong S data. Nice. Okay. I mean, the fact that this is S data is also a little bit annoying. I mean, I think I was just trying to be quicker to write all this thing, so I was like, "Sure, this can just all be static. " But I probably wouldn't do that these days. I think basically everything should have a
Segment 4 (15:00 - 20:00)
lifetime. I mean, this is pretty fundamental. This is like almost back to basics, but it's like if you're not being lazy, then you should be very careful about what you make uh static. And a lot of people say that it's not good to make things static like as in you don't want to make singletons or single instances of things because you can't test it as if like you know you're eventually going to pass this through like unit tests and you want to be able to like build it up and tear it down. Realistically unit tests for certain things in game engines can obviously be useful but I would say that game engines typically have a much different sort of testing strategy. So I would say the more realistic reason why this is bad is because you then have to think about shutdown. Like a lot of people like to for example, let's just take this entity classes thing. Like if I just wrote this code like this, if I was just like static, here's my map of entity classes, right? So this might look fine because you're like, okay, well I have one instance of this. I'm only ever going to have one sort of map of where I store all of my script classes. Everything is great. But then these like this is a ref which in this version of Hazel is just a shared pointer. So this is a strong reference to an instance of a class that you are probably going to keep here forever until static shutdown unless you explicitly go through this and clear this on shutdown. So in a way it's a bit funny because you're not really you're trying to automatically manage memory obviously with this reference counting system or in this case shared pointer which I guess is still a reference counting system. You're trying to automatically manage your memory here, but realistically you still rely on having to do like entity classes. probably every time you switch a project, you know, to a different project that's going to have a different script binary or when you shut down the engine because if you don't do it when you're shutting down the engine, then this will be shut down sort of after the main function ends in like a static shutdown region, which may or may not be acceptable because maybe the destructor for this has to do some cleanup that might rely on the script engine system that doesn't exist obviously by the time you reach static shutdown. So there's lots of issues that come out of just doing stuff like this which is effectively global. I mean static confines it to this translation unit to this specific CPP file. So it's a bit better than obviously if you had just done it globally but it still has the same problem of like but when will it be destroyed when will shutdown happen for that. I need to now keep track of that and manage that explicitly. So instead of that, if this was just inside say something else that was also part of your script engine system or something, then obviously that ties the lifetime of those two things together. So if you have everything stemming from like this thing where maybe this is your entire scripting engine and then from here you have like you know your script engine data or you have like some actual script object that you're using that maybe is that specific C# DL with all of its script engine data effectively which would be all the entity classes instances whatever else then if you tear this down it's going to tear down everything else along with it and you don't have to worry about explicitly managing all of these floating pieces that have their own lifetimes and their own memory allocations and possibly their own shutdown code that has to run in a specific order as well. So, for that reason, it would be very rare for me these days to ever do something like this. I think that it's just a it's just a bit of lazy engineering to be completely honest that is probably going to bite you later. Okay, so let's get back to sort of fixing this. Um, we have to figure out a way to build this and I'm not sure why that didn't work because I think setup. py Pi that calls Winen Projects and Winen Projects does run pre-make, but obviously that's just going to run pre-make for this which has Oh, look. It does include sandbox actually. Oh, so what is sandbox in here? Oh, sandbox is in here. Let's build sandbox. I thought because you know in proper Hazel it's a separate solution because of course that would be like your game project versus this being your engine project. You wouldn't really put it into the same solution like this. So, I think that was also just me trying to Oh, wait, wait, wait. Sandbox. That's a different That's a C++ project. Sandbox is sandbox not the C, I guess. What? What even is Sandbox? Well, this just a fun little thing. Okay, hang on. Hazelnut sandbox project. H, that's what I was thinking of. So, we have scripts in here and then here's Winen project. So, we run that. We get our sandbox solution. There we go. And then that's going to be something that we need to build, which obviously is annoying that we can't just programmatically build that from within the engine. We have to sort of find this and build it manually. I think that's a bit bad. But now that we've done that, we should be able to run Hazelnut again. Open this up. And I think if we just hit play now, we should be able to Yeah. So I'm using the WD keys now to actually move this around. Um, and that's kind of fun. And that is obviously coming from
Segment 5 (20:00 - 25:00)
this script over here, probably called player, which looks all gray, I guess, cuz it thinks it's got zero references, but it's actually being explicitly called from the script engine runtime. And then we're doing WD, which is setting the velocity and applying a linear impulse to the rigid body, which is going to go into Box 2D and actually apply that physics impulse. Cool. So, yeah, I mean, it works. Um, is it good? Well, you know, the biggest UX issue was probably the fact that I had to figure out how on earth to build the script instead of it maybe building the script when I open the project automatically. That's probably what should have happened. Anyway, that's probably the least of the problems, but that's just something that uh is wrong with even this simple kind of case. Okay, so we have a content browser with scenes, uh scripts, um textures. This is just showing stuff on disk, I believe. And then so, okay, so textures. So, we don't have any thumbnails. So, if I wanted to go into here and we have a sprite renderer with a texture. Can I just drag this on here? I can. And then we get a checkerboard texture, which is pretty cool. Just directly from the PNG. Um, oh, we have a class here. Okay. So, this is our script class. So, we just manually type in the name here. There's no like drop down or anything. For those of you who don't know, maybe it would have made sense to compare this to proper Hazel just to see the difference, but um I don't think it matters too much. And then, of course, we have speed and time. So speed and time would be these two variables. So the idea is if we add something here like hello and then I build this and then I go back here and then yeah you can see automatically we actually have a variable called hello here now right so you can just use public variables to uh be able to set data from the actual editor and from the entity data I guess that's inside these components into the actual C# script which is pretty cool. Prefabs are probably not a thing at all, I think. So, if I like Yeah, I can't even drag this. I don't think parenting is a thing. You can't really drag this anywhere, which is funny. Yeah. Turns out there's a lot to a game engine and a lot that isn't implemented in here. Uh, I think I can also tint. So, if I go color Yeah. So, you can see it's tinting the texture, which is pretty cool. Can do all the colors. And then, I think, well, we had one more texture here, the Cherno logo. Let's try that. Yeah. So, we got the Cherno logo. Let's make that white. And it looks like it's kind of transparent. Um, I don't know what really determines what's in front of what. I guess it's got a little Z thing. So, if we go like minus Okay. Oh, wait. It's perspective, isn't it? Yeah, this is 3D. So, there we go. Easy. I thought it was orthographic for a minute. Okay, cool. So, anyway, that's the engine. Um, honestly, just based off of this and based off of the 1% of the code that I've looked at in this video, I'm leaning towards just starting a fresh project from scratch. But like again, you know, this has architectural strengths surely, right? Let's have a look at a little bit about how it's actually architected in terms of like how all of the things work together and then maybe we can sort of formulate a plan to take some of that architecture but just you know expand on it and maybe fix some of the issues with it. So Hazelnut app this is the actual there's no runtime is there? I don't think so. Okay. So that's another thing that I would probably set up pretty early if I was to do this again. And the runtime is the actual game that you ship, which obviously is not the editor with all of the imiy like UI stuff in it. It's just the game exe that has that runs the game. So that should probably be established pretty early on. Okay, so we have um create applications is very similar to modern Hazel as well. So create application gets called from the entry point. I would still ah it's doing this really annoying JSON profiling. So I would use Tracy for that instead of this. We do login init. It's using speed log. I imagine it is probably an ancient version. Again, all of this being static. I think this is really annoying as well. I hate it when people do this. So, yes, logging is a system that is overall static. Like, it's overall global. You just have a log system. You don't have multiple log systems. You might have multiple log destinations or systems, I guess, within the bigger log system. But generally, you want to be able to obviously log from anywhere and usually via some macros. None of my shortcuts work in Visual Studio 2026, which is very frustrating. But anyway, so we have these log macros that obviously go to log. This is all fine. What's not fine is the fact that for some reason instead of just having a class called log, which I do have within that log, it's actually got multiple static members. So we have a static core logger and a static client logger. Why? What you do instead is you make this sort of globally accessible by making it static, by making it a singleton, by making well it doesn't really matter what you do with it, but just so that it's globally accessible because there's in fact going to be only one instance of this log class ever. And then since that itself is globally accessible, everything within it can just be normal members. And that again has that sort of automatic construction and destruction behavior. And it means that we don't have to go through and declare. I mean, obviously you can just use inline static now, but you don't have to keep track of all these things and create them and
Segment 6 (25:00 - 30:00)
tear them down explicitly because now they're just part of the class, right? So, they don't have to be separate because by making them separate, it's like you're taking this one thing that you really want to be global, but really you're making all of the details of it separately global, which is weird. So, don't do that. Uh, back to entry points. So, we're going to go through and create an application. This is an X10. The reason this is set up this way, which is fine. And I don't really hate this. And this is how proper Hazel does it as well is we have an entry point. h which actually includes the int main. That's so that you don't have to worry about which main to declare. I really should open up real Hazel. Okay. So real Hazel opened up in Visual Studio 2022 which is refreshing to see. I like this more. Anyway, so entry point. h you can see it has a few different mains, right? Right. So, it has just a normal standard sort of C++ main function, but then also it's got win main because if we're in a disc build, if we're on Windows specifically, we don't want to like spawn a console or anything, we just use winm main. So, obviously it would be weird to sort of include that in every project manually and then have every project, meaning every executable like the runtime or the editor or whatever else projects might be using the core engine to then have to deal with this stuff manually per project. Instead of all that, you simply have this file that does that for you. And all you have to do is inside your app. So for example, our Hazelnut app, you just include entry point and that's it. It just chooses the entry point for you automatically and you're good. You know, as a client using Hazel core to build a application like you don't have to worry about which entry point do I use. So that's kind of the reason for that. Now obviously we still want to configure what happens and what the application spec is for this specific application. So because of that the entry point does in fact have this extern that it calls over here. Now it's an extern and it's set up this way because obviously it's up to the actual application to define that because it's application specific. So this entry point is within the core Hazel module but then obviously like the Hazelnut app is within the Hazel module. So this is an EXE and Hazel is a static library. So basically the rule is yes you don't have to write int main but you do have to write this specific create application definition that you can just copy I guess from entry point here it is here's that declaration up here's the definition you have to provide inside the hazel namespace and then that will allow you to set up your application and then return an actual in this case a subclass from application that then pushes a layer. So that is how that works. Now the layer system is a another thing in and of itself that is probably implemented poorly here via this layer stack which is just yeah that which is a bit you know questionable but anyway that just allows us to configure what it is we want our application to do because again you can use the core library for different applications not just for the editor like also for the runtime and you want them to do different things. So therefore, you implement that different functionality in different layers. And you can have multiple layers on top of each other as well. If you want like a debug layer pushed on top of your regular runtime layer that has some extra debugging things that you don't actually push for distribution builds, then that's also a benefit of the layer system that you can just sort of stack these application layers on top of each other and then have them conditionally potentially run or you know condition compile time conditionally be created if that's what you so choose. Okay, so editor layer is the big file here, which is also very big in proper Hazel that is sort of the implementation of the entire editor. So it's got a bunch of mgoy code. just flow code and honestly this is something that I probably would break up into various different systems cuz this is wild. like you know just right here inside on update for some reason we have this which is you know basically reading back the pixel color from our frame buffer to see which entity we're hovering over because that's how the sort of mouse picking works. We render every single entity's ID into a separate frame buffer uh attachment. So I think one would be the attachment index. If we have a look at the shaders it probably make more sense. So here are the shaders. So yeah render 2D quad for example. You can see in the fragment shader we have here it is two outputs. So one is just the color that's what we're rendering and then we have an outint entity ID and that gets set to v entity ID which is just something that's part of the vertex attributes that we input when we actually render these specific vertices that make up a quad cuz this is all batched together as well. So it's sort of one draw call for probably everything that you saw there inside the actual viewport. But we still need to know which entity is which. So we push this int which I think just maps to if we go to renderer2d. cbp CBP and we look at HT which is editor only as it says apparently. Then yeah when we draw quad we pass that in. So let's have a look at the references for that. Where does that even get called from? So probably from here from draw sprite maybe it'll probably be like scene. cp right. So scene would do draw. Yeah. So draw sprites. So over here when we do draw sprite you can see we pass in the entity ID and it's just int. It's casting entity into an int because this is an
Segment 7 (30:00 - 35:00)
entity which is an actual 32bit sort of handle. But the thing is it's not like a UYU ID. It will change every time potentially you actually run the game. It's not like a universally unique identifier or anything. It's just an ID for that frame. I guess is more or less valid for which entity is which within the ECS data structure. So anyway, the fact that that's just done inside on update is pretty funny and that yeah shouldn't be like that at all. But yeah, look uh in terms of what I like about the architecture layer system, good uh application. Um we haven't really looked at it, but I think that's fine. Like a lot of this is good. actually matches my uh application architecture series which I made somewhat recently. I'll have a link to that playlist in the description below and you can see it on screen here. Honestly matches that. Um you know, I do like this whole idea of layers events as well, the event system. I think that's pretty solid. I would expand a lot of these systems and make them a little bit more um you know ideally not complicated but just a bit more powerful and a bit more robust for larger scale things where you're just going to want more functionality but I think at their core they're good um dare I say it's just early in development it's just not mature yet okay what else do we have here I mean we have quite a lot of stuff don't we've got this like file system which just yeah read file binary I mean sure the thing is like you really depending on what your goals are like you really don't need to over complicate things. I think the whole renderer is probably a little bit weird, right? So we have this render API and we can implement either none or OpenGL. So we have none presumably for headless uh which is not a thing here I think and then OpenGL would then mean that I'm guessing we would have like a platform OpenGL and then OpenGL render API which you can see is a subclass from renderer API and then that has all these. So this is um this is not good because uh it's relying on a very fine grain abstraction. So you really need to think about where like abstracting rendering APIs, graphics APIs like OpenJ, Vulcan, DirectX, whatever, Metal Abstracting all of those um is a very hard thing to do like in general. Abstracting those requires a lot of thought because there are so many different ways you could do it for a small engine like this. I probably wouldn't I would try to just run with one render API, but realistically it's really hard to also make that decision. I mean, the clear winner for that would be Vulcan these days, but you also might want to ship on a platform one day that doesn't run Vulcan. And in that case, it's like, well, you know, here's a scenario. You make a game with your own engine. It's written in Vulcan. It blows everything out of the water. It performs really well. You make millions of dollars. You want to ship on like a PlayStation 5 or something. And then it's like I have to rearchitect my whole engine or I just have to switch to something else entirely. that's going to be super difficult if you haven't thought about that from the beginning. So, I still think you need to consider that you might use more than one API in the future, but you don't have to make it super fine grain. something called an RHI, which is a rendering hardware interface. You don't have to do any of that. You can just sort of be like, you know, render mesh or like draw mesh. That's a function. Let's have a bunch of input for all of your data. You obviously have an asset that is your mesh. You might have like transforms. You might have materials, you'll have all of these things. You just push that into say the OpenGL renderer or the Vulcan renderer or the DirectX renderer and then it takes care of the rest. That's a very like macro way of doing it. A very high like highle thing. It's not fine grain like draw indexed and like set clear color. Like you don't have to be that low level because that's going to be a lot harder and you're of course going to run into issues with the multiple render APIs just having different designs because they do have different designs. The other thing that's super important with um rendering is shaders. Uh the shader system uh you know can be as if not more complex than the rest of the renderer because especially when you start getting into different render APIs because they all want to use different shader systems. Like I believe what is it? Shader model 7 for direct 3D is finally going to support spurv shaders. So that will actually help a lot. It's not going to need like DXIL or anything, but as it stands at the moment, like you still need to think about uh what language do I write my shaders in? What do I compile it into? How do I effectively build a pipeline where I just write my shaders in one language and have that work with all of my supported rendering APIs? Because that's still something that ends up being quite a complex system. And uh obviously there's no consideration given for that here, but it kind of almost worked since we I think yeah, so we ended up using spurvy cross and shader seed. That's exactly why the vulcan SDK is involved here even though we are using OpenGL. And then um you know since we're still going through this and we're doing some pre-processing here and then doing compile get vulcan binaries which is
Segment 8 (35:00 - 40:00)
basically going to use shader C here to uh compile Vulcan 1. 2 it says version of spurv and then we have the shader data which we also do reflection on. And that's useful because we can get stuff like the name of all of the uniform buffers. You can see we're printing all that there. And that's useful because we can build systems where we just set inputs to shaders and to pipelines just by their names if we want that. And so when we actually create the shader, so I don't know gel create shader create program. Here it is. What do we do with that? Let's have a look. Apparently nothing. Oh, here it is. Okay. Yeah. So you can see we do glader binary with format spurv and then there's that spurv data that's going into there. And then we attach all those stages into a program. Cool. So that's kind of cool. It's like more of a modern OpenGL thing where you're using spurv for shaders. Uh vertex array and all of these things are very open gel specific. So they'd need to get rewritten if we were using vulcan or some other rendering API. And then if we just flow through the rendering real quick. So obviously scene is yeah we're using entity for the entity component system which is good. I would probably use that again. On update runtime uh there'll probably be a be an on render. Actually it's happening here inside on updates. Sure. So on update runtime we'll go through all of the uh so with the camera components it's just pulling out data for that. So we have a main camera and then from there if we do have an active camera we do begin scene. This is during I guess the runtime. Yeah on update runtime. So that basically means when we hit that play button to actually play the scene it's running through this code. So we do begin scene which probably sets up some like uniform buffers as you can see and starts a batch of 2D rendering and then we go through do draw sprite which is just yeah just going to go through and populate our vertex buffer with all of our vertices that make up that specific quad that we are trying to draw. And because it's 2D rendering basically everything goes through this. Um, I mean I think especially in this kind of 2D renderer that we have here in Hazel, literally everything is a quad. Like whether it be text, whether it be mostly transparent sprites that are like circles. There are actually ways to draw circles, I believe. See if I can collapse everything here. So, uh, yeah. So, there's draw circle, right, which, um, goes into a separate circle vertex buffer, and then I'm guessing an end scene is where it will go through and do flush. And then flush will for each yeah for each type of geometry because they all have different pipelines different shaders is actually going to go through bind them and um draw that specifically. So even text shaders which are going to be quads per character it's still you know binds the text shader does that and goes into a separate batch for just the text stuff. That's what you see here. The text vertex array and then with circles it's going to bind the circle shader and draw the stuff that goes into the circle uh vertex array. And I think that was also Yeah. Here how it's still um seemingly Oh, okay. So, it doesn't actually draw like a circle out of lines like in triangles like a triangle fan or whatever. It's actually still drawing a quad. It's just using a special shader which will be the uh circle shader. Um and then yeah, that's just doing some shader kind of stuff from uh within the quad. So, you have like this quad that you're drawing in. And then basically what this will do is just go through and actually draw your circle. Um, and I think there's an appropriate like fade or something is there cuz there's a smooth step. So I think if we look at the um, this is funny. Yeah. So we've got one here. That's pretty cool actually. So we can do um, this is a circle obviously. So you can see we have a circle renderer. That's what it is. Then we have a thickness. So we can control the thickness. And then we have a fade. So you can see you can actually kind of make it all fade and stuff. Um, but it's still drawing it as a chord. And you can see that um over here obviously cuz the selection for that I don't even know how it's doing the selection. It's probably just a wireframe. But you can see the selection is a quad. It's a very disgusting wireframe though cuz look this is like overlapping and weird. Yeah. Weird. Okay. Yeah. And then that's like the 2D rendering. So yeah, I would um probably write this differently. I think it's fine for basic 2D rendering, but 2D rendering is one of those things that's like deceptively simple. like it's very simple if you just want to get a nice visual result and you want to have it working but there's a lot you can do to optimize it. Um so that's certainly something we could talk about especially if AI is sort of doing a lot of the implementation stuff and we're just supervising it then we can probably actually work on some pretty cool stuff in a very uh short time period which is yeah one of the benefits of I guess using AI again as long as you're you know keeping a very close watch of what it's actually doing and making sure that it's doing the right thing. Cool. Uh what else is there to look at? I think that's I think I'm happy to just end the video here to be quite honest. Yeah, of course we're using MUI for UI rendering. I think um you know we have a layer here for MGUI. It's another example of what you can use layers for. And then yeah, this is just again it's you like it's we don't even have our own renderer like our renderer is just for imgey is not
Segment 9 (40:00 - 43:00)
even implemented inside Hazel. It's actually inside this example file. So that's also a bit different from Big Hazel which has its own MGUI renderer using NV RHI now. But yeah, this uh this is pretty basic and I think I would still possibly start building an engine from this because as soon as you have some UI and the ability to use immediate mode UIs within your programming, you can it's just easier to sort of test things and visualize things and make sure that it's you know producing what you want rather than just starting with like the renderer and then you know not knowing why certain things aren't working and not being able to control data as much cuz obviously the whole point of a user interface is to manipulate data and that's very helpful when you're building something. That being said, you obviously don't need to start with a renderer. Um, I just like starting with a renderer cuz I think it's nice to have a visual result. And also, as I mentioned, even if you're working on some system that is not renderer related, having that UI and being able to control it and see data in a more meaningful way than just log messages in your console, I think is a better way in general of uh developing software, which is why I would start with the render and imui and UI just to actually have that components. h page all of our components here uh destructs of data that of course go into the entity component system and then scene yeah scene serializer and everything uh so on with a scene on update runtime is what we looked at on update editor is what we do it's basically doing the same thing it's just not actually going through um it's mostly drawing I guess it's not um actually running the scripts or doing anything that on update runtime did like it's not simulating physics or so physics is being simulated update in on update runtime. I mean this is just what is this on update runtime. Yeah, physics world step. There it is. That's what I was looking for. And then the script components. This is a native script component though. There's the normal one. So it's running the C# scripts by calling the on update function. So invoke on update calls the on update method which is defined over here. Get method on update and that's how we actually call into script classes. So this is also written using mono which is not something I would use. I would actually use big Hazel's coral um which Peter wrote. So that's definitely a lot better than mono and uh compatible with like. NET 10 and everything. So I would just use that. Okay, cool. Um that's it. Hope you guys enjoyed this little code review of the game engine series version of Hazel. Definitely brings back some memories. Um also as I mentioned, like I think there's a lot of things here that are perfectly fine. uh it's just needs to I mean obviously some things like the renderer and some other things I would definitely change but in general the actual core architecture I mostly agree with and it does in fact follow my recent C++ application architecture series mostly. So personally I think it would be really fun to take something like this but then make it from scratch with the help of AI to make it much faster and then sort of make like a game engine 2026 series where we try and build something like that. But of course, we're still trying to actually build a good quality product and not AI slop. So ultimately, if that's something that you want to see, definitely let me know in the comments section below. And I'll see you guys later. Goodbye.
Ctrl+V
Экстракт Знаний в Telegram
Экстракты и дистилляты из лучших YouTube-каналов — сразу после публикации.