What do you guys think - small or large projects? 👇 Also don’t forget you can try everything Brilliant has to offer-free-for a full 30 days, visit brilliant.org/TheCherno . You’ll also get 20% off an annual premium subscription.
More small please, easier to see 'the whole'. Would love to see a simple Breakout game, start off very simple, add powerup or three, everything explained.
@@eduameli What's pbrt? Sounds like “peanut butter and ray tracing”, lol. [edit] Nevermind, found the website. Still, it's one thing to read stuff, completely another to have it explained out loud by someone who already learned the ropes and knows all the “gotchas” - especially this guy, because he goes all the way down to the memory content.
@@cprn. true, I also followed his series but, at this point its unlikely the series will get finished anytime soon. pbrt, ray tracing in one weekend series, and the raytacing gems is probably good enough to learn a lot of raytacing. i haven't gone through it myself, although I plan to, but it's probably also better since you won't have a video tutorial for stuff once you get to a certain point
@@cprn. I would watch out for that view point, it's the easiest way to fall into tutorial hell. To get good at programming you also need to learn how to turn an idea you understand into code without someone walking you through it in code.
I think you asked us about 15x already if we wanted you to properly refactor something the way you would code it. And the answer was always yes, from what i could tell in the comments of those videos. :D
Thank you so much, Cherno, for reviewing my code! I learned a lot from your feedback. By the way, I've started using this-> when calling member functions because I find it makes the code more expressive and helps distinguish member methods from other functions at a glance. 🏆
It is best practice to specify exactly one of virtual, override, or final on a virtual function. Declaring a function virtual and override removes the error detection provided by override alone. If you accidentally get the signature of the virtual function wrong, having override only will generate an error to say that there is no virtual function with that signature to override. Having both virtual and override will suppress that error.
Making the main a separate project is common in the industry for enterprise code. The unit tests are linked to this static library with its own main. I have noticed that your world is much different to the world of embedded or enterprise, so don't be to quick to judge some conventions. With lto it shouldn't really matter anyway.
Warning to your convention of making destructors of children virtual: bad practice. Mark them override if anything. Same problem as marking the implementation of a virtual function virtual instead of override (or final). The base MUST contain a virtual counterpart or you want a compile error. Don't hide those by marking them virtual.
Using high resolution clock is also dangerous. It's an alias to system clock, which can change if the ntp client changes its mind, which can have weird results. Use steady clock!!!!!!
You should look up what static constexpr does in a function body. It's different from static const and may be different from constexpr by itself. I would also write static constexpr.
Thoughts on specifying "this->" for class members & methods? I always use it because I don't see a disadvantage, it is much easier to read and we certainly don't need to save the screen space any more.
11:05 you can actually just inline `LoadSound(somePathVar.string().c_str())` because the std::string object (which is a temporary) is destroyed after the LoadSound is done, so the pointer stays valid.
I was going to say this as well. The temporary created has its lifetime extended to the full expression (you can bind to a const lvalue reference or to another rvalue reference to extend its lifetime further).
@@דניאלאביב-ו6ת "All temporary objects are destroyed as the last step in evaluating the full-expression that (lexically) contains the point where they were created, and if multiple temporary objects were created, they are destroyed in the order opposite to the order of creation. This is true even if that evaluation ends in throwing an exception." From cppreference "Lifetime" under "Temporary object lifetime"
The main source would be section [class.temporary] of the C++ standard. You can also find it under "Temporary object lifetime" on the "Lifetime" page of cppreference: "All temporary objects are destroyed as the last step in evaluating the full-expression that (lexically) contains the point where they were created", with "full-expression" basically meaning until the semicolon.
1:20 I guess this guy could just make some other types of projects than games during uni. I mean for example writing own implementation of data structures, simple data processing apps, embedded systems etc.
23:20 For that kind of thing, I'd probably take the number of tiles in the screen minus the length of the snake, then generate a random number between 0 and the calculated number (let's call that 'pick'.) Then, I'd place the apple on the 'pick'th tile on the screen, skipping over the area occupied by the snake.
Sounds good on paper, but the magic is hidden in the last part: "skipping over the area occupied by the snake". The snake can lots of different shapes and then you need to loop over all tiles checking each one until you reach the index. I think this random functions is a lot faster then this. At the limit sure, if there are only a few empty tiles left it would be better, but them, don't optimize for rare occurrences.
@@LetsDark You mean collision detection? That part is there any ways, you can't really help it. I mean, you could optimise for certain shapes, and then work on index alone, e.g. when you know the snake currently occupies all indexes from 5 to 8, you can add 3 to your random number if it's >5 &&
for pro snake players apple in the end will almost always land in snake in case of rng, and the game will lag (maybe not, but still unpleasant to think about it).
14:10 Actually you should NOT use inline here. Recently I have done similar things with having paths as global variables and declaring them as inline within a header file. What I had: inline const auto g_kPathAssets = Path("Engine") / "Assets"; inline const auto g_kPathShaders = g_kPathAssets / "Shaders"; I have discovered a weird issue with the 'g_kPathShaders' sometimes being "Engine/Assets/Shaders" (correct path) but sometimes being just "Shaders". What I think is the issue here is that although the order of the initialization of global variables within one translation unit is defined (from top to bottom), inline implies that the linker is allowed to just choose one of the 'g_kPathAssets' definitions of any file. The problem right here is (I suspect) that the linker within a file including the header with the 2 lines above sees 'g_kPathAssets' as initialized (but within another translation unit which actually hasn't been worked on yet) and initializes 'g_kPathShaders' with the actual uninitialized 'g_kPathAssets' which results in the just "Shaders" path for 'g_kPathShaders'. I know my explanation is a bit weird but I am also not sure that this is correct. This is just what I suspect ATM. Please feel free to comment on this if you know better :)
Inline variables seem like a big footgun to me. I didn't even know you could do that but apparently it was added in C++ 17. I always just do the declaration in the header file like "extern int MyGlobal" and then stick the definition "int MyGlobal = 5" in a .cpp file.
32:46 About “compact code”: The brain has two main modes: one for reading/understanding, another for writing/creating. Those are the very same modes that process e.g. negations. You may have heard this example before: don't think about elephants! Did you get an elephant in your head? This happens to all neurotypical people; either you get an image or your inner narrator repeats the word. It occurs because your “understanding mode” doesn't process prohibitions or words like emptiness, infinity, etc. These are negative terms - “things that aren't”. They need to be translated into “things that are” by your “creative mode” before you can understand the meaning. As a result, you're engaging both processes. When reading code, you want to see related things grouped together, so your brain doesn't have to switch modes back and forth. However, when writing code, you compact multiple actions together because your brain is in “creating mode” constantly and tries to optimise for the least amount of code possible. The tighter it is, the stronger the impression that you're doing more with less, resulting in a higher reward (serotonin level). Normally, you'd fix that as soon as you start reading the lines you just wrote, but some people tend to leave them as is. I personally use a formatter, so I never have to worry about it at all.
Not sure I'm adding much. But I never compact my code that much when wirting it, in the sense of saving lines like in this video. I guess I even add too many functions sometimes, where I am not sure in the end if it really helps with understanding the code :)
@@tubeincompetence It's a thing that becomes second nature with practice (especially in a professional environment where other people review your code), but also, maybe you aren't as neurotypical as you thought.
What's the major part then? Math I guess. It is different here in the US, I only had to take up to CALC 1 for my CS degree, but it might be different in different universities, I know some require up to CALC 3 and Linear Algebra and maybe something else. Anyway, I don't see how much it helps you to become a better programmer, except that you can brag about your math knowledge I guess.
1:18 CS studies 20 years ago were a joke, man. Forget about coding a game - the closest we got was rendering a strip of triangles in OpenGL. We got to code tons of unrelated examples like that, e.g. multithreading in Java, inheritance in C++, some data structures in Pascal, a scaffolding example in a PHP framework. In theory, we got to build one fully-working expert system, but it was a boilerplate / template and TBH I still don't fully understand how it worked or what was the point - even that was just a small one file program. The only time you'd have to do a complete project was for the thesis during the 7th semester. I didn't get to it, though, because I found work in my 5th semester and had to drop out due to conflicting hours. I've been coding professionally ever since and still didn't make a single game, not even a slice.
@34:43 'Range-based for statements with initializer' is a C++20 feature. I like it. You also have if statements with initializer since C++17. I like that too. It tightens the scope of variables and is easier to read. Maybe a counter like size_t idx = 0; isn't the greatest example of this but it is quite nice anyway.
32:40 I think using move like this is like the opposite of impressive, since it's showing a lack of understanding of move semantics. Using std::move on a const variable is not going to move anything. Remember that std::move is basically just a cast to an xvalue expression, but you're not casting away the const part! So you get a const T&& from the std::move call, but the move constructor is T&&. That is, it requires T to be non-const since the move constructor will modify the variable when moving it. Using std::move here just ends up using the copy constructor instead. You didn't point it out here, but your IDE is calling this out.
@@MirrorsEdgeGamer01 Good point, yes you can indeed enable it through a Clang Tidy flag. I didn't see that in this project & Cherno's error was C26478, so I thought that was some Visual Studio thing in this particular case
I personally don't mind one line ifs when they are acting as guardians (i.e. return out of the function when a condition is not met). I also don't mind one line functions and dense code. However, I never code like this as it annoys so many people, its always wiser imo to write for the most common reader especially if you want some buy in for what you are making.
Yes please do make a video showing how you would structure a game, I don't program in C++ but your videos are always interest and often contain good practices that can be transferred to other programming languages.
18:27 I guess this is because in many not c based langs like rust for example you have to explicitly mark e.g. "self.field" to get acces to a member field or function.
34:17 yes the static is useless cause it is a compiler value that will be inline and is static and or not even used as a variable. The compiler choose what todo with best optimizations and pre compiling/calculating
The weird style is definitely a side effect from university. I was taught to code like that as well then later I realized whitespace is very useful for skimming the control flow
I honestly would prefer the more densely packed code with the K&C parenthesis style if it wasn't for the inline if statements. Those kind of make the code unreadable. In my opinion empty newlines in code blocks should be reserved to underline a "change in task" of the block of code or to just separate two logic blocks and should not be put after each statement as they just make less code fit in the screen and have no active purpose.
I don't like using newlines inside my functions at all since I like to use the `{` and `}` vim motions to jump from function to function. Obviously this style is just for my personal projects and not at work lmao. But densely packed code looks nicer in general to me too
30:40 I would make a enum wrapped into a struct (instead of enum class) and then just used a static array. So, it's possible to index the array by enum values directly
13:00 I would have just written a separate Load function that hides these differences by taking a path, easy peasy. Drawable was perfectly unnecessary, yeah. And short views into large projects is my vote.
Secret option 3, use a package manager like Conan or vcpkg. Also, i explicitly use the this pointer for clarity. Especially if there's a bunch of free functions or local vars. Not needed, but is more clear as to what belongs to the class.
If you think this was a case of "wall of code", you'd hate what I have to read at work all the time. Condensed else-branches (ie. "} else {"), no empty lines between classes or functions, but sometimes just randomly before closing braces. It's like they are actively trying to make it hard to read to discourage the reviewer from looking too closely. I tend to use one empty line within functions (after logical blocks, etc.) and two between functions. I just like when you can scroll through and see the structure at a glance.
Completely off topic but I have wondered for a long time it would be fun to see Cherno build this app as a mobile app, either using React Native or in MAUI :D
During my exchange semester in Canada there was a glitch with my course subscription and I was forced to study the courses nobody wanted : graph theory and NP-completeness... instead of learning about games.
I'm finishing my degree in CS and you have to take a game programming class to make a game... most classes are just theory or simple programs.. I took an Ai class where we were given a sokobon game and had to use A* to beat the game.
I fully concur with the dislike of the "wall of code", and if I'm trying to read such an abomination, I always have to fix the formatting too. I personally always use Allman over the K&R style, though I would NEVER avoid contributing to a project that renders itself unreadable by enforcing K&R. Never. Nope! Not me. Uh uh.
I imagine people using this-> is because otherwise its really unclear reading the code what is a method vs what is a function call, especially coming from Rust, Go or Python. That's my thinking when I look at this code anyway. Code conventions are so all over the place, as you often mention, that its hard to rely on them consistently?
I absolutely hate managing dependencies. I would rather have them as git submodules because i know where they are. i can't understand how people can live with cluttered c drive and unfortunately all programs including package managers natively shove their guts in these obscure directories of program files, program files x86, appdata etc.
I found Git submodules to be useful in case of internal packages usage (company scope) - in case of 3rd party dependencies I recently tried vcpkg in manifest mode, mostly for purpose of SBOM and other legal stuff. If you you have something better, I'm all ears :)
I recently switched to using submodules, because Spectrum in my area really sucks and keeps going down... So without an internet connection, cmake can't configure because it can't fetch the dependency. Submodule also keeps the build folder smaller, especially in projects that have multiple targets (also something I do, Linux, Windows, Android and hopefully soon, multiple CPU Android targets).
I'm an elder statesman, so forgive the antique...but are people still reading "The Pragmatic Programmer"? It is the first of several books I REQUIRE my mentees to read.
there's no reason to make an l-value for the LoadSound() calls. the lifetime of the temporary returned by string() is extended beyond the LoadSound call. completely unnecessary. also the ifdef should check for the size of the character type - that's what really matters here - not some magic compiler definition. although, the elephant in the room is why the fuck does raylib use char* paths on windows?
Computer science has rather little actual programming. And even then when learning programming Games are not a priority - if you are lucky you get to do 2- things related to games. And why would they? While that is fun it really doesnt help much with learning how computers work or how to create and utilise them. It is after all computer science and not game-development. virtual - nearly all sane guidelines will tell you to NOT mark function as virtual and override as that just muddies the what. Is it just a virtual function? is it an override of a virtual function? why mark it as virtual when override already tells you exactly what it is? and it also messes up tooling that would otherwise help detect problems (even just the compiler warnings ). why the "application" class? What purpose would it serve outside of adding more noise, more overhead, more files, more to keep in mind?
9:34 checking for _MSC_VER actually checks for if we are on any native windows platform as clang and clang-cl will also define it on windows (unless you are in an msys environment but I don't consider that native). This is done for msvc compatability.
"_body.emplace_back(std::move(newHeadPostiion))" WTF is this monstrosity? 1) you can't 'move' a const l-value, that doesn't make sense. 2) passing a value (l or r) to emplace_back is just going to call the copy-constructor. you should either push_back an (non-const) r-value, or emplace_back constructor arguments.
You are confusing global variables with class static variables. The Cherno is absolutely right in his explanation here. To test it yourself, make a header file that contains a static array of 1 million bytes. Include it in one file and compile. Your exe will be around 1MB. Next include it in 2 files. When you compile again, your exe will be 2 MB.
TH-cam is testing automatically generated audio tracks and somethings it get's enabled by default. You can switch it back to the original in the settings for the video.
วันที่ผ่านมา
Anyone knows why I see the title of this video in French (my language) ? I don’t want to .. :(
same (and YT chose the corresponding audio track too, which was even more jarring)... anyway, click on your avatar (top right on my browser, just right of the notifications bell) and choose another language (English for example)
วันที่ผ่านมา
@@sergiosarabando9211 That will change all my TH-cam to english though. Thanks anyway :) I'll just unsuscribe for now.
automation is alien for beginners, dont even talk about cmake or git sub modules. manual install by hand. dont automate before you know what you are automating. walk before running or you will fall. if your focus is in the tools, you are not programming, you are doing admin maintenance. the more you do actual programming, the better. the more you do all sort of maintenance stuff, the worse. actual value hmh. I hate vulkan (tm). even opengl raster is a ton better. opengl is much better than even raylib. basic opengl yep. tools cant be the focus. optimizations especially in code layout later. dont optimize code layout before you have stable code base otherwise, you dont know what the code layout will be until you have all the functionality. your goal is to solve/make a solution, not the underlying systems. priorities. if the systems take over, the project is over. school induces bad weird practices to forcibly push into code (tm)(r).
I tuned out the 10th time you said to let you know in the comments below, obviously your intentions are elsewhere. Here is the comment you so desperately solicited. never again though.
What do you guys think - small or large projects? 👇
Also don’t forget you can try everything Brilliant has to offer-free-for a full 30 days, visit brilliant.org/TheCherno . You’ll also get 20% off an annual premium subscription.
Short because I can't keep up with your previous content. 😂There is so much.
More small please, easier to see 'the whole'. Would love to see a simple Breakout game, start off very simple, add powerup or three, everything explained.
small
very small
snake game would be great kick off. how to build the structure maybe some UML part of how to think out of the box.
Anyone remember when he promised great things for the ray tracing series? 😢 Really hope to see it continue some day ❤
As do I. I've just finished watching it again.
just read pbrt
@@eduameli What's pbrt? Sounds like “peanut butter and ray tracing”, lol.
[edit] Nevermind, found the website. Still, it's one thing to read stuff, completely another to have it explained out loud by someone who already learned the ropes and knows all the “gotchas” - especially this guy, because he goes all the way down to the memory content.
@@cprn. true, I also followed his series but, at this point its unlikely the series will get finished anytime soon. pbrt, ray tracing in one weekend series, and the raytacing gems is probably good enough to learn a lot of raytacing. i haven't gone through it myself, although I plan to, but it's probably also better since you won't have a video tutorial for stuff once you get to a certain point
@@cprn. I would watch out for that view point, it's the easiest way to fall into tutorial hell. To get good at programming you also need to learn how to turn an idea you understand into code without someone walking you through it in code.
I think you asked us about 15x already if we wanted you to properly refactor something the way you would code it. And the answer was always yes, from what i could tell in the comments of those videos. :D
He's asking for comment engagement, not for opinions :)
Thank you so much, Cherno, for reviewing my code! I learned a lot from your feedback. By the way, I've started using this-> when calling member functions because I find it makes the code more expressive and helps distinguish member methods from other functions at a glance.
🏆
+1 the same opinion with "this->"
מלך
It is best practice to specify exactly one of virtual, override, or final on a virtual function. Declaring a function virtual and override removes the error detection provided by override alone. If you accidentally get the signature of the virtual function wrong, having override only will generate an error to say that there is no virtual function with that signature to override. Having both virtual and override will suppress that error.
Making the main a separate project is common in the industry for enterprise code. The unit tests are linked to this static library with its own main.
I have noticed that your world is much different to the world of embedded or enterprise, so don't be to quick to judge some conventions.
With lto it shouldn't really matter anyway.
Warning to your convention of making destructors of children virtual: bad practice. Mark them override if anything. Same problem as marking the implementation of a virtual function virtual instead of override (or final). The base MUST contain a virtual counterpart or you want a compile error. Don't hide those by marking them virtual.
Using high resolution clock is also dangerous. It's an alias to system clock, which can change if the ntp client changes its mind, which can have weird results.
Use steady clock!!!!!!
You should look up what static constexpr does in a function body. It's different from static const and may be different from constexpr by itself.
I would also write static constexpr.
Great advices here, for the record in the games industry it's still good practice to separate the game as a library for the same reasons you mentioned
Thoughts on specifying "this->" for class members & methods? I always use it because I don't see a disadvantage, it is much easier to read and we certainly don't need to save the screen space any more.
11:05 you can actually just inline `LoadSound(somePathVar.string().c_str())` because the std::string object (which is a temporary) is destroyed after the LoadSound is done, so the pointer stays valid.
I was going to say this as well. The temporary created has its lifetime extended to the full expression (you can bind to a const lvalue reference or to another rvalue reference to extend its lifetime further).
@SadsaGamer I just checked that in godbolt, saw the assembly code version and you are actually right
Do you have a source for this, how do you know that the lifetime of the temporary variable somePathVar.string() is extended into the function call?
@@דניאלאביב-ו6ת
"All temporary objects are destroyed as the last step in evaluating the full-expression that (lexically) contains the point where they were created, and if multiple temporary objects were created, they are destroyed in the order opposite to the order of creation. This is true even if that evaluation ends in throwing an exception." From cppreference "Lifetime" under "Temporary object lifetime"
The main source would be section [class.temporary] of the C++ standard. You can also find it under "Temporary object lifetime" on the "Lifetime" page of cppreference: "All temporary objects are destroyed as the last step in evaluating the full-expression that (lexically) contains the point where they were created", with "full-expression" basically meaning until the semicolon.
1:20 I guess this guy could just make some other types of projects than games during uni. I mean for example writing own implementation of data structures, simple data processing apps, embedded systems etc.
23:20 For that kind of thing, I'd probably take the number of tiles in the screen minus the length of the snake, then generate a random number between 0 and the calculated number (let's call that 'pick'.) Then, I'd place the apple on the 'pick'th tile on the screen, skipping over the area occupied by the snake.
Sounds good on paper, but the magic is hidden in the last part: "skipping over the area occupied by the snake". The snake can lots of different shapes and then you need to loop over all tiles checking each one until you reach the index.
I think this random functions is a lot faster then this. At the limit sure, if there are only a few empty tiles left it would be better, but them, don't optimize for rare occurrences.
@@LetsDark Yes and no. “Skipping over the snake” can be as simple as `i++`. The idea is to call RNG once, instead of every time it lands on a snake.
@@cprn. I know, but there is more than i++ you also need the if(snake) which is probably a heap memory lookup.
@@LetsDark You mean collision detection? That part is there any ways, you can't really help it. I mean, you could optimise for certain shapes, and then work on index alone, e.g. when you know the snake currently occupies all indexes from 5 to 8, you can add 3 to your random number if it's >5 &&
for pro snake players apple in the end will almost always land in snake in case of rng, and the game will lag (maybe not, but still unpleasant to think about it).
14:10 Actually you should NOT use inline here. Recently I have done similar things with having paths as global variables and declaring them as inline within a header file.
What I had:
inline const auto g_kPathAssets = Path("Engine") / "Assets";
inline const auto g_kPathShaders = g_kPathAssets / "Shaders";
I have discovered a weird issue with the 'g_kPathShaders' sometimes being "Engine/Assets/Shaders" (correct path) but sometimes being just "Shaders". What I think is the issue here is that although the order of the initialization of global variables within one translation unit is defined (from top to bottom), inline implies that the linker is allowed to just choose one of the 'g_kPathAssets' definitions of any file. The problem right here is (I suspect) that the linker within a file including the header with the 2 lines above sees 'g_kPathAssets' as initialized (but within another translation unit which actually hasn't been worked on yet) and initializes 'g_kPathShaders' with the actual uninitialized 'g_kPathAssets' which results in the just "Shaders" path for 'g_kPathShaders'.
I know my explanation is a bit weird but I am also not sure that this is correct. This is just what I suspect ATM. Please feel free to comment on this if you know better :)
Inline variables seem like a big footgun to me. I didn't even know you could do that but apparently it was added in C++ 17. I always just do the declaration in the header file like "extern int MyGlobal" and then stick the definition "int MyGlobal = 5" in a .cpp file.
32:46 About “compact code”:
The brain has two main modes: one for reading/understanding, another for writing/creating. Those are the very same modes that process e.g. negations. You may have heard this example before: don't think about elephants! Did you get an elephant in your head? This happens to all neurotypical people; either you get an image or your inner narrator repeats the word. It occurs because your “understanding mode” doesn't process prohibitions or words like emptiness, infinity, etc. These are negative terms - “things that aren't”. They need to be translated into “things that are” by your “creative mode” before you can understand the meaning. As a result, you're engaging both processes. When reading code, you want to see related things grouped together, so your brain doesn't have to switch modes back and forth. However, when writing code, you compact multiple actions together because your brain is in “creating mode” constantly and tries to optimise for the least amount of code possible. The tighter it is, the stronger the impression that you're doing more with less, resulting in a higher reward (serotonin level). Normally, you'd fix that as soon as you start reading the lines you just wrote, but some people tend to leave them as is. I personally use a formatter, so I never have to worry about it at all.
Not sure I'm adding much. But I never compact my code that much when wirting it, in the sense of saving lines like in this video. I guess I even add too many functions sometimes, where I am not sure in the end if it really helps with understanding the code :)
@@tubeincompetence It's a thing that becomes second nature with practice (especially in a professional environment where other people review your code), but also, maybe you aren't as neurotypical as you thought.
Engine architecture video sounds great!
Depeding on your university, coding is just a small fraction of studing computer sinece, at leasting here in Dresden (Germany ).
What's the major part then? Math I guess. It is different here in the US, I only had to take up to CALC 1 for my CS degree, but it might be different in different universities, I know some require up to CALC 3 and Linear Algebra and maybe something else. Anyway, I don't see how much it helps you to become a better programmer, except that you can brag about your math knowledge I guess.
@@MacusAvrlots of matrices and transformation in programming games or simulations software so that math is useful.
@@MacusAvrtheory, lots of theory. Operating System, Comouter Architecture, Networking, Algortihms.
1:18 CS studies 20 years ago were a joke, man. Forget about coding a game - the closest we got was rendering a strip of triangles in OpenGL. We got to code tons of unrelated examples like that, e.g. multithreading in Java, inheritance in C++, some data structures in Pascal, a scaffolding example in a PHP framework. In theory, we got to build one fully-working expert system, but it was a boilerplate / template and TBH I still don't fully understand how it worked or what was the point - even that was just a small one file program. The only time you'd have to do a complete project was for the thesis during the 7th semester. I didn't get to it, though, because I found work in my 5th semester and had to drop out due to conflicting hours. I've been coding professionally ever since and still didn't make a single game, not even a slice.
@34:43 'Range-based for statements with initializer' is a C++20 feature. I like it. You also have if statements with initializer since C++17. I like that too. It tightens the scope of variables and is easier to read. Maybe a counter like size_t idx = 0; isn't the greatest example of this but it is quite nice anyway.
20:30 A video on how to build a resource manager would be great
32:40
I think using move like this is like the opposite of impressive, since it's showing a lack of understanding of move semantics.
Using std::move on a const variable is not going to move anything. Remember that std::move is basically just a cast to an xvalue expression, but you're not casting away the const part! So you get a const T&& from the std::move call, but the move constructor is T&&. That is, it requires T to be non-const since the move constructor will modify the variable when moving it. Using std::move here just ends up using the copy constructor instead.
You didn't point it out here, but your IDE is calling this out.
I was just going to say this. I think this is due to Clang Tidy, not the IDE it self.
@@MirrorsEdgeGamer01 Good point, yes you can indeed enable it through a Clang Tidy flag. I didn't see that in this project & Cherno's error was C26478, so I thought that was some Visual Studio thing in this particular case
I use it in Rider. I think these kind of warnings are enabled by default.
I personally don't mind one line ifs when they are acting as guardians (i.e. return out of the function when a condition is not met).
I also don't mind one line functions and dense code.
However, I never code like this as it annoys so many people, its always wiser imo to write for the most common reader especially if you want some buy in for what you are making.
Yes please do make a video showing how you would structure a game, I don't program in C++ but your videos are always interest and often contain good practices that can be transferred to other programming languages.
18:27 I guess this is because in many not c based langs like rust for example you have to explicitly mark e.g. "self.field" to get acces to a member field or function.
Well in some countries like Ghana, they will never teach games just lectures and notes
With the advent of C++20 modules (several years ago now), maybe let's not refer to projects as "moduels", to avoid confusion...
Ray tracing series please
I actually would like to see how you would make a non game engine application class.
34:17 yes the static is useless cause it is a compiler value that will be inline and is static and or not even used as a variable. The compiler choose what todo with best optimizations and pre compiling/calculating
The weird style is definitely a side effect from university. I was taught to code like that as well then later I realized whitespace is very useful for skimming the control flow
I honestly would prefer the more densely packed code with the K&C parenthesis style if it wasn't for the inline if statements. Those kind of make the code unreadable. In my opinion empty newlines in code blocks should be reserved to underline a "change in task" of the block of code or to just separate two logic blocks and should not be put after each statement as they just make less code fit in the screen and have no active purpose.
I don't like using newlines inside my functions at all since I like to use the `{` and `}` vim motions to jump from function to function. Obviously this style is just for my personal projects and not at work lmao. But densely packed code looks nicer in general to me too
C.128: Virtual functions should specify exactly one of virtual, override, or final
30:40 I would make a enum wrapped into a struct (instead of enum class) and then just used a static array. So, it's possible to index the array by enum values directly
13:00 I would have just written a separate Load function that hides these differences by taking a path, easy peasy.
Drawable was perfectly unnecessary, yeah.
And short views into large projects is my vote.
5:00 I would rather have no CMake at all 😉😂
8:18 It can't be a Raylib thing. Raylib is in C. And it has a simple straightforward design.
Secret option 3, use a package manager like Conan or vcpkg.
Also, i explicitly use the this pointer for clarity. Especially if there's a bunch of free functions or local vars. Not needed, but is more clear as to what belongs to the class.
It would be cool to have more shorter format focusing on really specific tips and concepts
Nice video as always
Also I would love to see an actual structure example video like you mentioend around 20:40
If you think this was a case of "wall of code", you'd hate what I have to read at work all the time. Condensed else-branches (ie. "} else {"), no empty lines between classes or functions, but sometimes just randomly before closing braces. It's like they are actively trying to make it hard to read to discourage the reviewer from looking too closely.
I tend to use one empty line within functions (after logical blocks, etc.) and two between functions. I just like when you can scroll through and see the structure at a glance.
git submodule!
Completely off topic but I have wondered for a long time it would be fun to see Cherno build this app as a mobile app, either using React Native or in MAUI :D
20:30 id love to you setting up some project from bare bones :)
During my exchange semester in Canada there was a glitch with my course subscription and I was forced to study the courses nobody wanted : graph theory and NP-completeness... instead of learning about games.
21:53 I mean, I always try to make things explicit, so there is no guessing what type (in this case) snake is.
23:37 But is this procedure being called every time? Because when the snake gets bigger, the probability will become much lower.
Actually really good code this time
I'm finishing my degree in CS and you have to take a game programming class to make a game... most classes are just theory or simple programs.. I took an Ai class where we were given a sokobon game and had to use A* to beat the game.
34:50 whoa xD
I fully concur with the dislike of the "wall of code", and if I'm trying to read such an abomination, I always have to fix the formatting too. I personally always use Allman over the K&R style, though I would NEVER avoid contributing to a project that renders itself unreadable by enforcing K&R. Never. Nope! Not me. Uh uh.
33:50 compiler will probably optimize that code away and wont store anything
yes I would like to see
Emplace_back creates objects in place and should just be passed ctor arguments... so guess they were trying to have it call the move-ctor?
I imagine people using this-> is because otherwise its really unclear reading the code what is a method vs what is a function call, especially coming from Rust, Go or Python. That's my thinking when I look at this code anyway. Code conventions are so all over the place, as you often mention, that its hard to rely on them consistently?
This code formatting is making me insane.
Please the video on why games are good for programming. 🙏
I absolutely hate managing dependencies. I would rather have them as git submodules because i know where they are. i can't understand how people can live with cluttered c drive and unfortunately all programs including package managers natively shove their guts in these obscure directories of program files, program files x86, appdata etc.
I found Git submodules to be useful in case of internal packages usage (company scope) - in case of 3rd party dependencies I recently tried vcpkg in manifest mode, mostly for purpose of SBOM and other legal stuff. If you you have something better, I'm all ears :)
I recently switched to using submodules, because Spectrum in my area really sucks and keeps going down... So without an internet connection, cmake can't configure because it can't fetch the dependency.
Submodule also keeps the build folder smaller, especially in projects that have multiple targets (also something I do, Linux, Windows, Android and hopefully soon, multiple CPU Android targets).
I'm an elder statesman, so forgive the antique...but are people still reading "The Pragmatic Programmer"? It is the first of several books I REQUIRE my mentees to read.
I kind of miss the other series that were topics other than code reviews... Just my preference.
there's no reason to make an l-value for the LoadSound() calls. the lifetime of the temporary returned by string() is extended beyond the LoadSound call. completely unnecessary. also the ifdef should check for the size of the character type - that's what really matters here - not some magic compiler definition. although, the elephant in the room is why the fuck does raylib use char* paths on windows?
Computer science has rather little actual programming. And even then when learning programming Games are not a priority - if you are lucky you get to do 2- things related to games. And why would they? While that is fun it really doesnt help much with learning how computers work or how to create and utilise them. It is after all computer science and not game-development.
virtual - nearly all sane guidelines will tell you to NOT mark function as virtual and override as that just muddies the what. Is it just a virtual function? is it an override of a virtual function? why mark it as virtual when override already tells you exactly what it is? and it also messes up tooling that would otherwise help detect problems (even just the compiler warnings ).
why the "application" class? What purpose would it serve outside of adding more noise, more overhead, more files, more to keep in mind?
I have raylib installed systemwide so I dont have to bother with cpp's bullship when it comes to libraries..
9:34 checking for _MSC_VER actually checks for if we are on any native windows platform as clang and clang-cl will also define it on windows (unless you are in an msys environment but I don't consider that native). This is done for msvc compatability.
Here are some whitespaces "
"
They are cheap
"_body.emplace_back(std::move(newHeadPostiion))" WTF is this monstrosity? 1) you can't 'move' a const l-value, that doesn't make sense. 2) passing a value (l or r) to emplace_back is just going to call the copy-constructor. you should either push_back an (non-const) r-value, or emplace_back constructor arguments.
Edit: never mind, I was confusing a keyword.
You are confusing global variables with class static variables. The Cherno is absolutely right in his explanation here. To test it yourself, make a header file that contains a static array of 1 million bytes. Include it in one file and compile. Your exe will be around 1MB. Next include it in 2 files. When you compile again, your exe will be 2 MB.
@@theKStocky Sorry, yes I was confusing it with 'extern'
How do you style vscode like that?
This is Visual Studio not Visual Studio Code
was the video in spanish or was it me? like AI translated
At the beginning he says the audio is bad and will get better
TH-cam is testing automatically generated audio tracks and somethings it get's enabled by default. You can switch it back to the original in the settings for the video.
Anyone knows why I see the title of this video in French (my language) ? I don’t want to .. :(
same (and YT chose the corresponding audio track too, which was even more jarring)... anyway, click on your avatar (top right on my browser, just right of the notifications bell) and choose another language (English for example)
@@sergiosarabando9211 That will change all my TH-cam to english though. Thanks anyway :) I'll just unsuscribe for now.
Not only the title, it also auto select translated audios too. I don't think this is really any great...
automation is alien for beginners, dont even talk about cmake or git sub modules. manual install by hand. dont automate before you know what you are automating. walk before running or you will fall. if your focus is in the tools, you are not programming, you are doing admin maintenance. the more you do actual programming, the better. the more you do all sort of maintenance stuff, the worse. actual value hmh. I hate vulkan (tm). even opengl raster is a ton better. opengl is much better than even raylib. basic opengl yep. tools cant be the focus. optimizations especially in code layout later. dont optimize code layout before you have stable code base otherwise, you dont know what the code layout will be until you have all the functionality. your goal is to solve/make a solution, not the underlying systems. priorities. if the systems take over, the project is over. school induces bad weird practices to forcibly push into code (tm)(r).
How about a raylib+box2d?
I tuned out the 10th time you said to let you know in the comments below, obviously your intentions are elsewhere. Here is the comment you so desperately solicited. never again though.
7th
First!
Cmake Get is nice cause anyone cant get it with no problems.