C# GAME ENGINE BY 16-YEAR-OLD! // Code Review

แชร์
ฝัง
  • เผยแพร่เมื่อ 23 พ.ย. 2024

ความคิดเห็น • 583

  • @TheCherno
    @TheCherno  2 ปีที่แล้ว +116

    Thanks for watching! More C# code reviews? Also don't forget to visit brilliant.org/TheCherno/ to get started learning STEM for free, and the first 200 people will get 20% off their annual premium subscription! ❤️

    • @Dustyy01
      @Dustyy01 2 ปีที่แล้ว +4

      First time i didn't skip the sponsored ad, so well presented 😂

    • @Stabby666
      @Stabby666 2 ปีที่แล้ว +1

      Heh, this reminds me of years back when I was converting old arcade games over to run in JS for fun, and then they started popping up on other sites, just with my name removed from the code etc. I then started adding weird variable names so I could google for them, and contacted the people who were pretending they'd written the games themselves. In almost EVERY case they apologised with "Oh, I didn't think you'd mind". I wouldn't have minded - I never made money from them in any way, but having other people claim they wrote the code you worked hard on is a bit disrespectful...

    • @wcdeich4
      @wcdeich4 2 ปีที่แล้ว

      Are there any good JavaScript 3D frameworks besides Three.JS?

    • @Stabby666
      @Stabby666 2 ปีที่แล้ว +1

      @@wcdeich4 ThreeJS is great. I've used it for a lot of event installation work and its been really reliable. There are other game engines for JS though if that's what you need.

    • @potterloko0175
      @potterloko0175 2 ปีที่แล้ว +5

      Yes. more C# code!!!

  • @TechRevi
    @TechRevi 2 ปีที่แล้ว +383

    One question mark broke Cherno for like 10 minutes.

    • @lightknight876
      @lightknight876 2 ปีที่แล้ว +38

      In fairness to this genius, he did warn us about his c# skills

    • @tavaroevanis8744
      @tavaroevanis8744 2 ปีที่แล้ว +7

      The difference in time between Cherno's puzzled state versus mine would be 10 hours, not 10 minutes.

    • @AntoninHoudekidk
      @AntoninHoudekidk ปีที่แล้ว +4

      btw, it only works if u enable it in csproj by `enable `, so by default its not how it actually works

    • @perrylets
      @perrylets 4 หลายเดือนก่อน

      Except that all templates have it on by default​, so you need to createthe cspeoj by hand or change it on purpose for it to not be enabled @@AntoninHoudekidk

  • @LRAnimations
    @LRAnimations 2 ปีที่แล้ว +564

    Those new non-nullable features are actually a big thing and most modern programming languages are moving towards approaches like this. For example Kotlin, Dart, Typescript. They all work like this, where everything is considered non-nullable until you specifically say its nullable. This is really helpful, because most of the cases you don't need things to be nullable and in the cases you do need them to be nullable, the compiler helps you checking if its null or not before doing operations on it. Also when you do stuff like if (variable != null) {, inside the block after the if, the variable gets promoted and the compiler automatically considers it to be non-nullable, which is just amazing

    • @flerfbuster7993
      @flerfbuster7993 2 ปีที่แล้ว +76

      I like Rust's approach the most. The concept of null doesn't exist in that language, so if you need to represent "nothing", you need to wrap your value with an Option type. This way, you always have to explicitly handle the possibility of no value, essentially preventing null references.

    • @andy010
      @andy010 2 ปีที่แล้ว +18

      most people don't know this, but you can actually chage that behaviour, by modifying the .csproj file. It also allows to get the old using behaviour back

    • @nxte8506
      @nxte8506 2 ปีที่แล้ว +12

      Yes it is really helpful, I’m a typescript dev and it’s one of the things that makes web development so much easier. Now most of the runtime errors I face are just browser incompatibility issues like an api not being available on the window in some browsers. Typescript usually helps as much as possible in these cases.

    • @luk17032000
      @luk17032000 2 ปีที่แล้ว +9

      @@flerfbuster7993 what also great about is, that this is a zero-cost abstraction as rust represents the None as a null pointer, while the Some just points to the memory location.

    • @ko-Daegu
      @ko-Daegu 2 ปีที่แล้ว +12

      Python & JS dev: what are y’all on about 🙃

  • @asmrtv2566
    @asmrtv2566 2 ปีที่แล้ว +231

    Every time a video like this is uploaded I feel dangerously stupid.

    • @tiesjongebreur2770
      @tiesjongebreur2770 2 ปีที่แล้ว +2

      🤣

    • @lanchanoinguyen2914
      @lanchanoinguyen2914 ปีที่แล้ว +4

      C# is for noobs

    • @rmt3589
      @rmt3589 ปีที่แล้ว +3

      ​@Morrisson._Python, C++, or Assembly, in that order?

    • @ThisShitWontWor
      @ThisShitWontWor ปีที่แล้ว +20

      Real g use assembly in a hex editor

    • @mateuszgaecki5949
      @mateuszgaecki5949 ปีที่แล้ว +13

      ​@@lanchanoinguyen2914exactly. I use only machine code. I don't udnerstund why would anyone use a "programming language". More like "I'm an idiot language"

  • @frederikja2210
    @frederikja2210 2 ปีที่แล้ว +331

    Im one of the maintainers of OpenTK, and it is really interesting to watch through someone critiquing a project made using a library i have helped so much.
    I have spent way more time developing opentk itself than making products with it. So very interesting watch.

    • @smrtfasizmu6161
      @smrtfasizmu6161 2 ปีที่แล้ว +3

      I'm just curious, what language do you use to make these libraries and how are libraries which allow dealing with graphics even made, like how is opengl, glfw, freeglut, or awt and swing libraries for Java even made?

    • @jashitshah3897
      @jashitshah3897 2 ปีที่แล้ว +9

      @@smrtfasizmu6161 to the best of my knowledge, it just starts off with a couple of system-provided methods that allow you to open a window and draw a bunch of pixels, and it builds up from there using a whole lot of math. For starters you could take a look at projection matrices to get a feel of how 3D stuff is rendered on a 2D screen.

    • @smrtfasizmu6161
      @smrtfasizmu6161 2 ปีที่แล้ว +1

      @@jashitshah3897 but how do you accès these system methods, can I access them from within my program? If not, then how do these libraries access it? Do they get some special permission from Microsoft? I mean, I don't know how to do that in Ubuntu either.

    • @sps014
      @sps014 2 ปีที่แล้ว +6

      Thanks for your amazing work

    • @jashitshah3897
      @jashitshah3897 2 ปีที่แล้ว +5

      @@smrtfasizmu6161 it usually differs from one operating system to another. For example, the Windows API abstracts this functionality for you in Windows. So when you create a traditional desktop app in C++, it starts off with this WinAPI. For other languages, the functionality is just C or C++ wrappers. I don't exactly know the Linux equivalent to WinAPI, but it works on pretty similar basis.

  • @tanmaypanadi1414
    @tanmaypanadi1414 2 ปีที่แล้ว +60

    45:00 the moment cherno realised it's his code.
    Thanks Sam 🤣 for the code.
    *insert Spiderman meme*

  • @r1pfake521
    @r1pfake521 2 ปีที่แล้ว +119

    Since you asked about C#, I would be interested to see you talk about your C# scripting integration in Hazel

    • @Knollee_oder_so
      @Knollee_oder_so 2 ปีที่แล้ว

      ⇑ This please

    • @bendywills859
      @bendywills859 2 ปีที่แล้ว +1

      Yes, please.

    • @iXmerof
      @iXmerof 2 ปีที่แล้ว

      Yes, definitely, please tell us how you script other language's code

  • @scottwalker4619
    @scottwalker4619 2 ปีที่แล้ว +67

    I wasn’t sure about the nullability improvement in C#8 but after using it for a few years I’ve became a fan. It forces you to think a lot more about the code you’re writing and you can change the compiler settings to force a compile failure when there is a potential null reference exception. It came after some statistics were released that said something like 80% of all exceptions in C# are null reference exceptions.
    But if you don’t like it, you can always turn it off and choose not to use it.

  • @QckSGaming
    @QckSGaming 2 ปีที่แล้ว +92

    Rendering architecture was probably the biggest hurdle after figuring out OpenGL so would definitely be a helpful video for a lot of people!

    • @garlfin
      @garlfin 2 ปีที่แล้ว +10

      all the opengl tutorials teach you how opengl works but not the architecture of something that could be commercial so I kinda just went with whatever I thought of. garengine was my first "engine" and gESilk I improved on what I thought could be different.

    • @QckSGaming
      @QckSGaming 2 ปีที่แล้ว +4

      @@garlfin It's extremely good. You are gonna make a great graphics programmer in the future and get paid well :P Already ahead of engineering bachelors

    • @garlfin
      @garlfin 2 ปีที่แล้ว +6

      @@QckSGaming that’s really nice to hear :)

    • @theinventor8793
      @theinventor8793 2 ปีที่แล้ว

      @@garlfin Why not use C++?

    • @garlfin
      @garlfin 2 ปีที่แล้ว +1

      @@theinventor8793 dunno I just don’t like c++

  • @ironnoriboi
    @ironnoriboi 2 ปีที่แล้ว +188

    C#'s new nullable (which needs to be explicitly enabled) is really nice. Especially for large codebases, you no longer need to check for null in every method that receives a class as argument unless its an Optional (? postfix) type.

    • @Mischu708
      @Mischu708 2 ปีที่แล้ว +2

      It's also good for asp backends since you use optionals everywhere when you talk to the database(your fields may or may not be null).

    • @HermanWillems
      @HermanWillems 2 ปีที่แล้ว

      A language should not contain null. Also default variables should be not muttable. Also C# error handling blegh. Languages without NULL always work way better.

    • @ironnoriboi
      @ironnoriboi 2 ปีที่แล้ว +15

      @@HermanWillems Every way of working has its usecase, including null values.

    • @vishaalshiva2009
      @vishaalshiva2009 2 ปีที่แล้ว +2

      Is C# nullable any different from Swift's optional? To me, they appear to perform the same function.

    • @HermanWillems
      @HermanWillems 2 ปีที่แล้ว

      @@ironnoriboi then why the inventor of NULL regrets it and wishes he never invented it?

  • @ianknowles
    @ianknowles 2 ปีที่แล้ว +95

    The null-able opt-in is a tad useless in your case but if you declare something as not null then it forces you to set in the constructor. Which is really handy in c# especially when dealing with DI..

    • @monad_tcp
      @monad_tcp 2 ปีที่แล้ว +4

      Its not useless if you flip that switch on the compiler to require assertions.

    • @ianknowles
      @ianknowles 2 ปีที่แล้ว

      @@monad_tcp point in kind its there for a reason... We usually have CI/CD pipes pick up these kinda errors.

    • @monad_tcp
      @monad_tcp 2 ปีที่แล้ว +1

      @@ianknowles no, CI/CD is not to pick those errors. They are for integration and functional tests. Those tests that test for conformance to the specs.
      The entire purpose of a statically typed system is to catch those kind of errors (nullability) as you type the code, besides for CI/CD to work, you need to write the tests. You want to test real functionality, not correct input to functions, this is just part of the contract of the function, you test the implementation. (that was used to be called unit testing from electrical engineering, before it was subverted to do what type systems do, because of languages like Python/Ruby/Javascript)
      I'm totally against unit testing. Type systems were designed to catch boundary errors automatically.
      Csharp is a tad outdated in this regard, which is why I'm switching to F#, because of a better type system.

    • @monad_tcp
      @monad_tcp 2 ปีที่แล้ว

      That said, the new non-null by default goes a great way to reduce errors.
      That's the problem with testing in general, you make an error and try to find it and fix.
      This is stupid, I'm much prefer tools that make entire classes of errors impossible.
      Which is why I'm totally against unit testing on statically typed languages.

    • @monad_tcp
      @monad_tcp 2 ปีที่แล้ว +1

      Come on, that's basic software engineering, the earliest you catch the error, the cheapest is to fix it.
      What's earlier than not even being able to write the error? Because the compiler will flag it as you type...
      (earlier than that only your brain...)

  • @asherhaun
    @asherhaun 2 ปีที่แล้ว +50

    I really enjoyed seeing a dive into a C# renderer and the critiques of the architecture.
    It would be great if you made a video about basic openGL rendering architecture like you mentioned.

    • @jondoe6608
      @jondoe6608 2 ปีที่แล้ว +1

      yep, id love that opengl video to be made!

    • @pieTone
      @pieTone 2 ปีที่แล้ว +2

      Yoo asher be here also

    • @asherhaun
      @asherhaun 2 ปีที่แล้ว

      @@pieTone naturally :D

  • @voxelrifts
    @voxelrifts 2 ปีที่แล้ว +38

    I was a part of the discussion where Sam helped garlfin with the bloom and he did make it clear that this was your code but slightly modified for better names for garlfin to understand.
    He didn't mean to steal your code just saying :D

    • @asherhaun
      @asherhaun 2 ปีที่แล้ว +4

      LOL, I had a suspicion that the sam was from the GWG discord, but I just found the conversation. I knew I had seen that model and scene somewhere before.

    • @ltl-cx9xs
      @ltl-cx9xs 2 ปีที่แล้ว

      Glad to see the problem resolved. But still, this is great work by someone who is 16.

  • @chadwad12
    @chadwad12 2 ปีที่แล้ว +6

    As mentioned in previous comments, in this case in the code the nullable reference type didn't really mean much and really didn't need to be there. But the feature is a great feature to have turned on in projects. There is actually stories of major libraries turning it on and actually finding bugs that they never knew were there because the compiler could actually tell you that "hey this could be null." Most modern languages are implementing things like this. NULL is something most languages are really trying to force out of the language now. I recently just wrote a library for something at work with the feature turned on and it really helped me out a lot.
    I am learning Swift and it's similar, and I love it. I've yet to actually run into a null pointer exception because the compiler will really force you to think about "are you sure you want to do this...I could be null."

  • @ruannilton5119
    @ruannilton5119 2 ปีที่แล้ว +18

    I see a big problem with this ECS implementation, since Component is a class, we can not controll where his objects will be instantiated, even when we have a List behind the hood what we have is just a list of references, the true objects will be spread in the application memory.

    • @etodemerzel2627
      @etodemerzel2627 2 ปีที่แล้ว

      ECS without benefits of ECS. Lol

  • @TizzyT455
    @TizzyT455 2 ปีที่แล้ว +23

    Being mainly a C# programmer myself I'd love to see more C# videos but I would like to see a bunch of other languages too. Also I think the diagram thing you were thinking of was a dependency graph? Its a feature only available I think in the professional or enterprise versions.

  • @nitrous1001
    @nitrous1001 2 ปีที่แล้ว +18

    I started watching your channel in learning computer graphics and a bit of interest of C++ programming but I'm mainly geared towards C#. Because of it, I'd love to see more C# code reviews as well!

  • @okcharles7
    @okcharles7 2 ปีที่แล้ว +1

    Nullable reference type is a compiler service rather than a type.
    You can call the service by post-fixing '?" to the variable you want the compiler to watch your code mistake.
    from time to time, for a short time, you can turn it off by post-fixing '!'.
    That is different from nullable value type, which wraps a value type and expose the underlying value thru "Value" properperty.
    Nullable reference type doesn't cost you at all because it is a compiler service.
    However, it gives you amazing relief and safety to your code.
    It is the very cure for the all the madness from the implicit null initialization.
    It is one of top three adorable things in C#, along with ref( and out) and delegate.

  • @Chiramisudo
    @Chiramisudo 2 ปีที่แล้ว +30

    14:21 I was also 16 when I started programming, but didn't get to run my first line of code until I was 17 because I was too poor to afford a PC back in the 90s when they were several thousand $, so I "programmed" with paper and pencil. On the plus (or rather C++) side, all my code was bug free. 😋

    • @balern4
      @balern4 2 ปีที่แล้ว

      Lmao

  • @bity-bite
    @bity-bite 2 ปีที่แล้ว +8

    @ 7:33 The NRT feature was introduced in C# 8 to *basically* protect developers from making null reference exceptions.

  • @samsonmayeem8409
    @samsonmayeem8409 2 ปีที่แล้ว

    At 8:30, "so if I understand this now". I say this usually in lectures when confirming my understanding. I have recommended your tutorial for my trainees, and they are excited too. You are my boss. Greetings from Ghana, I love you the Cherno.

  • @jpack61108
    @jpack61108 2 ปีที่แล้ว +1

    This is probably the most informative code channel ive come across on youtube. i appreciate that.

    • @infinityminuszero
      @infinityminuszero 2 ปีที่แล้ว

      I suggest you also check Nick Chapsas if you're looking for a good C# channel

  • @rafaeldeconde8148
    @rafaeldeconde8148 2 ปีที่แล้ว +16

    I particularly prefer the C++ reviews, but once in a while is good to have other languages. Awesome review!

  • @iaobardar3452
    @iaobardar3452 2 ปีที่แล้ว +10

    I’d love it if you made a rendering architecture video! I’ve been trying to figure out how to use OpenGL in my free time, and been stumped by how to make it more abstract and less hard coded. Right now I just have it hard coded directly in the main loop (really bad design) because I’m using C and have no idea what sort of structures to make to make it work. Am I supposed to make stuff global, and if not, what? Malloc meshes and point to them in an global or static array? Having a tutorial or explanation of how to make a better and more expandable system would be epic!

  • @Chris-dx6dw
    @Chris-dx6dw 2 ปีที่แล้ว +1

    Its a static member of the class. If you didn run Main, then it will always be null. Now in this case, we always run Main. But if this was another class, you may never run the method that initialises it. So any use of it would get a null ref exception. So this is to let us know that null is a possible value.

  •  2 ปีที่แล้ว +2

    Nullable is really useful, especially when the TreatWarningsAsError flag is switched on. Because your code won't even compile when you forgot to assign a value to the variable, which is intended to be not null. It saves a lot of time being lost during compilation/test/debug/fix/retry/foo/bar. And we all make such mistakes often.

  • @fredvin27
    @fredvin27 2 ปีที่แล้ว +4

    +1 for the renderer architecture. Great videos, great review, thanks a ton for the content !

  • @lukefidalgo8154
    @lukefidalgo8154 2 ปีที่แล้ว +28

    16:52 I'm pretty sure you _can_ start your variables with _ in C++, or at least MSVC supports it

    • @kapilverma2668
      @kapilverma2668 2 ปีที่แล้ว +6

      GCC also supports naming variables with Underscore< _ >. C++ identifier rule states that name can start upper or lower case alphabet or Underscore but not a number.

    • @ohwow2074
      @ohwow2074 2 ปีที่แล้ว +9

      _ is reserved for future uses in standard library. You shouldn't use them in your code.

    • @ChlorieHCl
      @ChlorieHCl 2 ปีที่แล้ว +6

      You can name your identifiers _like_this but not _Like_this. Also if the identifier in question is global then starting with underscores would not be allowed. So most people just don't bother risking that and just use other naming schemes.

    • @dmail00
      @dmail00 2 ปีที่แล้ว +1

      You can as long as it is not in the global scope and is not followed by another underscore or a capital letter.

    • @dmail00
      @dmail00 2 ปีที่แล้ว

      Just for anybody interested why it is used in C#: C# Coding standard from msdn "Use camel casing ("camelCasing") when naming private or internal fields, and prefix them with _." ... "When editing C# code that follows these naming conventions in an IDE that supports statement completion, typing will show all of the object-scoped members."

  • @PostNoteIt
    @PostNoteIt 2 ปีที่แล้ว +1

    I'd like to see right after the end of your analysis another run of the program where you point at the effects you've seen the implentation of in the actual code. Like going full circle, starting with the program, exploring the details, getting bck to the program and putting the details into perspective.

  • @smrtfasizmu6161
    @smrtfasizmu6161 2 ปีที่แล้ว +14

    I was told the same thing about static variables from experienced programmers on the Internet that Cherno said about 20:00. And Cherno also nailed the reason why I used static too much in my biggest Java project that I have made. It was the path of least resistence to make variables reachable from everywhere within the program. I was told that I should have made variables non static and pass around the instance of the class to which these variables belong but now I am too lazy to rewrite the code. However, at least now I think I see why that was the prefer way of doing it even though if you are a beginner it can seem like an unnecessary complicated way to do it (as opposed to just making variables static). Also, I thought that by making variables static I was saving memory (yeah, saving like 40 bytes of memory, good job me). Because if I pass the instance around then I would have a lot of pointers pointing at the same thing. So, I was like, let's just use static then, no unnecessary memory consumption! (I saved couple of bytes of memory yay, I am so good and I see what matters in the program!). Some person on the Internet told me that if I want several chess match to play simultaneously, my architecture fails, while the approach with non static variables with passing around the instance of the class to which those variables belong can do the job. Yeah, I think I see his point, but I am too lazy to rewrite the code. I will keep that in mind for the future. Another experienced programmer told me that a lot of the time if you declare something static you shoot yourself in the foot and I thought "but why". Then I realised that in another Java project I did shoot myself in the foot by putting static in the certain places in my code and the reason was quite similar as in the chess example. It was a server-client kind of project. If I wanted to have several servers running at the same time, my approach didn't work because I used the word static somewhere where I shouldn't have.
    Another thing which I as a beginner learned I was doing wrong is that I didn't use constants enough so my code often times has too much random looking numbers which will probably just look confusing to someone else looking at my code.
    It is funny because when I wrote the chess program 2 months ago I thought my code was great and just 2 months later I kind of think at least parts of my code are crap. It will be interesting to see what I will think about that chess program a year from now. Probably I will think it is total crap.

    • @MichaelMorguarge
      @MichaelMorguarge 2 ปีที่แล้ว +3

      Static used in moderation for utility classes if fine. Static on most things or everything for a large project is not good. There is a time and a place for lots of static (small example projects) and non-static (medium to larger projects). Static also effects reduces your ability to test (Testing/Mocking in C# is not nice with static methods) with the number of libraries that support the static-design programming. Unless you are going for a specific architecture, you are limiting the possibility of expanding on what you are working with and saying "this is the last iteration of this application. It is perfect now and forevermore.".

    • @AdroSlice
      @AdroSlice 2 ปีที่แล้ว +6

      Dogmatic OOP is horrible anyway, a lot of the time if static-everywhere does suffice that stuff should really be straight procedural code

    • @MichaelMorguarge
      @MichaelMorguarge 2 ปีที่แล้ว

      @@AdroSlice sometimes you feel like you are trying to shave a square peg to fit in a round hole when you use a solution that does not fit the platform you are working on. Whether it is C# and Linux or Static Typing and large long-term projects, it will eventually drive you insane. It is better to not write code you hate for your own sanity.

    • @smrtfasizmu6161
      @smrtfasizmu6161 2 ปีที่แล้ว

      @@AdroSlice Majority of professional programmers use OOP. Why wouldn't you use the OOP way of passing the instance unless you have very small amount of RAM. Static and global variables place limitations on what your program can do, while OOP way of passing the instance around makes the program way more powerful in a sense that program can have several chess matches at once, several servers at once etc, also professional programmers say that if you do it the OOP way, there is less problems with race conditions, several functions modifying the same variables etc. compared to using global variables.
      This is what experienced programmers said. They all wanted me to write OOP over global variables.

    • @AdroSlice
      @AdroSlice 2 ปีที่แล้ว +4

      @Smrt fašizmu I'm not arguing against objects in programming at all, but the dogmatic OOP way of trying to mangle everything into a class or method is actively detrimental to your code quality, forcing more boilerplate and requiring more resources for no practical reason.
      Sticking blindly to a paradigm because of an echo chamber extolling its virtues is not something anyone should strive for. In fact, I believe the very notion of separate paradigms is somewhat misguided. Functional, procedural and object-related programming features all have their strengths/weaknesses, and can, and really, *should* be mixed.
      Btw, I'm a professional software engineer, and I use a lot of Go and JavaScript at work, but I don't think I'm biased here as C# remains my most comfortable programming language (which until recently very much tried to enforce OOP).

  • @jjxtra
    @jjxtra 2 ปีที่แล้ว +3

    Biggest advice: Separate your scene hierarchy, scene objects and rendering into completely separate classes and interfaces. Renderer knows how to render scene objects, Scene knows how to store and manage scene objects. Scene and scene objects don't need to know a renderer even exists.

  • @Meoiswa
    @Meoiswa 2 ปีที่แล้ว

    The idea behind non-nullable reference types is that, when you're working with an "object driven design", the variables for said objects are no longer "pointers to regions in memory" (that could be empty), but rather "things". Having "things" not allowed (by default) to be null allows writing less strict code (no need to null-check for safety everywhere) without creating less safe code (by guaranteeing by contract that things will have value).
    From a very top-level point of view, its basically syntax sugar for adding null checks on setters/getters/parameters/etc.

  • @buggyspeaks7349
    @buggyspeaks7349 2 ปีที่แล้ว

    Yes! Please do make a video that shows how rendering should interact with oop. The biggest obstacle in deciding how to set up my own engine has been deciding where to put the actual opengl functions without making the Renderer class insanely complicated.

  • @selore4865
    @selore4865 2 ปีที่แล้ว +12

    I'm over here, 26 years old, and I'm still struggling to figure out DirectX...

    • @AcridDragoon
      @AcridDragoon 2 ปีที่แล้ว +4

      Fr tho smart kid

    • @Thomahawk1234
      @Thomahawk1234 2 ปีที่แล้ว +10

      Don't worry. If you keep at it you'll eventually be a 27 year old who figured out DirectX

    • @hawks3109
      @hawks3109 2 ปีที่แล้ว +2

      all good, everyone has their own pace. The kid in this review has a lot of knowledge for his age, but I imagine it's pretty scattered (as evidenced by this code). He probably doesn't have the strong math background to really understand what's going on, just how to get the code and modify it a little bit in some places. Of course for his age this is incredibly good, but there is still a whole ocean of knowledge out there he hasn't touched.

    • @XxxGuitarMadnessxxX
      @XxxGuitarMadnessxxX 2 ปีที่แล้ว +3

      Don't know if it'll make you feel better, but I only started dabbling with programming when I was 25 (28 now) and things like DirectX and Vulkan will definitely come easily to you once you do other types of projects and get a feel for how data should really be processed and transferred - this is just presuming you're like me who started with no knowledge of programming lol But in all seriousness, just do what you've been doing and chunk things out into smaller bits - you got this =D
      I'm going to pull from a personal project of mine as an example - I saw Cherno use spdlog a while back and decided to try spdlog out (it's absolutely fantastic and reeeaalllyy fast). I then started out writing a wrapper but was having a lot of fun with extending it, that I ended up writing my own logging framework inspired from spdlog (it's nowhere near as robust and only handles the terminal, single files, and rotating logs at the moment) but a lot of the fun came from trying to make it faster than spdlog - which I've done just that (40% faster terminal: with color and stdout, 90% with stderr/color, 105% with stderr/nocolor, 45% file, and ~50% for rotating logs).
      So the point of my rambling on and on there - if you just find someway to make DirectX or any graphics API programming fun - you'll just lose yourself in the learning process and come out as a strong and knowledgeable person in that context. Rambling finished lmao Best of luck!!

    • @bluesillybeard
      @bluesillybeard 2 ปีที่แล้ว

      @@hawks3109 I'm a 16 year-old, and actually can relate to that. I myself have a lot of holes in my knowledge, and it's probably going to be a loooong time before I actually figure out what a transformation really is doing, or how to properly organize my code, or even just how to import a library in C or C++.

  • @siddharthasarmah9266
    @siddharthasarmah9266 2 ปีที่แล้ว +6

    The flawless switch to the sponsor moment was brilliant ;)

  • @DoorThief
    @DoorThief 2 ปีที่แล้ว +1

    It'll show the nullable warning for a field If it isn't initialized in the constructor. You're running from the main method where it's assigned to then, but since it's not the constructor you're going to see the warning.
    Recently added nullable has a setting in the csproj. The idea is you have to be more explicit and careful with things that may not have a value when you try to use them, so that you'll hit less Object Reference Exceptions If you were to access a property or something for a null value

  • @Chiramisudo
    @Chiramisudo 2 ปีที่แล้ว

    Haha, well done on the sponsor ad. You just snuck that right in there during the clone. Smooth! 😋

  • @NR1612
    @NR1612 2 ปีที่แล้ว +5

    That Brilliant segue got me 🤣I love that

  • @VerdASMR
    @VerdASMR 2 ปีที่แล้ว +2

    That sam guy really knows his bloom ;)

  • @rp8133
    @rp8133 2 ปีที่แล้ว

    Hi Cerno, first thanx for your videos over the last years. I love to consume them and to learn from. Concerning the way programmers write their code one could easily start a holy war :-) Within the team I've been working for more than 2 decades we stumbled over this problem too. We negotiated our 'do's and 'dont's and the work was fine. We constantly work with C++ and C# (and C++/CLI). So we decided to use the most common notation for each language. So, as an example for C# class fields we use the leading underscore camel case rule (e.g. 'double _axisVelocity'). For C++ in general we decided to hold it near to what the stl guys do: all lower case and syntactically delimited by underscore. For C++ class fields we are not really unanimous. Some prefer m_* or _*. I prefer *_ which should never collide with C++ internals (e.g. 'double axis_velocity_'). But, in the end the only thing that counts is: CAN I READ IT (3 months later) and CAN OTHERS READ IT.

  • @chalkchalkson5639
    @chalkchalkson5639 2 ปีที่แล้ว +3

    I think it's really cool to see some other languages highlighted! The most interesting parts are the general CS lessons anyway and not "hey this way of doing it in c++ is slightly nicer". Reading code in a language you're not 100% familiar with also also a really good skill to have and so might be neat to showcase that :)
    Maybe you could even branch out and review some code that's done in even more different languages like Java (channel history and all that), FORTRAN or Python.

  • @vast634
    @vast634 6 หลายเดือนก่อน

    If something just exists once in the runtime, or is just created once, it totally fine to just use static. Its shorter, easier to unterstand and avoids boilerplate code with passing references around. If you write an API or have potentially several instances running, dont use static.

  • @KamillaMirabelle
    @KamillaMirabelle 2 ปีที่แล้ว

    The nullable postfix, helps the compiler in the optimization process, it make runtime information, compile time information, which give the compiler clues about runtime behaviour. If you flag a variable (here a pointer) as not null able, the compiler can eliminate that check, this will lead to some performance gain

  • @dcmayo
    @dcmayo 2 ปีที่แล้ว +1

    I would LOVE to see more C# on the channel.

  • @unlxck3255
    @unlxck3255 2 ปีที่แล้ว +1

    I really hope that i will have the knowledge about these stuff like you have. Its so impressive

  • @TheXenoEnder
    @TheXenoEnder 2 ปีที่แล้ว +1

    Nullable reference types is for static validation (find errors at compile time instead of runtime)

  • @zhh174
    @zhh174 2 ปีที่แล้ว +1

    You can use sln with dotnet cli on any platform. c# is cross-platform and open-source now.

  • @jurikristjouw
    @jurikristjouw 2 ปีที่แล้ว

    Thank you! I am so glad you were flabbergasted with the nullable reference question mark.. It also does not make any sense to me as to how this improves quality... There are more of these gizmo's these days.

  • @taylorswe
    @taylorswe ปีที่แล้ว

    To be clear anything NOT marked as nullable can still be null in some edge cases...
    The question mark is more of a DX thing than a compilation concern (note that not including a question mark creates a warning, not an error).

  • @Beatsbasteln
    @Beatsbasteln 2 ปีที่แล้ว +1

    thanks sam for this code review

  • @tanmaypanadi1414
    @tanmaypanadi1414 2 ปีที่แล้ว +1

    38:45 I don't know anything about it . but I would love more videos about it.

  • @C3NTesports
    @C3NTesports 2 ปีที่แล้ว

    I barley know anything about programming but this was in my feed but my goodness this is fascinating 🤯

  • @peter1745dev
    @peter1745dev 2 ปีที่แล้ว +1

    Love the fact that you're doing C++ as well as C# reviews :) I really like C# as a language. Wasn't aware that reference types should use the nullable operator nowadays, doesn't actually make a lot of sense to me since reference types can be null by default, I get it for value types since they're not nullable by default

  • @jiiiiim_xyz
    @jiiiiim_xyz 5 หลายเดือนก่อน

    I've watched half your youtube channel and have been attempting to learn for 2 years or so.. and have just about learnt how to make Pong. This 16 year old kid need not worry about finding a job.. wow I feel stupid.. Thanks for all the videos! I should probably sign up to Brilliant so it sinks in..

  • @coolguyflex
    @coolguyflex 2 ปีที่แล้ว

    Nullable reference types are great. If you had waited another second, the static analysis would have warned you that you were about to call a method on an object that could be null.

  • @shottyman6013
    @shottyman6013 2 ปีที่แล้ว

    That segway to brilliant was genuinely smooth

  • @zimcoder
    @zimcoder ปีที่แล้ว +2

    He encounters a new concept and because fear of change "this is why I stick to C++"

  • @bluesillybeard
    @bluesillybeard 2 ปีที่แล้ว +1

    I would absolutely love to see a video about creating a good simple OpenGL rendering architecture.

  • @ultimatesoup
    @ultimatesoup ปีที่แล้ว

    I write game engines and tooling as well (27 years experience). The main thing to get correctly is that all the high level "manager" components (e.x PhysicsManager , EventManager, ResourceManager, Memorypool, RenderManager, AIManager, etc) typically need to be accessible from anywhere. The two primary architectures I see for this is to make them Singletons or to contain them all within a top level class as statics. I prefer the statics approach. Typically its a resource drain to have every renderable or AI capable component have a copy of the corresponding manager. I really dislike making them singletons because it has more overhead for the dereference and call to getinstance().

  • @KieranDevvs
    @KieranDevvs 2 ปีที่แล้ว +18

    You missed the compiler warning because you were going too fast. If you declare the type as non null and then invoke a member without instantiating, the compiler analysis will warn you that this code is unlikely to succeed.

  • @localatticus4748
    @localatticus4748 2 ปีที่แล้ว

    The new nullability annotations are fantastic, honestly, especially since the compiler can check branches to see if things have been initialized or null-checked previously as well so you don't have to explicitly handle null from then on either even if the type is marked nullable. I use it everywhere I can for documentation of my intent and to get those flow control warnings from the compiler. I only get null reference exceptions now when I'm trying to be cheeky and throw something together without thinking about it first.

  • @marytooker956
    @marytooker956 2 ปีที่แล้ว

    I believe moving the initialization of _window to a static constructor would have removed the intellisense warning.
    It was just pointing out that _window was a non-nullable ref type, but during the lifetime of the Program class (i.e. right after construction prior to invoking Main) the _window reference IS null.

  • @Hr1s7i
    @Hr1s7i 2 ปีที่แล้ว

    That... was a good sponsor transition. Me gusta.

  • @glewfw7989
    @glewfw7989 2 ปีที่แล้ว

    more about cherno learning c# PLZ
    a heck lot of ppl will relate with you

  • @idc20627
    @idc20627 2 ปีที่แล้ว

    we just witnessed how cherno learns and explains a specific feature of c# he never encountered both at the same time

  • @criptych
    @criptych 5 หลายเดือนก่อน

    Technically you can start a variable name with an underscore in C++; it's still a legal name, but the pattern is "reserved" for system names. And I'm pretty sure (though could be remembering it wrong) that only applies to global names, not class members.

  • @vincentvoillot6365
    @vincentvoillot6365 2 ปีที่แล้ว +3

    I write my first line of basic in the early 90', and let me tell you for a 10 years french old boy, finding resources or softwares was the hardest. Maybe 2-3 books in the local library and compilers weren't free (mostly C), every thing on floppy. Bigest issues IMO, it's to begin with an strict OO langage (specialy java, find it aweful ).
    Better begin with C or Javascript even, start with basic control flow and structure, then with that knowledge discover class with c++ or prototype and class with JS, then design pattern . Once you understand those, you can read and write almost any langage with google helping for the syntax.
    A serie on design pattern would interest me greatly.

    • @MichaelMorguarge
      @MichaelMorguarge 2 ปีที่แล้ว

      You need both sides of the coin or else you will force scripting-language principles into object-oriented languages and vice versa. If you get an understanding of both programming ideologies (no matter which side you start with), you will know when you can and cannot apply certain language principles to certain things.
      Consider the coding standards per language. An example that extends past the scripting vs. OO barrier is where Java wants people to use formats for method names like "getName" but in C# they want people to use "GetName". In other languages, you may have to use "_getName" or "#getName". The amount of semantics and unique design decisions per language is incredible considering how one small detail can change how language operates (pass by value vs. pass by reference).
      Overall, there is still much to learn for every programming language that seems similar but handles things in a very different way under the hood that makes one It's what makes programming fun.

    • @vincentvoillot6365
      @vincentvoillot6365 2 ปีที่แล้ว +2

      ​@@MichaelMorguarge Java enforce oriented object and is very strict about his syntax.
      And you can implement class in C (in JS class are simulate by prototype) or any paradigme, High level programming is mostly syntaxic sugar.
      About coding convention, you can name your getter setter as you like. But following a common naming convention help to make it readable. (Eclipse do the heavy lifting for java)
      There is no "scripting-language principles", Java was and still is "interpreted", C++ is a compiled langage but can be interpreted (cf Cling ).
      Beyond that aspect of executing your code, programing paradigme are the same across all langages.
      All Class oriented langages have constructor, destructor, super public and private method. (Java, C++, Rust, Python, Scala, Typescript, PHP)
      You write a while in JS or PHP, it's mostly the same syntax.
      It take me one week to be efficient (not googling the syntax every two line) in PL/SQL and it is ugly to read and write as f*ck.

    • @MichaelMorguarge
      @MichaelMorguarge 2 ปีที่แล้ว +1

      one of the things I try to follow is to leave the code cleaner than when you started working with it. So whatever coding convention that is defined for the language or IDE you are using will technically be the cleanest standard to use unless the setup of the language is a mess. Doing the best you can to put produce clean and efficient code that follows the expectations of the original intent of the current iteration of the language/ide goes a long way for immediate and long-term maintenance.
      I agree that some languages will leave you writing code that you never wanted to write to work around shortfalls or bad architecture. Pl/SQL is a nice addition to SQL but it has a ways to go before in terms of cleaner implementation.

    • @vincentvoillot6365
      @vincentvoillot6365 2 ปีที่แล้ว

      @@MichaelMorguarge I don't disagree with you on the importance of following convention, official or proposed by the IDE.
      But the most important is not the convention in itself, it's the logic behind it and how to think your code.
      The case made by Chernot, for example, separate the renderer code.
      If you cannot write good code on notepad, no IDE or convention will help you to structure your code, mainly make it readable, not the same thing.
      In school i learned about UML, it's laborious but sanity-saving on a big project, from that you can even generate a skeleton code ready to be complete.
      I liked to have a bird-eye-view of what i'm doing and how i'm doing it.
      My grief with Java is the stupidity to force everything into the Class for the sake of it.
      I still have nightmare about the three flavors of String.
      As for PL/SQL, it's mostly influence by pre-C++ langage like ADA or PASCAL.
      It's kind of oriented object with the package. Writing a simple loop bring me back to the late 80's.

    • @vincentvoillot6365
      @vincentvoillot6365 2 ปีที่แล้ว

      @@MichaelMorguarge FYI, you can't implement a cleaner pl/sql. It wouldn't be PL/SQL.
      Don't mistake implementation and specification.
      The spec contain the grammar (keyword like While, super, loop ..) and syntax ( simply put where, how and when you use { } ( ) , ; , etc )
      Ex : Ecmascript is the definition of the javascript langage, typescript is an other langage based on ecmascript with his own specification.
      Scriptmonkey, V8, Chakra, ducktape, quick.js are all Javascript interpreter, they are implementation of the Ecmascript specification.
      Or C99 is a specification as C11 ( an extention count as a different spec, C99 compiler can't compile C11 news features, but you can implement them in C99) and VS, clang and gcc "'implement" the specification into the compiler.

  • @Christobanistan
    @Christobanistan 2 ปีที่แล้ว

    I really like C#'s new(ish) non-nullable reference types. I turn it on in all my projects now.

  • @axellara3353
    @axellara3353 2 ปีที่แล้ว

    Congratulations! That engine looks really nice!!

  • @SkrekkLich
    @SkrekkLich 2 ปีที่แล้ว

    Non-Nullable helps the dev to see potential NullReferenceExceptions ... if you put your _window in an if-block and check if _window is null, the warning will go away.

  • @CodeNova
    @CodeNova 2 ปีที่แล้ว +4

    Kudos for figuring up so quickly what is not-null-able in C#10. And you explained pitfalls on window.Run() if window is null. Thumb up !

  • @GamesWithGabe
    @GamesWithGabe 2 ปีที่แล้ว +10

    I don't get why people prefer singletons to just declaring a global variable. The only reason I can think of is you can control the lifetime of the object, but if you're in C/C++ you can just make the global variable a pointer.
    I feel like singletons were created to address the common issues of having too much global state. But that state is still global.
    I also think that turning the variable into a private member and passing it in through a constructor is still pretty dumb, because you haven't changed anything, you just moved the dependency to a different place. In my experience, if you're exposing a lot of state globally it's just because you have a fundamental issue with the way your code is architected. Instead of automatically jumping to a design pattern like a singleton or using an IOC or something, I think more programmers should ask themselves why the dependency is there in the first place, if it's necessary, and if it is necessary is your code fragmented across different units for some reason?

    • @user-dh8oi2mk4f
      @user-dh8oi2mk4f 2 ปีที่แล้ว

      Definitely agree with this

    • @DFPercush
      @DFPercush 2 ปีที่แล้ว +1

      A singleton is basically a namespace. Never understood why people liked them. I personally am not too religious about globals, at least for quick hack projects, but you make a good point about identifying dependencies in your architecture.

    • @CyrilCommando
      @CyrilCommando 2 ปีที่แล้ว

      It's just another C# case of designing a pointless alternative to something incredibly simple. See: Properties. Interfaces.

    • @solaris5303
      @solaris5303 2 ปีที่แล้ว

      ​@@CyrilCommando
      Properties are just syntactic sugar that save space and time compared to Get/Set methods. Interfaces are incredibly useful across countless scenarios - like pure abstract classes, except for a system that (by design) doesn't have multiple inheritance.

  • @quintencabo
    @quintencabo 2 ปีที่แล้ว

    That was one of the most smooth sponsorship inclusions I have ever seen

  • @SkyFallsLegion
    @SkyFallsLegion 2 ปีที่แล้ว

    Brilliant (pun intended) sponser transition!

  • @DioArsya
    @DioArsya 2 ปีที่แล้ว +1

    Nullable features like that really helpful, especially on js & ts, can't wait to uss that on cs

    • @HermanWillems
      @HermanWillems 2 ปีที่แล้ว

      NULL should be forbidden. It's the biggest mistake made in programming history. It should be deleted from every programming language ASAP. Rust programming language has no NULL, and it works perfectly and without all those shit null mistakes. Null is just total bullshit. The inventor and creator of NULL regret what he has made. (He said this himself)

    • @undefinedchannel9916
      @undefinedchannel9916 ปีที่แล้ว

      @@HermanWillemsThe nullable features basically do what Rust does. You get warnings (or errors, if you configure it) if you don’t mark something as nullable or you try to use something nullable in a way that would cause a runtime error.

    • @HermanWillems
      @HermanWillems ปีที่แล้ว

      @undefinedchannel9916 not a compile error? Why not a compile error. While runtime. U want to catch things in compile time. Runtime is not good.

  • @speedracerx713x
    @speedracerx713x 2 ปีที่แล้ว

    The nullable reference indicator forces developers to acknowledge possible runtime null references they may have otherwise missed. IT's basically shorthand for saying "Yeah, I'm aware of this situation".

  • @zhh174
    @zhh174 2 ปีที่แล้ว

    Nullable reference types are just compiler annotations. So compiler would give a warning if you try to use that nullable type without checking for null.
    Window? window;
    window.Run(); // this will generate a warning
    if (window != null)
    {
    window.Run(); // this will not generate a warning
    }
    A shorthand for the above code is
    window?.Run()
    This is very helpful because you won't ever get a runtime exception for null values. Most modern languages have this feature from the beginning.

  • @GonziHere
    @GonziHere 2 ปีที่แล้ว +1

    the point of explicit nullable is that now, you can actually have a method that takes a class as a parameter but since is not nullable, you don't have to do the nullcheck everywhere... you can easily differentiate between the "initialization code" that needs to check these things and the "actually running code" that doesn't anymore. You can, in most of your codebase, act with objects the same way as you would with integers, or, well, references with cpp.
    edit: "this is why I stick to cpp" - funny, since it's something I would love dearly in cpp. What would be your equivalent of doing it? using pointers for "nullable objects" and references when in "nullable not allowed anymore" parts? thanks.

    • @HermanWillems
      @HermanWillems 2 ปีที่แล้ว +1

      NULL is the biggest mistake in programming history. It should be deleted from Java and C#. ASAP.

  • @zmania101
    @zmania101 2 ปีที่แล้ว

    That sponsored segment transition was so cheesy, I love it.

  • @sps014
    @sps014 2 ปีที่แล้ว +1

    Great video looking for more C# & C++ content

  • @minastaros
    @minastaros 2 ปีที่แล้ว

    In C++ thinking: the _window is not like a class instance, but similar to a (C++) pointer or a reference. A pointer would be the nullable variant with "?", whereas the reference is guaranteed to "point" to a real instance.

  • @jansvoboda8940
    @jansvoboda8940 ปีที่แล้ว

    This nullability can be turned off in project configuration file. It can be also turned to mode, when everything what is by default green underlined is an error.

  • @andersmalmgren6528
    @andersmalmgren6528 2 ปีที่แล้ว

    If you move the declaration to the constructor you can ommit the forgiving operator.. if you write solid code you should try to have as few nullable members as possible

  • @simonbaxter8001
    @simonbaxter8001 2 ปีที่แล้ว

    Thanks Sam! ... oh, and great review Cherno 🤣🤣😎

  •  2 ปีที่แล้ว

    you can enable the nullable reference types feature on a file or project or solution level, so it can be a granular change file by file if you want to add this to your existing codebase. also it is only for the compiler, it does not change the runtime at all

  • @blackcoder41
    @blackcoder41 2 ปีที่แล้ว

    I can relate to that reaction on null references coming from Java/JavaScript to Kotlin/TypeScript hahaha 😅
    This is how Kotlin sells this feature:
    Kotlin's type system is aimed at eliminating the danger of null references, also known as The Billion Dollar Mistake.
    One of the most common pitfalls in many programming languages, including Java, is that accessing a member of a null reference will result in a null reference exception. In Java this would be the equivalent of a NullPointerException, or an NPE for short.

  • @Kitulous
    @Kitulous 2 ปีที่แล้ว +4

    c#8's nullable reference types are a blessing to null safety
    it's the same in Kotlin, you cannot have a null value in T, no matter what T is (class, struct, enum, whatever)
    that way you will only get null reference exceptions if you opt-in (by using the postfix !)
    for example:
    MyClass x = null; // compile error
    MyClass? x = null; // fine, you need to check for null now
    MyClass x = null!; // you shut the compiler up and if something tries to reference x you will get a NRE

  • @christopher8641
    @christopher8641 ปีที่แล้ว

    good ad, I know a lot of people don't say that genuinely. But, I thought it was funny and well incorporated

  • @user-sl6gn1ss8p
    @user-sl6gn1ss8p 2 ปีที่แล้ว

    just to chime in, the video idea at 38:30 sounds great

  • @sigmareaver680
    @sigmareaver680 2 ปีที่แล้ว +1

    My only experience with C# really is with Unity. On the game I'm currently writing (in C++), I plan on using Lua as a scripting language.

  • @bmryner90
    @bmryner90 2 ปีที่แล้ว +1

    Title should've been
    "16 year old breaks The Cherno"

  • @roxxel8167
    @roxxel8167 2 ปีที่แล้ว

    22:35 I'd rather call it Current (Application.Current) since we shouldn't have more than 1 instance of application it's totally fine imo

  • @infinityminuszero
    @infinityminuszero 2 ปีที่แล้ว

    You could've been more encouraging to the 16 year old that sent you the code. Few 16 year olds can do this kind of programming, let alone a "hello world".

  • @philipmrch8326
    @philipmrch8326 2 ปีที่แล้ว +1

    Use dependency injection alongside singletons. This kind of hides the fact that it's a singleton

  • @Kenbomp
    @Kenbomp 2 ปีที่แล้ว

    Great vid very well organized and practical.

  • @narumikazuchi
    @narumikazuchi 2 ปีที่แล้ว +2

    Let me explain the reasoning behind the nullable reference types. If viewed from a compilers perspective it does effectively nothing. However that isn't it's purpose in the first place. It's intended to visualize the intend of the code to the programmer. Example, you want to utilize a method of a class from a third-party library and you see a nullable reference type as a parameter of that method (like SomeMethod(Someclass? parameter)). This method signature implies to the programmer, that you CAN pass it a null value without the compiler throwing an exception. Meaning that as the creator of the third party library you are communicating, that this reference type parameter is allowed to be null, whereas if the signature would be like SomeMethod(SomeClass parameter) the programmer who consumes the third party library knows at a glance that passing null would likely yield an exception.
    I hope I could shed some light on the topic of nullables for you or anyone else, who doesn't know why they exist.

  • @Aragubas
    @Aragubas 2 ปีที่แล้ว

    I'm currently 16, I used to write game engines with custom programming language I made in c# back then as well 👀

  • @chriswilloughby48
    @chriswilloughby48 2 ปีที่แล้ว

    So cool, you will have inspired this talented young person on to great developments, as long as they don't become arrogant and believe greatness is owed to them.

  • @DylanMatthewTurner
    @DylanMatthewTurner 2 ปีที่แล้ว +2

    Despite being quite different than C#, another language that has a really great IDE integration tool is Rust imo.

  • @vuufke4327
    @vuufke4327 2 ปีที่แล้ว +1

    I would highly suggest you start using a VM with these