The Cherno
The Cherno
  • 844
  • 74 337 945
Serialization in Hazel - My Game Engine
To 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.
Patreon ► patreon.com/thecherno
Instagram ► thecherno
Twitter ► thecherno
Discord ► discord.gg/thecherno
Hazel ► hazelengine.com
🕹️ Play our latest game FREE (made in Hazel!) ► studiocherno.itch.io/dichotomy
🌏 Need web hosting? ► hostinger.com/cherno
📚 CHAPTERS
0:00 - Intro
0:36 - Keep it simple.
7:48 - API overview
10:27 - Code walkthrough
18:55 - Real-world usage examples
21:22 - From scratch example
26:05 - Concerns
27:02 - Serializing to memory buffers
💰 Links to stuff I use:
⌨ Keyboard ► geni.us/T2J7
🐭 Mouse ► geni.us/BuY7
💻 Monitors ► geni.us/wZFSwSK
This video is sponsored by Brilliant.
zhlédnutí: 17 999

Video

WHY did this C++ code FAIL?
zhlédnutí 177KPřed 21 dnem
To 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. Patreon ► patreon.com/thecherno Instagram ► thecherno Twitter ► thecherno Discord ► discord.gg/thecherno CODE ► github.com/luizfelipemb/OresSDL Hazel ► hazelengine.com 🕹️ Play our latest game FREE (made in Hazel!) ► s...
Make C++ Apps & Games FOR THE WEB
zhlédnutí 44KPřed 21 dnem
Use code CHERNO for a limited time to get an additional discount for all yearly plans! ► hostinger.com/cherno Patreon ► patreon.com/thecherno Instagram ► thecherno Twitter ► thecherno Discord ► discord.gg/thecherno raylib ► www.raylib.com/ raylib itch.io deployment ► github.com/raysan5/raylib/wiki/Working-for-Web-(HTML5)#7-upload-raylib-web-game-to-itchio Genesis Remak...
Harder Than It Seems? 5 Minute Timer in C++
zhlédnutí 146KPřed měsícem
To 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. Patreon ► patreon.com/thecherno Instagram ► thecherno Twitter ► thecherno Discord ► discord.gg/thecherno Timer thread ► cplusplus.com/forum/beginner/99555/ Why I don't "using namespace std" ► czcams.com/video/4NYC-VU-...
The WORST BUG in my Game Engine
zhlédnutí 42KPřed měsícem
🚀 Take control of your schedule and boost your productivity with time-blocking! Try Akiflow today: bit.ly/cherno-free-trial Patreon ► patreon.com/thecherno Instagram ► thecherno Twitter ► thecherno Discord ► discord.gg/thecherno Hazel ► hazelengine.com 🕹️ Play our latest game FREE (made in Hazel!) ► studiocherno.itch.io/dichotomy 🌏 Need web hosting? ► hostinger.com/che...
I REMADE My First Game 12 YEARS LATER!
zhlédnutí 53KPřed měsícem
Try JetBrains IDEs NOW! ► jb.gg/Try-JetBrains-IDEs PLAY THE GAME! ► studiocherno.itch.io/Genesis Patreon ► patreon.com/thecherno Instagram ► thecherno Twitter ► thecherno Discord ► discord.gg/thecherno 💾 CODE Original (Java) ► github.com/TheCherno/Genesis Remake (C ) ► github.com/TheCherno/Genesis-Remake Hazel ► hazelengine.com 🕹️ Play our latest game FREE (made in Haz...
BINARY vs TEXT File Serialization
zhlédnutí 47KPřed měsícem
To 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. Hazel ► get.hazelengine.com Patreon ► patreon.com/thecherno Instagram ► thecherno Twitter ► thecherno Discord ► discord.gg/thecherno HxD hex editor ► mh-nexus.de/en/hxd/ Hazel ► hazelengine.com 🕹️ Play Dichotomy for F...
I Rewrote This Entire Main File // Code Review
zhlédnutí 130KPřed 2 měsíci
Patreon ► patreon.com/thecherno Instagram ► thecherno Twitter ► thecherno Discord ► discord.gg/thecherno Hazel ► hazelengine.com 🕹️ Play the latest Hazel game FREE ► studiocherno.itch.io/portal-me-away Code ► github.com/ural89/ConsoleCraftEngine Send an email to chernoreview@gmail.com with your source code, a brief explanation, and what you need help with/want me to re...
TERMINAL GAME ENGINE! // Code Review
zhlédnutí 63KPřed 2 měsíci
Try Code Rabbit for FREE now ► coderabbit.ai Patreon ► patreon.com/thecherno Instagram ► thecherno Twitter ► thecherno Discord ► discord.gg/thecherno Hazel ► hazelengine.com 🕹️ Play the latest Hazel game FREE ► studiocherno.itch.io/portal-me-away Code ► github.com/ural89/ConsoleCraftEngine 🌏 Need web hosting? ► hostinger.com/cherno Send an email to chernoreview@gmail.c...
Conversion Operators in C++
zhlédnutí 35KPřed 2 měsíci
To 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. Hazel ► get.hazelengine.com Patreon ► patreon.com/thecherno Instagram ► thecherno Twitter ► thecherno Discord ► discord.gg/thecherno 📚 CHAPTERS 0:00 - What are Conversion Operators in C 9:40 - Real world BUG 13:54 - R...
How New Game Engine Features are Implemented
zhlédnutí 25KPřed 2 měsíci
Schedule a Grammarly demo for your team using my link ► grammarly.com/cherno02 Patreon ► patreon.com/thecherno Instagram ► thecherno Twitter ► thecherno Discord ► discord.gg/thecherno Hazel ► hazelengine.com 🕹️ Play our latest game FREE (made in Hazel!) ► studiocherno.itch.io/dichotomy 🌏 Need web hosting? ► hostinger.com/cherno 📚 CHAPTERS 0:00 - Implementing a new feat...
Asset Packs
zhlédnutí 20KPřed 2 měsíci
To 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. Hazel ► get.hazelengine.com Patreon ► patreon.com/thecherno Instagram ► thecherno Twitter ► thecherno Discord ► discord.gg/thecherno Hazel ► hazelengine.com 🕹️ Play Dichotomy for FREE (made in Hazel!) ► studiocherno.i...
I ACCIDENTALLY Created Hazel's Greatest Feature
zhlédnutí 38KPřed 3 měsíci
I ACCIDENTALLY Created Hazel's Greatest Feature
EVERYTHING takes longer than it seems
zhlédnutí 32KPřed 3 měsíci
EVERYTHING takes longer than it seems
40 Days.
zhlédnutí 38KPřed 3 měsíci
40 Days.
Path Tracer Code Walkthrough (C++/OpenGL) // Code Review
zhlédnutí 20KPřed 3 měsíci
Path Tracer Code Walkthrough (C /OpenGL) // Code Review
From Editor to Runtime - The Hazel Engine Workflow
zhlédnutí 29KPřed 4 měsíci
From Editor to Runtime - The Hazel Engine Workflow
This Animation System IS AMAZING
zhlédnutí 54KPřed 4 měsíci
This Animation System IS AMAZING
BEST WAY to understand graphics and rendering code
zhlédnutí 35KPřed 4 měsíci
BEST WAY to understand graphics and rendering code
I'm Struggling.
zhlédnutí 103KPřed 5 měsíci
I'm Struggling.
I HAD to fix this immediately // Code Review
zhlédnutí 29KPřed 5 měsíci
I HAD to fix this immediately // Code Review
PATH TRACER made by 15-YEAR-OLD in C++ OpenGL! // Code Review
zhlédnutí 69KPřed 6 měsíci
PATH TRACER made by 15-YEAR-OLD in C OpenGL! // Code Review
Learning Programming by Trying and Failing // Code Review
zhlédnutí 37KPřed 6 měsíci
Learning Programming by Trying and Failing // Code Review
My Favourite Way To Make Websites
zhlédnutí 59KPřed 7 měsíci
My Favourite Way To Make Websites
How to Properly Setup C++ Projects
zhlédnutí 95KPřed 7 měsíci
How to Properly Setup C Projects
Don't Make This Mistake! // Code Review
zhlédnutí 50KPřed 7 měsíci
Don't Make This Mistake! // Code Review
Hazel Engine ON LINUX!
zhlédnutí 45KPřed 7 měsíci
Hazel Engine ON LINUX!
Game Engine Architecture 101 // Code Review
zhlédnutí 51KPřed 7 měsíci
Game Engine Architecture 101 // Code Review
2000 HOUR 2D Game Engine! // Code Review
zhlédnutí 79KPřed 8 měsíci
2000 HOUR 2D Game Engine! // Code Review
GPU Particle System, Real-time Global Illumination and More: The Future of Hazel - My Game Engine
zhlédnutí 31KPřed 8 měsíci
GPU Particle System, Real-time Global Illumination and More: The Future of Hazel - My Game Engine

Komentáře

  • @brunomarques5258
    @brunomarques5258 Před 4 hodinami

    Hi the cherno, thanks for this content of graphics programming, a little inspired by you, I've started to learn Vulkan and D3D12, and I found the VMA brother the D3D12MA, and now I've pulled request 2 features, all in cmake. I've followed the cmake path, instead of premake that your prefer, but i really thank you to be introduced in this content.

  • @nexovec
    @nexovec Před 7 hodinami

    Just today a compiler crashed on me after coming around to compile my hobby project after a year. I have something of this level every 2 months. 😅This is nothing

  • @Raattis
    @Raattis Před 8 hodinami

    With memory mapped files you wouldn't need different back-ends for memory and files. It also happens to be faster than file streams. Just something to consider.

  • @DavidOnonokpono
    @DavidOnonokpono Před 10 hodinami

    Thanks

  • @AnythingSoupVODS
    @AnythingSoupVODS Před 12 hodinami

    running on 165 hz, 6 ms per frame

  • @Edelina_Heine64
    @Edelina_Heine64 Před 14 hodinami

    I love your videos and want you to keep doing them💛💛

  • @Pablo360able
    @Pablo360able Před 15 hodinami

    Fascinating. I vaguely remember some of this from when I was first learning C++. I think that C# multidimensional arrays are really single-dimensional arrays under the hood, so they avoid some of the downfalls of actual meta-arrays listed here.

  • @MattSitton
    @MattSitton Před 16 hodinami

    Have you considered a merged writer reader serialization interface? Since it's possible to use the same interface to do both using pointers. This reduces the amount of serialization code and massively reduces the error of needing to write the code twice!

  • @xSferQx
    @xSferQx Před 16 hodinami

    I wish everything was manual and simple. All the magic behind the scene is not worth the time you invest learning what is happening there

  • @ohwow2074
    @ohwow2074 Před 18 hodinami

    Endianness, padding bytes, different sizes of data types in different platforms, etc will cause you quite a bit of headaches soon. That's why I forgot about binary serialization and just went with text sterilization. Not super efficient but easy to deal with.

  • @slygamer01
    @slygamer01 Před 19 hodinami

    I do a very similar thing, even in C#. The only real addition is that I version every non-trivial section, i.e. every implementation of Serialize/Deserialize in a class. On read, if the version of that section is less than the current version, it calls the method to read that version. It only ever writes the latest version, but it can read every previous version in addition to the current version. This way it can always load old data, and updates it when the data is saved again. Deserialize() { byte version = ReadByte(); switch (version) { case 1: DeserializeV1(); break; case 2: DeserializeV2(); break; } } DeserializeV1() { // Reads V1 data into the current data structure, adapting/updating where necessary or filling new fields with defaults } DeserializeV2() { // Reads V2 data into the current data structure }

  • @felix8103
    @felix8103 Před 20 hodinami

    std::format in C++ 20 🔥

  • @faizydeveloper
    @faizydeveloper Před 23 hodinami

    Best series i ever seen, #Thanks_Cherno

  • @pannagasudarshan6639
    @pannagasudarshan6639 Před 23 hodinami

    what's memory leak??

  • @pannagasudarshan6639

    Lot of questions left unanswered I felt. Why was Printable turned into a pointer in the function argument? lot of scrolling which left me confusing ://

  • @pannagasudarshan6639

    Yeah this whole video just went over my head :/

  • @swapansaha2368
    @swapansaha2368 Před dnem

    Vulkan series pls

  • @swiftcodey
    @swiftcodey Před dnem

    As a related topic, I'd love if you could cover versioning with respect to serialization. Seems not as trivial as I'd hope.

  • @ultimatesoup
    @ultimatesoup Před dnem

    That's not always true. Sometimes you have to introduce complexity for flexibility and extensibility purposes

    • @Pablo360able
      @Pablo360able Před 23 hodinami

      in which case the best solution is the simplest one that meets the design goals of flexibility and extensibility

  • @MrSofazocker
    @MrSofazocker Před dnem

    15:18 "Non trivial types" Why not deconstruct them and save a mapping + the data?

  • @MrSofazocker
    @MrSofazocker Před dnem

    IMO Complexity Demon enters code-base when requirements are not clear and one doesn't see the common denominators. When abstracting things down to the common and shared denominators a system becomes most simplistic in a way, that is so generalized it will fit any use-case. Yes, things can be really simple if built for one thing, and one thing only. But they can also be simple, if not simpler when they were built to the underlying mechanics that govern whatever system you are building.

  • @MrFluteboy1980
    @MrFluteboy1980 Před dnem

    I'm not great at C#, but why couldn't they just have used a local variable in the Main function instead of a static member object? I suspect it's not happy with the Window being not null because the class in which the Main function sits doesn't instantiate the Window object in its constructor, but that would mean having to still instantiate a brand new unrelated object, and have a constructor for it, just to remove the question mark

  • @SirusStarTV
    @SirusStarTV Před dnem

    What a fun and easy process

  • @nitrogenez
    @nitrogenez Před dnem

    i did some serialization in zig. let me just say that it's much less hassle for the task than in c++.

  • @SeishukuS12
    @SeishukuS12 Před dnem

    I wouldn't write a size_t to a stream, it *could* be different on different compilers/platforms/OSes, should *always* use a known size type when doing data streams. It's been mentioned in other comments, but endianness can also come into play, but also a majority of systems out there are little endian... So that's less of an issue IMO.

  • @marcususa
    @marcususa Před dnem

    Way too much babbling. I lasted 10 minutes, then stopped.

  • @shadow_blader192
    @shadow_blader192 Před dnem

    MGE 🤔

  • @EraYaN
    @EraYaN Před dnem

    Any reason for not using any of the serialization formats that use a code generator? Protobuf, cap’n’proto, msgpack etc.

  • @mlecz
    @mlecz Před dnem

    While this code effectively reads and writes int32_t and int64_t, it's important to note that it may not be cross-platform due to differences in endianness between systems. Little-endian and big-endian architectures store bytes in different orders, leading to potential incompatibility when sharing binary files across platforms. To ensure cross-platform compatibility, consider explicitly managing byte order or using libraries like Boost which have functions like native_to_big and big_to_native to handle endianness conversion.

    • @NullPointerDereference
      @NullPointerDereference Před dnem

      ChatGPT wrote this comment.

    • @mlecz
      @mlecz Před dnem

      @@NullPointerDereference yup I'm too lazy

    • @MrSofazocker
      @MrSofazocker Před dnem

      You generally can tell the system which one to use, its a non-problem.

    • @mlecz
      @mlecz Před dnem

      ​@@MrSofazocker How in C++17/23 without using assambler can I do this globally? endian.h from c++ provide only functions like htole32 and htobe32 similar to native_to_big and big_to_native from boost? Some chips provide method to switch but not all.

    • @Pablo360able
      @Pablo360able Před dnem

      @@mlecztoo lazy to write your own comment on a youtube video? what's this world coming to

  • @crouette
    @crouette Před dnem

    The "difficult" part is not structs/objects... it's shared struct/objects, that are saved in one place and then, pointers to them need to be recreated when loading... pointers can act as a "uid"... but that means that a struct/objects when loading need to have a place to store the pointer at save time, and when loading look for it in the loaded objects. That is introducing some dependencies, and so, the order to save and load becomes important.

    • @MrSofazocker
      @MrSofazocker Před dnem

      Why do you think that? The datatypes already are loaded into a tree, which you can derive their usage through, data gets stored somewhere every use gets the pointer to it? Done.

    • @Muzzleflash1990
      @Muzzleflash1990 Před 13 hodinami

      @@MrSofazocker In that scenario they might not be in a tree; the pointed structs might form a graph. Then you cannot simply use WriteObject as done here since you might with mutual recursion. While you could serialize the pointer as an ID, you still need to also associate the objects with the ID in the serialization itself. And when reading, the pointed to object might not have been loaded yet, so either you would have return to the object later to fix the pointer, or you can load it immediately assuming you have associated the current object with its ID. None of this is insurmountable at all, but still more difficult. Even in the simpler case with just a shared object, you will still need to avoid duplicating it when deserializing again.

  • @paradox8425
    @paradox8425 Před dnem

    Great content, but where is Vulkan series? :d

  • @dino_source
    @dino_source Před dnem

    Why to use char* data to represent bytes in 2024? Is the std::vector<std::byte> not good enough? We have std::byte since C++17 if I'm not mistaken...

  • @kingofspades9720
    @kingofspades9720 Před dnem

    Someone already commented this, but if you get something like this #error OpenGL already defined The reason is your includes in ImGuiLayer.cpp has to be like this #include "voxpch.h" #include "ImGuiLayer.h" #include "imgui.h" #include "backends/imgui_impl_glfw.h" #include "backends/imgui_impl_opengl3.h" #include "Voxen/Application.h" #include <GLFW/glfw3.h> #include <glad/glad.h> If it looks like it is the exact same as what you have, it isnt, you are likely including 'imgui_impl_glfw.cpp' and 'imgui_impl_opengl3.cpp' but those both need to be the .h in the ImGuiLayer.cpp

  • @mlecz
    @mlecz Před dnem

    Cherno, you don't need to call the .close() method on std::ofstream in destructor because RAII (Resource Acquisition Is Initialization) automatically handles closing the file when the object goes out of scope. Manually calling .close() leads to redundancy since the destructor of std::ofstream closes the file automatically.

    • @MrSofazocker
      @MrSofazocker Před dnem

      Nope, while redundant it's more implicit writing. It's not gonna close twice or smth, or idk what you think.

    • @mlecz
      @mlecz Před dnem

      @@MrSofazocker this is redundant in terms of style and code management, as well as good practices for complying with the principles of the RAII idiom

    • @ohwow2074
      @ohwow2074 Před 20 hodinami

      Close can fail inside the destructor and the destructor will hide the error. By calling close before the destructor is called you ensure that nothing inside the destructor is going to fail without you knowing about it. It's a clever technique.

    • @mlecz
      @mlecz Před 19 hodinami

      @@ohwow2074 but the problem is that there is no exception handling in his code which would make your theory valid in this case

    • @ohwow2074
      @ohwow2074 Před 19 hodinami

      @@mlecz he said he didn't have time to implement error handling. He'll probably do it in the near future. Also no need for exceptions. Calling close and then checking the state of the stream with `if (!stream) return error_code;` is the way to go.

  • @kingofspades9720
    @kingofspades9720 Před dnem

    "I'm not even sure how that code I wrote yesterday worked at all" - Every programmer 23:39

  • @nan0s500
    @nan0s500 Před dnem

    What about endianess of architectures? Am I correct that you don't handle it or am I missing something?

  • @Arwahanoth
    @Arwahanoth Před dnem

    why not use std::ostream and std:istream interface?

  • @ferdynandkiepski5026

    Yeah, as others have said, add versioning. The first few bits should be used for checking the version.

  • @anon_y_mousse
    @anon_y_mousse Před dnem

    I still think you should just use an open source library for some configuration format like TOML and just serialize all data that doesn't fit into a basic type as a b64 encoded string, but that may just be me who is thinking that and you'll never read this anyway. Also, you must have written Java for many years looking at how you're designing this.

    • @Muzzleflash1990
      @Muzzleflash1990 Před dnem

      Why on earth would expand increase the size by 33% and do extra processing for b64 encoded strings unless you are passing it from and to poorly made web services that have escaping errors?

    • @anon_y_mousse
      @anon_y_mousse Před dnem

      @@Muzzleflash1990 Since my responses keep getting deleted I'll say it again, to prevent data corruption from users that edit the file with notepad and for web services.

    • @Muzzleflash1990
      @Muzzleflash1990 Před dnem

      @@anon_y_mousse It a simple transform (assuming you already have it byte-serialized) to convert to b64 if you need to. But there is no need to pay the cost up front if you never need to. I see what you mean now with TOML and b64 serialization for the "too complex" types. Having user editable data format though has entirely different considerations and goals than a binary encoding for performance. NVME SSDs are faster nowadays than many JSON parsing libraries.

    • @Muzzleflash1990
      @Muzzleflash1990 Před dnem

      @@anon_y_mousse Also, 9 out of 10 times I include a link in a YT comment the entire comments gets auto-deleted. YT really is a terrible platform for technical discussions.

    • @anon_y_mousse
      @anon_y_mousse Před dnem

      @@Muzzleflash1990 I referred to the program by its filename and that's probably what triggered it. Possibly another reason to dislike D's scope resolution operator over C++'s.

  • @xthebumpx
    @xthebumpx Před dnem

    How much simpler would this be with something like Serde in Rust? Is there no good C++ equivalent, or is something like that generally not suitable for a game engine?

  • @RelayComputer
    @RelayComputer Před dnem

    It doesn't look much different conceptually than Apple's Foundation NSCoding protocol. Serialising and deserialising is totally manual but extremely simple too.

  • @miket591
    @miket591 Před dnem

    I would really recommend you to dump std::ofstream, wrap a low level FILE (fopen) API, as it can easily be between 2x to 10x faster to read/write files than std::ofstream. Please test it.

    • @ohwow2074
      @ohwow2074 Před 19 hodinami

      Just switching to a C API makes things 10X faster?

    • @miket591
      @miket591 Před 18 hodinami

      @@ohwow2074 It has nothing to do with it being C but how they have been implemented. std::ofstream is build upon FILE but adds an extra layer of buffering and lock access, in addition to stream checking and accounting. If you use FILE directly, you only get 1 buffer layer instead of 2, and you can "not use" the associated lock given that mostly likely you will never have 2 threads competing to read the same file stream simultaneously. I have measured this.

    • @ohwow2074
      @ohwow2074 Před 17 hodinami

      @@miket591 interesting. I'll try and dig deeper into this.

  • @K3rhos
    @K3rhos Před dnem

    4 things that got to my eyes instantly: - Like you mentioned the useless if - The usage of what I call an "OG loop" ahah, honestly a foreach would be cleaner here, since the index "i" doesn't seem to be involved in any other things rather than just getting the element from the array. - The usage of 2 "GetComponent" that can be associated to a variable like this: Collectable coll = g.GetComponent... - And also a check to see if the component "Collectable" actually exist on the gameobject.

  • @lennymclennington
    @lennymclennington Před dnem

    I don't understand why you need to explicitly define a destructor to call m_Stream.close() when the destructor of m_Stream itself will close the stream. Standard streams already use RAII. It would also probably make more sense to construct m_Stream directly in the constructor's member initialiser list rather than constructing a temporary and move assigning it in the body.

  • @nextlifeonearth
    @nextlifeonearth Před dnem

    To me the static member function is polluting the data type. You can just make functions, so you may even put the serializers in another file and don't have to include your serializers if you don't need them.

    • @Muzzleflash1990
      @Muzzleflash1990 Před dnem

      That will highly couple the now separate serialization implementation to the data type, and you would not be able to serialize private or protected members.

  • @ScottHess
    @ScottHess Před dnem

    I once implemented a serialization framework where I abstracted write and read to a certain extent. Instead of readInt() and writeInt(), there was processInt(), with a pointer to an int (and so on). The reader object would fill in the pointer, the writer object would write out the pointed-to value. Maybe 5% of code needed to test whether it was reading or writing, for instance if it needed to initialize other structures on read, but you didn't want to run that code on write. Doing it this way really removed a lot of the boilerplate associated with such systems. [In general you have my full agreement on simple, though. In my experience self-deriving serialization libraries are only useful for tech demos, for real code it is worthwhile to do it right the first time.]

  • @AadiPoddarKolkata
    @AadiPoddarKolkata Před dnem

    what's the visual studio color theme

  • @elirannissani914
    @elirannissani914 Před dnem

    So the first 10 minutes is you crying on your little project

  • @julkiewicz
    @julkiewicz Před dnem

    The simplest solution is the best solution - and that's why I chose C++ :) Just kidding, I know there isn't much of an alternative really

  • @utilyre
    @utilyre Před dnem

    This pattern is gonna be so useful for my final project. Thanks man